java | c结构体转java实体类 字节 --- 绝版彩蛋

一、前言

在与c通讯时,因语言只有结构体的概念,在与java进行socket通信时,java往往需要把接收到的字节流在处理成需要的数据,这一步我看网络上大部分都没有一个好用东东,因此分享出来。

注意:c的结构体自己了解去,这里不再叙述,同时c常用类型占几字节也务必清楚,这里不再叙述。

主要思想:根据c结构体的类型占位,自定义注解标注占位数,之后通过java反射直接生成对象。这里面主要基础基础就是反射、注解、字节进制这些东东,这些理解了可以根据自己业务去封装自己需要的。

还有 博主比较佛系,有些问题可能不会回复

二、自定义注解

里面有一些根据业务自定义一些业务逻辑,根据自己需要去修改。

StructRecv 结构报文 --- 与 c结构体对应。

import java.lang.annotation.*;

/**
 * 结构报文 --- 与 c结构体对应
 * 标记在类上
 * <p>
 * 默认支持
 * 请求 类型映射 byte[] -> int、long、String、LocalDateTime、class、boolean(0.false 1.true)
 * 响应 类型映射 int、long、LocalDateTime、String、class、boolean -> byte[]
 * </p>
 * <p>
 * 支持继承(有父类情况下,优先解析父类的)
 * 实体类对象必须要有无参构造
 * 如果 字段为 class 不可 继承
 * 如果有子类,那么对应 value 用 0 表示 ,会 动态计算出子类的大小 class、list
 * </p>
 *
 * @author wbw
 * @date 2020年8月1日09:16:53
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface StructRecv {

	/**
	 * 结构体 每个字段所占 字节数
	 * <p>
	 * 如果字段为 class 那么必须包含
	 * 如果带有 0 则视为list
	 * </p>
	 *
	 * @return int[]
	 */
	int[] value();

	/**
	 * 描述
	 *
	 * @return String
	 */
	String desc() default "";

	/**
	 * 模块
	 *
	 * @return 模块类型
	 */
	ModuleTypeEnum module() default ModuleTypeEnum.IGNORE;

	/**
	 * 策略标识
	 *
	 * @return 策略
	 */
	int policy() default 0;

	/**
	 * 策略子类型
	 *
	 * @return PolicySubtypeEnum
	 */
	PolicySubtypeEnum subtypeEnum() default PolicySubtypeEnum.NONE;

	/**
	 * 含有内部类 - 成员变量类
	 *
	 * @return false
	 */
	boolean hasInnerClass() default false;

	/**
	 * 日志类型
	 *
	 * @return String
	 */
	String logType() default "";
}

StructRecvField 自定义 结构体 字段 类型


import com.hbcloud.hbx.audit.centre.common.constant.RecvFieldTypeEnum;

import java.lang.annotation.*;

/**
 * 自定义 结构体 字段 类型
 * 标记在字段上
 *
 * @author wbw
 * @date 2020年8月1日09:17:01
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface StructRecvField {
	/**
	 * 字段类型
	 * 默认为忽略字段
	 *
	 * @return 报文字段类型
	 */
	RecvFieldTypeEnum value() default RecvFieldTypeEnum.IGNORE;

	/**
	 * list bean 对象
	 * <p>只有发时存在</p>
	 *
	 * @return class
	 */
	Class<?> bean() default Object.class;

	/**
	 * list 字段
	 * <p>只有发时存在</p>
	 *
	 * @return class
	 */
	String field() default "";

	/**
	 * list 数量
	 * <p>只有收时存在</p>
	 * <p>支持直接数字定义、支持自定义当前类字段</p>
	 *
	 * @return 数量
	 */
	String count() default "";

	/**
	 * 需要计算进去的 frameLength
	 *
	 * @return 0
	 */
	int frameLength() default 0;
}

三、解析生成实体与解析成字节数组工具类

ByteUtil 字节工具类,这里如果对二进制转换不理解请自行百度


