快捷搜索:  汽车  科技

封装数据的javabean,手写一个Java的结构体实现Buffer和JavaBean的转换

封装数据的javabean,手写一个Java的结构体实现Buffer和JavaBean的转换100%排序获取对象在接受TCP或者UDP数据回传buffer时,可以直接解析成java对象进行处理,因为java中删除了结构体,所以只能自己实现,并且反射后没有顺序,所以必须有order字段功能点完成度

#头条创作挑战赛#

物联网产品越来越多,很多公司都需要和单片机等硬件对接,有些硬件设备使用的是16进制的传递和接收协议;

如果突然有一天,协议更新或改变了,java中就需要去调整每一个处理的地方,使用结构体可以有效避免出现这样的情况,协议改变了只需要修改结构体的顺序和数据类型;

不巧的是,java删除了结构体这个概念,使之只在C系列语言中得以广泛应用,这时候为了满足业务需求,就需要手写一个结构体出来。

在接受TCP或者UDP数据回传buffer时,可以直接解析成java对象进行处理,因为java中删除了结构体,所以只能自己实现,并且反射后没有顺序,所以必须有order字段

已完成功能:

功能点

完成度

排序获取对象

100%

String获取长度可以定义

100%

可以将某一个字段的值,作为下面某个字段的长度,用于动态获取

100%

将字符串转位2进制字符串输出

100%

传入class返回对象

100%

使用ByteBuffer解析,避免并发

100%

本教程第一部分为文件结构,第二部分为实操代码

文件结构(具体代码看下面实操部分,工具类代码在最后)

封装数据的javabean,手写一个Java的结构体实现Buffer和JavaBean的转换(1)

实际项目中结构截图

├──anno 【核心解析类文件夹】 │ │ └── SrStruct.java 【核心类(里面包含结构体解析方法,和调用方法)】 │ ├──cache 【缓存文件夹】 │ │ └── StructCache.java 【缓存类(包含反射缓存,降低调用时间,充当二级缓存)】 │ ├──emun 【枚举文件夹】 │ │ └── structEnum.java 【枚举类(包含在注解方法中需要使用的枚举)】 │ ├──vo 【测试实体类文件夹,不用关注】 │ └── Struct.java 【自定义注解】

实操

第一步:新建一个注解-- Struct.java

