龙空技术网

手写一个Java的结构体实现Buffer和JavaBean的转换

lintge 227

前言:

眼前小伙伴们对“c语言结构体删除”都比较关怀,姐妹们都想要知道一些“c语言结构体删除”的相关资讯。那么小编同时在网摘上网罗了一些有关“c语言结构体删除””的相关知识,希望我们能喜欢,小伙伴们一起来学习一下吧!

#头条创作挑战赛#

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

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

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

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

已完成功能:

功能点

完成度

排序获取对象

100%

String获取长度可以定义

100%

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

100%

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

100%

传入class返回对象

100%

使用ByteBuffer解析,避免并发

100%

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

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

实际项目中结构截图

├──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)@Documentedpublic @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 下午*/@Datapublic 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-持续分享开发新技术,代码新知识!

标签: #c语言结构体删除