import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import lombok.experimental.UtilityClass;

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 字节工具类,提供字节转换等方法
 * <p>
 * 二进制位逢二进一
 * 1 1 1 1  1 1 0 1 = 256 + 128 + 64 + 32 + 16 + 8 + 4 + 0 + 1 = 253
 * </p>
 * <p>
 * 这里 | 和 & 均是 二进制运算法
 * 把对应的数字 转换成二进制 在把运算符带入 即是结果
 * 如 8 | 9 | 12 = 0000 1000 | 0000 1001 | 0000 1100 = 0000 1101 = 8 + 4 + 1 = 13
 * 如 8 & 9 & 12 = 0000 1000 & 0000 1001 & 0000 1100 = 0000 1000 = 8
 * <p>
 * 同时 << 和 >> 均为移动符号 左边为乘 右边为除
 * 如: 1 << 3 = 1 * 2 * 2 * 2 = 0000 0001 << 0000 1000 = 8
 * </p>
 *
 * @author wbw
 * @date 2020年8月1日11:13:01
 */
@UtilityClass
public class ByteUtil {
	/**
	 * 补码使用
	 * 0xff = 255 = 1111 1111
	 */
	private final short FRAME = 0xFF;

	/**
	 * 数字 转 bytes
	 *
	 * @param value 值
	 * @param src   源
	 * @param start 开始
	 * @param len   长度
	 */
	public void toBytes(long value, byte[] src, int start, int len) {
		if (ArrayUtil.isEmpty(src) || src.length < len) {
			return;
		}
		for (int i = 0; i < len; i++) {
			src[start + i] = (byte) ((value >> (8 * i)) & FRAME);
		}
	}

	/**
	 * 字段串 转 bytes
	 * 默认 utf-8
	 *
	 * @param value 值
	 * @param src   源
	 * @param start 开始
	 */
	public void toBytes(String value, byte[] src, int start) {
		ByteUtil.toBytes(value, src, start, StandardCharsets.UTF_8);
	}

	/**
	 * 字段串 转 bytes 默认 gbk
	 *
	 * @param value 值
	 * @param src   源
	 * @param start 开始
	 */
	public void toBytesGbk(String value, byte[] src, int start) {
		ByteUtil.toBytes(value, src, start, Charset.forName(CharsetUtil.GBK));
	}

	public void toBytes(String value, byte[] src, int start, Charset charset) {
		if (StrUtil.isEmpty(value) || ArrayUtil.isEmpty(src)) {
			return;
		}
		byte[] valBytes = value.getBytes(charset);
		System.arraycopy(valBytes, 0, src, start, valBytes.length);
	}

	/**
	 * bytes  转 字符串
	 *
	 * @param bytes   源
	 * @param start   开始
	 * @param len     长度
	 * @param charset 编码
	 * @return String
	 */
	public String toString(byte[] bytes, int start, int len, String charset) {
		if (ArrayUtil.isEmpty(bytes) || start + len > bytes.length) {
			return "";
		}
		byte[] res = new byte[len];
		System.arraycopy(bytes, start, res, 0, len);
		return StrUtil.str(res, charset).trim();
	}

	/**
	 * bytes  转 字符串 默认 utf-8
	 *
	 * @param bytes 源
	 * @param start 开始
	 * @param len   长度
	 * @return String
	 */
	public String toString(byte[] bytes, int start, int len) {
		return ByteUtil.toString(bytes, start, len, CharsetUtil.UTF_8);
	}

	/**
	 * 时间特殊处理 yyyy-mm-dd hh:mm:ss
	 *
	 * @param bytes 字节
	 * @param start 起始位置
	 * @return 时间
	 */
	public String toTime(byte[] bytes, int start) {
		List<Integer> time = new LinkedList<>();
		time.add(ByteUtil.toInt(bytes, start, 2));
		time.add(ByteUtil.toInt(bytes, start + 4, 2));
		time.add(ByteUtil.toInt(bytes, start + 6, 2));
		time.add(ByteUtil.toInt(bytes, start + 8, 2));
		time.add(ByteUtil.toInt(bytes, start + 10, 2));
		time.add(ByteUtil.toInt(bytes, start + 12, 2));
		Object[] array = time.stream()
				.map(e -> e.toString().length() > 1 ? e.toString() : '0' + e.toString())
				.toArray();
		return String.format("%4s-%2s-%2s %2s:%2s:%2s", array);
	}

	/**
	 * bytes  转 字符串 默认 gbk
	 *
	 * @param bytes 源
	 * @param start 开始
	 * @param len   长度
	 * @return String
	 */
	public String toStringGbk(byte[] bytes, int start, int len) {
		return ByteUtil.toString(bytes, start, len, CharsetUtil.GBK);
	}

	public long toLong(byte[] bytes, int start, int len) {
		if (ArrayUtil.isEmpty(bytes) || start + len > bytes.length) {
			return 0L;
		}
		long result = 0;

		for (int i = 0; i < len; i++) {
			result |= (bytes[start + i] & FRAME) << (i * 8);
		}
		return result;
	}

	public int toInt(byte[] bytes, int start, int len) {
		return Math.toIntExact(ByteUtil.toLong(bytes, start, len));
	}

	/**
	 * 反的
	 *
	 * @param bytes 源
	 * @param start 开始
	 * @param len   长度
	 * @return int
	 */
	public int toIntReverse(byte[] bytes, int start, int len) {
		if (ArrayUtil.isEmpty(bytes) || start + len > bytes.length) {
			return 0;
		}
		int result = 0;
		for (int i = len - 1, location = 0; i >= 0; i--) {
			result |= (bytes[start + location++] & FRAME) << (i * 8);
		}
		return result;
	}
}

StructRecvUtil 反射解析生成实体类


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.net.NetUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import lombok.SneakyThrows;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * tio 工具类
 *
 * @author wbw
 * @date 2020/7/31 14:52
 */
@UtilityClass
@Slf4j
public class StructRecvUtil {
	/**
	 * 默认支持的类型
	 */
	private final String HBX_TYPE = "com.xxx.xxx";
	private final String STRING_TYPE = "string";
	private final String INT_TYPE = "int";
	private final String LONG_TYPE = "long";
	private final String DATE_TYPE = "date";

	/* ----------------------------------------接收------------------------------------------------------*/

	/**
	 * 转换成bean 含有 BaseFrame
	 *
	 * @param bytes 字节流
	 * @param t     类
	 * @param frame 需要拷贝该类
	 * @param <T>   类
	 * @return T
	 */
	public <T> T toBeanFrame(byte[] bytes, Class<T> t, BaseFrame frame) {
		return StructRecvUtil.toBean(bytes, t, frame, BaseFrame.FRAME_HEAD_LENGTH, true, false);
	}

	public <T> T toWinBeanFrame(byte[] bytes, Class<T> t, BaseFrame frame) {
		return StructRecvUtil.toBean(bytes, t, frame, BaseFrame.FRAME_HEAD_LENGTH, true, true);
	}


	/**
	 * 转换成bean 从 0 开始
	 *
	 * @param bytes 字节流
	 * @param t     类
	 * @param <T>   类
	 * @return T
	 */
	public <T> T toBean(byte[] bytes, Class<T> t) {
		return StructRecvUtil.toBean(bytes, t, null, 0, false, false);
	}

	public <T> T toBean(byte[] bytes, Class<T> t, int startLocation, boolean isWin) {
		return StructRecvUtil.toBean(bytes, t, null, startLocation, startLocation > 0, isWin);
	}

	public <T> T toWinBean(byte[] bytes, Class<T> t) {
		return StructRecvUtil.toBean(bytes, t, null, 0, false, true);
	}

	/**
	 * 反射 加载 被 @StructRecv 修饰的 T
	 * <p>赋值不包括 父类</p>
	 *
	 * @param bytes         字节流
	 * @param t             类
	 * @param frame         需要拷贝的 基础实体类
	 * @param startLocation 起始位置
	 * @param skipAnalysis  跳过baseFrame
	 * @param <T>           类
	 * @return T
	 */
	@SneakyThrows
	private <T> T toBean(byte[] bytes, Class<T> t, BaseFrame frame, int startLocation, boolean skipAnalysis, boolean isWin) {
		T entity = t.getDeclaredConstructor().newInstance();
		if (ArrayUtil.isEmpty(bytes) || bytes.length <= BaseFrame.FRAME_HEAD_LENGTH) {
			return entity;
		}
		Class<?> clazz = entity.getClass();
		// 类 顺序接收
		List<Class<?>> classList = new LinkedList<>();
		classList.add(t);
		// 得到 父类 所有需要解析的bean
		StructRecvUtil.analysisSuperClass(clazz, skipAnalysis, classList);

		// 当前类解析 包含子类 深层递归解析
		if (!clazz.isAnnotationPresent(StructRecv.class)) {
			return entity;
		}
		// 计算解析开始位置
		AtomicInteger atomicInteger = new AtomicInteger(startLocation);
		// 解析 并 递归子类
		for (int i = classList.size() - 1; i >= 0; i--) {
			StructRecvUtil.loadingReqField(bytes, classList.get(i), entity, atomicInteger, isWin);
		}
		// 是否拷贝 BaseFrame
		if (frame != null) {
			BeanUtil.copyProperties(frame, entity);
		}
		return entity;
	}

	/**
	 * 接收 深层 解析 父类
	 *
	 * @param clazz        class
	 * @param skipAnalysis s跳过BaseFrame
	 * @param classList    类接收
	 */
	private void analysisSuperClass(Class<?> clazz, boolean skipAnalysis, List<Class<?>> classList) {
		Class<?> superClass = clazz.getSuperclass();
		String simpleName = superClass.getSimpleName();

		// 如果 为  object 或者 Packet 最外层
		boolean marginFlag = simpleName.equals(Object.class.getSimpleName()) || simpleName.equals(Packet.class.getSimpleName());
		// 如果为 BaseFrame 并且 进行 BaseFrame 拷贝 则 不解析
		boolean frameFlag = simpleName.equals(BaseFrame.class.getSimpleName()) && skipAnalysis;
		if (marginFlag || frameFlag) {
			return;
		}
		// 需要解析
		classList.add(superClass);
		// 否则 是 需要的
		StructRecvUtil.analysisSuperClass(superClass, skipAnalysis, classList);
	}

	/**
	 * 赋值 请求 字段
	 *
	 * @param bytes         原数据
	 * @param entity        实体类
	 * @param startLocation 下一个位置(可能递归)
	 * @param <T>           T
	 */
	@SneakyThrows
	private <T> void loadingReqField(byte[] bytes, Class<?> clazz, T entity, AtomicInteger startLocation, boolean isWin) {
		// 当前 类 的报文数组
		int[] recvArray = clazz.getAnnotation(StructRecv.class).value();
		// 下一个位置
		AtomicInteger nextRecv = new AtomicInteger(0);
		for (Field field : clazz.getDeclaredFields()) {
			// 报文已经解析完毕
			if (nextRecv.get() > recvArray.length - 1) {
				return;
			}
			Object val;
			if (field.isAnnotationPresent(StructRecvField.class)) {
				// 特殊的 类型
				StructRecvField recvField = field.getAnnotation(StructRecvField.class);
				if (recvField.value() == RecvFieldTypeEnum.IGNORE) {
					continue;
				}
				if (recvField.value() == RecvFieldTypeEnum.RECV_REVERSAL) {
					// 反转
					val = ByteUtil.toIntReverse(bytes, startLocation.get(), recvArray[nextRecv.get()]);
				} else if (recvField.value() == RecvFieldTypeEnum.LIST_COUNT || recvField.value() == RecvFieldTypeEnum.LIST_LEN) {
					// list 发送的
					val = StructRecvUtil.getBasicTypeReq(bytes, startLocation, recvArray, nextRecv, isWin, field);
				} else if (recvField.value() == RecvFieldTypeEnum.LIST) {
					// list 接收的
					List<Object> list = new ArrayList<>();
					StructRecv recv = recvField.bean().getAnnotation(StructRecv.class);
					// 得到 结构体的大小
					int recvVal = Arrays.stream(recv.value()).sum();
					// 获取 已经 解析的 list count 数量
					int count;
					if (NumberUtil.isNumber(recvField.count())) {
						count = NumberUtil.parseInt(recvField.count());
					} else {
						Method method = entity.getClass().getMethod(StrUtil.upperFirstAndAddPre(recvField.count(), "get"));
						count = NumberUtil.parseInt(String.valueOf(method.invoke(entity)));
					}
					for (int i = 0; i < count; i++) {
						list.add(StructRecvUtil.toBean(bytes, recvField.bean()
								, startLocation.getAndAdd(recvVal), isWin));
					}
					val = list;
				} else if (recvField.value() == RecvFieldTypeEnum.IP) {
					// 特殊格式处理 ip、mac
					long ip = ByteUtil.toLong(bytes, startLocation.get(), recvArray[nextRecv.get()]);
					val = NetUtil.longToIpv4(ip);
				} else if (recvField.value() == RecvFieldTypeEnum.TIME_SYS) {
					// 特殊的时间处理
					val = ByteUtil.toTime(bytes, startLocation.get());
				} else {
					val = ByteUtil.toString(bytes, startLocation.get(), recvArray[nextRecv.get()]);
				}
			} else {
				val = StructRecvUtil.getBasicTypeReq(bytes, startLocation, recvArray, nextRecv, isWin, field);
			}
			if (val instanceof String && String.valueOf(val).endsWith("�")) {
				// 尾部 可能存在乱码情况
				val = StrUtil.subAfter(String.valueOf(val), "�", false);
			}
			startLocation.getAndAdd(recvArray[nextRecv.getAndIncrement()]);
			// 注入值
			field.setAccessible(true);
			field.set(entity, val);
		}
	}

	/**
	 * 获取基础类型请求
	 *
	 * @param bytes        数据
	 * @param location     位置
	 * @param recvArray    结构体对应数组
	 * @param nextLocation 下一个位置
	 * @param isWin        是否为 window
	 * @param field        字段
	 * @return 数据
	 */
	@SneakyThrows
	private Object getBasicTypeReq(byte[] bytes, AtomicInteger location, int[] recvArray
			, AtomicInteger nextLocation, boolean isWin, Field field) {
		Object val;
		String type = field.getGenericType().toString().toLowerCase();
		// 默认的属性支持
		if (type.contains(HBX_TYPE)) {
			Object obj = field.getType().getDeclaredConstructor().newInstance();
			StructRecvUtil.loadingReqField(bytes, obj.getClass(), obj, location, isWin);
			val = obj;
		} else if (type.contains(DATE_TYPE)) {
			long date = ByteUtil.toLong(bytes, location.get(), recvArray[nextLocation.get()]);
			val = DateUtil.toLocalDateTime(DateUtil.date(date));
		} else if (type.contains(INT_TYPE)) {
			val = ByteUtil.toInt(bytes, location.get(), recvArray[nextLocation.get()]);
		} else if (type.contains(LONG_TYPE)) {
			val = ByteUtil.toLong(bytes, location.get(), recvArray[nextLocation.get()]);
		} else {
			if (isWin) {
				val = ByteUtil.toStringGbk(bytes, location.get(), recvArray[nextLocation.get()]);
			} else {
				val = ByteUtil.toString(bytes, location.get(), recvArray[nextLocation.get()]);
			}
		}
		return val;
	}


	/* ----------------------------------------响应------------------------------------------------------*/

	public byte[] toBytes(Object data) {
		return StructRecvUtil.toBytes(data, 0);
	}

	/**
	 * 转换成 bean 带有 BaseFrame
	 *
	 * @param data 数据
	 * @return byte[]
	 */
	public byte[] toBytesFrame(Object data) {
		return StructRecvUtil.toBytes(data, BaseFrame.FRAME_HEAD_LENGTH);
	}


	/**
	 * 响应解析
	 *
	 * @param data      数据
	 * @param baseFrame BaseFrame 是否解析
	 * @return byte[]
	 */
	public byte[] toBytes(Object data, int baseFrame) {
		if (data == null) {
			return new byte[0];
		}
		Class<?> clazz = data.getClass();
		if (!clazz.isAnnotationPresent(StructRecv.class)) {
			log.error("错误的报文响应:\t{}", data.toString());
			return new byte[0];
		}
		boolean frameAnalysis = baseFrame == BaseFrame.FRAME_HEAD_LENGTH;
		List<Class<?>> classList = new LinkedList<>();
		classList.add(data.getClass());
		// 得到所有父类,并计算出 总长度
		StructRecvUtil.analysisSuperClass(clazz, frameAnalysis, classList);
		int len = baseFrame + classList.stream()
				.mapToInt(e -> Arrays.stream(e.getAnnotation(StructRecv.class).value()).sum())
				.sum();
		// 计算内部类 单个 或 集合
		len += StructRecvUtil.geInnerClassLength(clazz, data);
		// 剔除 BaseFrame
		classList.remove(BaseFrame.class);
		// 计算长度
		byte[] bytes = new byte[len];

		BaseFrame frame = new BaseFrame();
		BeanUtil.copyProperties(data, frame);
		boolean isWin = ModuleTypeUtil.isWin(frame.getFrameNo());
		// 是否需要 计算 BaseFrame
		if (baseFrame == BaseFrame.FRAME_HEAD_LENGTH) {
			// 设置中心响应
			frame.setFrameNo(Integer.parseInt(ModuleTypeEnum.CENTER.getValue()));
			frame.setFrameLength(len);
			StructRecvUtil.loadingResField(frame, BaseFrame.class, bytes, new AtomicInteger(0), isWin);
		}
		AtomicInteger atomicInteger = new AtomicInteger(12);
		// 因 需要 从 顶级 父类 往下面解析
		for (int i = classList.size() - 1; i >= 0; i--) {
			// 私有动态计算
			StructRecvUtil.loadingResField(data, classList.get(i), bytes, atomicInteger, isWin);
		}
		return bytes;
	}

	/**
	 * 自动装载 私有 字段
	 *
	 * @param clazz        类
	 * @param bytes        数组
	 * @param nextLocation 下一个 报文
	 * @param isWind       是否为windows
	 */
	@SneakyThrows
	private void loadingResField(Object data, Class<?> clazz, byte[] bytes, AtomicInteger nextLocation, boolean isWind) {
		AtomicInteger recvLocation = new AtomicInteger(0);
		int[] recvArray = clazz.getAnnotation(StructRecv.class).value();
		for (Field field : clazz.getDeclaredFields()) {
			// 报文已经解析完毕
			if (recvLocation.get() > recvArray.length - 1) {
				return;
			}
			// 忽略的
			StructRecvField recvField = field.getAnnotation(StructRecvField.class);
			if (recvField != null && recvField.value() == RecvFieldTypeEnum.IGNORE) {
				continue;
			}
			Method method = clazz.getMethod(StrUtil.upperFirstAndAddPre(field.getName(), "get"));
			Object result = method.invoke(data);
			if (field.isAnnotationPresent(StructRecvField.class) && recvField != null
					&& recvField.value() != RecvFieldTypeEnum.RECV_REVERSAL) {
				if (recvField.value() == RecvFieldTypeEnum.SIZE) {
					// 规则 发送 时 策略总大小,不包含 BaseFrame
					Class<?> aClass = data.getClass();
					int local = Arrays.stream(aClass.getAnnotation(StructRecv.class).value()).sum();
					int length = StructRecvUtil.geInnerClassLength(aClass, data);
					// 只继承一层父类
					int parent = Arrays.stream(aClass.getSuperclass().getAnnotation(StructRecv.class).value()).sum();
					ByteUtil.toBytes(local + length + parent, bytes, nextLocation.get(), recvArray[recvLocation.get()]);
				} else if (recvField.value() == RecvFieldTypeEnum.SIZE_POLICY) {
					// 规则 发送时 单纯 策略大小
					Class<?> aClass = data.getClass();
					// 本地的
					int local = Arrays.stream(aClass.getAnnotation(StructRecv.class).value()).sum();
					int length = StructRecvUtil.geInnerClassLength(aClass, data);
					ByteUtil.toBytes(local + length, bytes, nextLocation.get(), recvArray[recvLocation.get()]);
				} else if (recvField.value() == RecvFieldTypeEnum.IP) {
					ByteUtil.toBytes(NetUtil.ipv4ToLong(String.valueOf(result))
							, bytes, nextLocation.get(), recvArray[recvLocation.get()]);
				} else if (recvField.value() == RecvFieldTypeEnum.LIST_COUNT) {
					// 解析出 list 的 count
					method = clazz.getMethod(StrUtil.upperFirstAndAddPre(recvField.field(), "get"));
					int size = ((List) method.invoke(data)).size();
					//进程策略特殊处理
					if (clazz.getSimpleName().equals(PolicyConstant.PROCESS_POLICY) && size > 1) {
						size -= 1;
					}
					ByteUtil.toBytes(size, bytes, nextLocation.get(), recvArray[recvLocation.get()]);
				} else if (recvField.value() == RecvFieldTypeEnum.LIST_LEN) {
					method = clazz.getMethod(StrUtil.upperFirstAndAddPre(recvField.field(), "get"));
					int count = ((List) method.invoke(data)).size();
					StructRecv recv = recvField.bean().getAnnotation(StructRecv.class);
					// 得到 结构体的大小
					int recvVal = Arrays.stream(recv.value()).sum();
					ByteUtil.toBytes(count * recvVal, bytes, nextLocation.get(), recvArray[recvLocation.get()]);
				} else if (recvField.value() == RecvFieldTypeEnum.LIST) {
					// list 数据 动态计算出大小 --- 存放于最后一位
					for (Object obj : (List) result) {
						// 注意: 内部类不能继承父类
						StructRecvUtil.loadingResField(obj, obj.getClass(), bytes, nextLocation, isWind);
					}
				}
			} else if (result != null) {
				// 判断是否有子类
				String type = field.getType().toString().toLowerCase();
				if (type.contains(HBX_TYPE)) {
					StructRecvUtil.loadingResField(data, clazz, bytes, nextLocation, isWind);
					continue;
				}
				// 赋值 计算出 下一个位置
				if (type.contains(STRING_TYPE)) {
					if (isWind) {
						ByteUtil.toBytesGbk(String.valueOf(result), bytes, nextLocation.get());
					} else {
						ByteUtil.toBytes(String.valueOf(result), bytes, nextLocation.get());
					}
				} else if (type.contains(DATE_TYPE)) {
					LocalDateTime time = (LocalDateTime) result;
					ByteUtil.toBytes(Timestamp.valueOf(time).getTime()
							, bytes, nextLocation.get(), recvArray[recvLocation.get()]);
				} else {
					ByteUtil.toBytes(NumberUtil.parseLong(String.valueOf(result)), bytes
							, nextLocation.get(), recvArray[recvLocation.get()]);
				}
			}
			// 更新坐标
			nextLocation.getAndAdd(recvArray[recvLocation.getAndIncrement()]);
		}
	}

	/**
	 * 获取 子类
	 *
	 * @param clazz 类
	 * @param data  数据
	 * @return Integer
	 */
	private int geInnerClassLength(Class<?> clazz, Object data) {
		int len = 0;
		StructRecv annotation = clazz.getAnnotation(StructRecv.class);
		if (annotation.hasInnerClass()) {
			for (Field field : clazz.getDeclaredFields()) {
				// 单个类
				String packagePath = field.getType().toString().toLowerCase();
				if (packagePath.contains(HBX_TYPE)) {
					StructRecv structRecv = field.getClass().getAnnotation(StructRecv.class);
					len += Arrays.stream(structRecv.value()).sum();
					continue;
				}
				// 集合类
				if (field.isAnnotationPresent(StructRecvField.class)) {
					StructRecvField recvField = field.getAnnotation(StructRecvField.class);
					if (recvField.value() == RecvFieldTypeEnum.LIST) {
						try {
							Method method = clazz.getMethod(StrUtil.upperFirstAndAddPre(field.getName(), "get"));
							List list = (List) method.invoke(data);
							StructRecv recv = recvField.bean().getAnnotation(StructRecv.class);
							int count = Arrays.stream(recv.value()).sum();
							len += (list.size() * count);
						} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
							e.printStackTrace();
						}
					}
				}
			}
		}
		return len;
	}
}

四、使用

使用@StructRecv() 标注字节数,注意对应顺序

import com.hbcloud.hbx.audit.centre.common.annotaion.StructRecv;
import com.hbcloud.hbx.audit.centre.common.entity.BaseFrame;
import lombok.Data;
import lombok.EqualsAndHashCode;

/**
 * 中心确认信息
 *
 * @author wbw
 * @date 2020/8/2 10:13
 */
@EqualsAndHashCode(callSuper = true)
@Data
@StructRecv({4, 4, 64, 128, 128, 8, 8})
public class CenterConfirmInfo extends BaseFrame {
	/**
	 * 大小
	 */
	private Integer size;
	/**
	 * 客户端确认状态 0.未确认 1.确认
	 */
	private Integer sign;
	/**
	 * 主机名称
	 */
	private String hostName;
	/**
	 * 客户端的责任人名称
	 */
	private String onwerName;
	/**
	 * 客户端的部门名称
	 */
	private String deptName;
	/**
	 * 客户端的机构ID
	 */
	private Integer deptId;
	/**
	 * 客户端的用户ID
	 */
	private Long userId;
	/**
	 * 客户端的用户名称
	 */
	private String userName;
}

具体方法使用

// 解析成实体类
CenterConfirmInfo info = StructRecvUtil.toBean(new byte[数据懒得写,省略了;],CenterConfirmInfo.class);
// 解析成 字节 byte
byte[] bytes = StructRecvUtil.toBytes(info);

 五、结束

这里面并没有什么复杂的地方,本质上就是对解析的这一步骤进行数据抽象与封装。

彩蛋 ---- 手打狗头