java中自定义结构体,方便解析TCP或者UDP回传byte数组时转为java对象调用

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

已完成功能:
  • 排序获取对象
  • String获取长度可以定义
  • 可以将某一个字段的值,做为下面某个字段的长度,用于动态获取
  • 将字符串转位2进制字符串输出
  • 传入class返回对象
  • 使用ByteBuffer解析,避免并发

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


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

├── 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 {
    int order(); //排序,java反射后没有顺序,所以需要排序字段辅助
    StructEnum value() default StructEnum.Null; //枚举,如果要将byte数组取出为2进制字符串时,需要使用
    int length() default 0;  //长度,标识byte[]和String类型的变量需要取出多少个字节
    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

/**
 * @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 xx = SrStruct.translation(XXX.class,bytes,11);
2、不带偏移量
	XXX xx = SrStruct.translation(XXX.class,bytes);
3、使用NettyByteBuf的(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{
    @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();
    }
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值