/** * @author N_Xiang * @describe 自定义结构体注解 * @time 2022/2/24 10:17 下午 */ @Target({ElementType.FIELD ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Struct { //排序,java反射后没有顺序,所以需要排序字段辅助 int order(); //枚举,如果要将byte数组取出为2进制字符串时,需要使用 StructEnum value() default StructEnum.Null; //长度,标识byte[]和String类型的变量需要取出多少个字节 Int length() default 0; //动态长度,如果当前字段获取需要获取的字节长度是依靠上面某个字段定义的,需要使用这个属性 String lengthMethod() default ""; }第二步:写缓存类 -- StructCache.java

/** * @author N_Xiang * @describe 全局缓存 * @time 2022/4/26 09:07 */ public class StructCache { /** * 二级缓存 */ public static final Map<Class<?> Map<String StructEnum>> ANN_FIELD_MAP = new ConcurrentHashMap<>(); public static final Map<Class<?> Map<String Field>> ANN_FIELD_TYPE_MAP = new ConcurrentHashMap<>(); public static final Map<Class<?> Map<Field Integer>> ANN_length_MAP = new ConcurrentHashMap<>(); public static final Map<Class<?> Map<String String>> ANN_LENGTH_METHOD_MAP = new ConcurrentHashMap<>(); public static final Map<Class<?> SortedMap<Integer Field>> ANN_LABEL_MAP = new ConcurrentHashMap<>(); public static final Map<String Method> ANN_METHOD_MAP = new ConcurrentHashMap<>(); }第三部:补充枚举类--StructEnum.java (建议拷贝后在编辑器中查看)

/** * @author N_Xiang * @describe 类型枚举 * @time 2022/2/24 10:39 下午 */ public enum StructEnum { String Int Byte Char Short Long Double Float Null ; }第四步:写解析类 -- SrStruct.java

(有些长,建议拷贝到编辑器中查看,主要功能就是将JavaBean中每个字段的注解值获取到,然后对应将Buffer中的值通过反射赋值)

/** * @author N_Xiang * @describe 注解解析器 * @time 2022/2/24 10:19 下午 */ public class SrStruct { /** * 字段注解中强转属性map * K:字段名 * V:类型枚举 */ private static Map<String StructEnum> fieldMap = new ConcurrentHashMap<>(); /** * 字段FieldMap * K:字段名 * V:Field */ private static Map<String Field> fieldTypeMap = new ConcurrentHashMap<>(); /** * 长度属性的值,k:filed v:长度 */ private static Map<Field Integer> lengthMap = new ConcurrentHashMap<>(); /** * 统计所有有长度属性的字段 k:filed名字 */ private static Map<String String> lengthMethodMap = new ConcurrentHashMap<>(); /** * 将实体类中的注解字段分解 * @param cls 实体类Object * @return */ private static SortedMap<Integer Field> parsStruct(Object cls) { return parsStruct(cls.getClass()); } /** * 将实体类中的注解字段分解 * @param cls 实体类Class * @return */ private static SortedMap<Integer Field> parsStruct(Class<?> cls) { if (!ObjectUtils.isEmpty(StructCache.ANN_FIELD_MAP.get(cls))) { fieldMap = StructCache.ANN_FIELD_MAP.get(cls); fieldTypeMap = StructCache.ANN_FIELD_TYPE_MAP.get(cls); lengthMap = StructCache.ANN_LENGTH_MAP.get(cls); lengthMethodMap = StructCache.ANN_LENGTH_METHOD_MAP.get(cls); return StructCache.ANN_LABEL_MAP.get(cls); } SortedMap<Integer Field> lableMap = new TreeMap<>(); Field[] fields = cls.getDeclaredFields(); Arrays.stream(fields).forEach(f -> { if (f.isAnnotationPresent(Struct.class)) { Struct annotation = f.getAnnotation(Struct.class); int value = annotation.order(); StructEnum value1 = annotation.value(); String s = annotation.lengthMethod(); int length = annotation.length(); if (value1 != StructEnum.Null) { fieldMap.put(f.getName() value1); } lengthMap.put(f length); lengthMethodMap.put(f.getName() s); if (!ObjectUtils.isEmpty(lableMap.get(value))) { throw new RuntimeException("在" cls.getName() " 中 " f.getName() " 字段@Struct的order属性出现重复!"); } lableMap.put(value f); fieldTypeMap.put(f.getName() f); } }); StructCache.ANN_FIELD_MAP.put(cls fieldMap); StructCache.ANN_FIELD_TYPE_MAP.put(cls fieldTypeMap); StructCache.ANN_LENGTH_MAP.put(cls lengthMap); StructCache.ANN_LENGTH_METHOD_MAP.put(cls lengthMethodMap); StructCache.ANN_LABEL_MAP.put(cls lableMap); return lableMap; } @Deprecated public static void translation(Object o byte[] bytes) { translation(o ByteBuffer.wrap(bytes)); } @Deprecated public static void translation(Class<?> o byte[] bytes) { try { translation(o.newInstance() ByteBuffer.wrap(bytes) 0); } catch (InstantiationException | IllegalAccessException e) { e.printStackTrace(); } } @Deprecated public static void translation(Object o byte[] bytes int offset) { translation(o ByteBuffer.wrap(bytes) offset); } @Deprecated public static void translation(Object o String bytes) { ByteBuffer buffer = ByteBuffer.wrap(StringToHex.hexStr2Byte(bytes)); translation(o buffer); } public static void translation(Object o ByteBuffer buffer) { translation(o buffer 0); } @Deprecated public static void translation(Object o String bytes int offset) { ByteBuffer byteBuffer = ByteBuffer.wrap(StringToHex.hexStr2Byte(bytes)); translation(o byteBuffer offset); } @Deprecated public static <T> T translation(Class<T> cls String bytes) { ByteBuffer byteBuffer = ByteBuffer.wrap(StringToHex.hexStr2Byte(bytes)); try { T tclass = cls.newInstance(); translation(tclass byteBuffer 0); return tclass; } catch (InstantiationException | IllegalAccessException e) { throw new RuntimeException(e); } } public static <T> T translation(Class<T> cls ByteBuffer buffer) { try { T tclass = cls.newInstance(); translation(tclass buffer 0); return tclass; } catch (InstantiationException | IllegalAccessException e) { throw new RuntimeException(e); } } public static <T> T translation(Class<T> cls ByteBuffer buffer int offset) { try { T tclass = cls.newInstance(); translation(tclass buffer offset); return tclass; } catch (InstantiationException | IllegalAccessException e) { throw new RuntimeException(e); } } /** * 解构为JavaBean * * @param source 目标对象 * @param buffer 字节流 * @param offset 偏移量 */ public static void translation(Object source ByteBuffer buffer int offset) { // 设置偏移量 buffer.position(offset); // 获取结构体注解 SortedMap<Integer Field> map = parsStruct(source); for (Map.Entry<Integer Field> entry : map.entrySet()) { // 获取字段 Field v = entry.getValue(); // 获取字段Type Class<?> type = getType(v); // 获取字段名 String name = getName(v); // 获取set方法名 String setMethodName = fieldSetMethodName(name); // 获取set方法 Method setMethod = MethodBySet(source setMethodName type); try { // 判断是否是子类,子类进入递归 if (!ObjectUtils.isEmpty(getType(v).getSuperclass()) && !getType(v).equals(byte[].class) && !getType(v).equals(String.class) && getType(v).getSuperclass().equals(Object.class)) { Object subclass = getType(v).newInstance(); translation(subclass buffer buffer.position()); setMethod.invoke(source subclass); continue; } } catch (Exception e) { e.printStackTrace(); } // 获取需要取字节的长度 int len = getLength(source name type v); // 获取byte流中对应的数据 Object deconstructedValue = typeToObject(type buffer len fieldMap.get(name)); // 将解构的数据存储到目标Bean try { setMethod.invoke(source deconstructedValue); } catch (IllegalAccessException | InvocationTargetException e) { e.printStackTrace(); } } } public static void unTranslation(Object o String bytes) { unTranslation(o StringToHex.hexStr2Byte(bytes)); } /** * 通过结构体构建成Byte数组 * @param source 源对象 * @param bytes byte数组 */ public static void unTranslation(Object source byte[] bytes) { } /** * 获取字段type * @param field 字段Field * @return */ private static Class<?> getType(Field field) { return field.getType(); } /** * 获取字段名 * @param field 字段Field * @return */ private static String getName(Field field) { return field.getName(); } /** * 将bytebuffer中的值取出 * @param type 源字段类型 * @param buffer 源buffer数据 * @param length 取得字段长度 * @param structEnum 字段目标类型 * @return */ private static Object typeToObject(Class<?> type ByteBuffer buffer int length StructEnum structEnum) { if (type.equals(String.class)) { if (length == 0) { length = 1; } byte[] bytes1 = new byte[length]; buffer.get(bytes1); if (ObjectUtils.isEmpty(structEnum)) { return StringToHex.bytesToHexString(bytes1); } else if (structEnum == StructEnum.Byte) { return StringToHex.byteArraytoByteString(bytes1); } else { return StringToHex.bytesToHexString(bytes1); } } else if (type.equals(int.class) || type.equals(Integer.class) || type.equals(U32.class)) { return buffer.getInt(); } else if (type.equals(short.class) || type.equals(Short.class) || type.equals(U16.class)) { return buffer.getShort(); } else if (type.equals(Byte.class)) { return buffer.get(); } else if (type.equals(byte.class) || type.equals(U8.class)) { return buffer.get(); } else if (type.equals(byte[].class)) { byte[] bytes = new byte[length]; buffer.get(bytes); return bytes; } else { throw new RuntimeException(type.getName() "是不支持的属性"); } } /** * 获取set方法 * @param source 源对象 * @param setMethodName set字段名 * @param type 源字段类型 * @return */ private static Method MethodBySet(Object source String setMethodName Class<?> type) { Method method = StructCache.ANN_METHOD_MAP.get(source setMethodName); if (ObjectUtils.isEmpty(method)) { try { method = source.getClass().getMethod(setMethodName type); } catch (NoSuchMethodException e) { throw new RuntimeException(e); } StructCache.ANN_METHOD_MAP.put(source setMethodName method); } return method; } /** * 获取get方法 * @param source 源对象 * @param getMethodName get字段名 * @param type 源字段类型 * @return */ private static Method MethodByGet(Object source String getMethodName Class<?> type) { Method method = StructCache.ANN_METHOD_MAP.get(source getMethodName); if (ObjectUtils.isEmpty(method)){ try { method = source.getClass().getMethod(getMethodName); } catch (NoSuchMethodException e) { throw new RuntimeException(e); } StructCache.ANN_METHOD_MAP.put(source getMethodName method); } return method; } /** * 获取get方法名 * @param name 源字段名 * @return */ private static String fieldGetMethodName(String name) { String lengthMethodMapLetter = name.substring(0 1).toUpperCase().concat(name.substring(1)); return "get" lengthMethodMapLetter; } /** * 获取set方法名 * @param name 源字段名 * @return */ private static String fieldSetMethodName(String name) { String lengthMethodMapLetter = name.substring(0 1).toUpperCase().concat(name.substring(1)); return "set" lengthMethodMapLetter; } /** * 获取字段值 * @param type1 字段类型 * @param getMethod get方法 * @param source 源对象 */ private static int getValue(Type type1 Method getMethod Object source){ try{ if (type1.equals(int.class) || type1.equals(Integer.class) || type1.equals(short.class) || type1.equals(Short.class) || type1.equals(double.class) || type1.equals(Double.class) || type1.equals(long.class) || type1.equals(Long.class)) { Number invoke = (Number) getMethod.invoke(source); return invoke.intValue(); } else if (type1.equals(byte.class) || type1.equals(Byte.class)) { return (byte) getMethod.invoke(source) & 0xFF; } }catch (Exception e){ e.printStackTrace(); } return 0; } /** * 获取长度 * @param source 源对象 * @param name 目标字段名 * @param type 目标字段类型 * @param v 字段Field */ private static int getLength(Object source String name Class<?> type Field v){ // 判断当前字段 @注解中是否有 lengthMethod 属性的字段名 String lengthMethodName = lengthMethodMap.get(name); if (!StringUtils.isEmpty(lengthMethodName)) { //获取目标字段的get方法名 String getLengthMethodMapName = fieldGetMethodName(lengthMethodName); //获取目标字段的get方法 Method getMethod = MethodByGet(source getLengthMethodMapName type); //从内存中获取到目标字段对应到Field属性 Field field = fieldTypeMap.get(lengthMethodName); if (ObjectUtils.isEmpty(field)) { throw new RuntimeException("没有在当前对象中找到属性" lengthMethodName); } //获取目标字段Type Class<?> targetType = field.getType(); //取出长度值,并赋值给当前字段 return getValue(targetType getMethod source); } else { // 没有 lengthMethod 属性就获取定义的length属性或者默认值 return lengthMap.get(v); } } } 第五步:调用

1:传入字符串 SrStruct.translation(需要接收数据的对象 传入的16进制字符串); 2:传入字符串加上偏移量 SrStruct.translation(需要接收数据的对象 传入的16进制字符串 偏移量); 3:传入byte数组 SrStruct.translation(需要接收数据的对象 传入的byte数据); 4:传入byte数组加上偏移量 SrStruct.translation(需要接收数据的对象 传入的byte数据 偏移量); 5:传入ByteBuffer对象 SrStruct.translation(需要接收数据的对象 传入的byteBuffer数据 偏移量);真实调用方法演示

1、带偏移量的(XXX为JAVABean) XXX xx = SrStruct.translation(XXX.class bytes 11); 2、不带偏移量(XXX为JAVABean) XXX xx = SrStruct.translation(XXX.class bytes); 3、(XXX为JAVABean)使用Netty的ByteBuf的(ByteBuffer.wrap()方法是将Bytebuf转为java中自带的ByteBuffer使用) XXX xx = SrStruct.translation(XXX.class ByteBuffer.wrap(byteBuf.array()));补充:注解使用方法

支持子类和父类一起解析

/** * @author N_Xiang * @describe * @time 2022/2/24 11:53 下午 */ @Data public class FooA extends Goo{ //order标注的顺序 @Struct(order = 0) int aa; @Struct(order = 1) short bb; @Struct(order = 2) short cc; //lengthMethod 获取cc的值作为当前需要获取的长度 @Struct(order = 3 lengthMethod = "cc") String dd; //获取长度为2的byte数组 @Struct(order = 4 length = 2) byte[] ee; //获取长度为2的byte数组转为字符串,字符串默认长度取1个byte @Struct(order = 5 length = 2) String dd; // @Struct(order = 3) // byte dd; /** *内部实体内 **/ @Data public static class Foo2{ @Struct(order = 0) short ff; @Struct(order = 1) String gg; } }用到的工具方法--类中的StringToHex工具

//16进制字符串转byte数组 public static byte[] hexStr2Byte(String hex) { ByteBuffer bf = ByteBuffer.allocate(hex.length() / 2); for (int i = 0; i < hex.length(); i ) { String hexStr = hex.charAt(i) ""; i ; hexStr = hex.charAt(i); byte b = (byte) Integer.parseInt(hexStr 16); bf.put(b); } return bf.array(); } //byte数组转16进制字符串 public static final String bytesToHexString(byte[] bArray) { StringBuffer sb = new StringBuffer(bArray.length); String sTemp; for (int i = 0; i < bArray.length; i ) { sTemp = Integer.toHexString(0xFF & bArray[i]); if (sTemp.length() < 2) { sb.append(0); } sb.append(sTemp.toUpperCase()); } return sb.toString(); } //byte数组转2进制字符串 public static final String byteArraytoByteString(byte[] bArray) { StringBuilder sb = new StringBuilder(bArray.length); for (int i = 0; i < bArray.length; i ) { byte t = bArray[i]; StringBuilder tsb = new StringBuilder(); for (int j=7;j>=0;j--){ int temp = 0x01 & t; tsb.append(temp); t >>=1; } sb.append(tsb.reverse()); } return sb.toString(); }


Lintge-持续分享开发新技术,代码新知识!

猜您喜欢: