一种pojo对象自动二进制编解码的技术(序列和反序列化)

本文介绍了一种利用注解和反射技术实现的简单高效的POJO对象编码解码方案,能自动处理Integer、String等基本类型,提升开发效率。通过assign和simple模式,支持自定义编码顺序或按字段顺序操作。
摘要由CSDN通过智能技术生成

1、功能简介:

在java网络编程中,数据传输是基于二进制的编码方式,所以我们常常需要对对象进行序列和反序列化,最常见的有rest风格(比如json)的序列和反序列化,或者基于继承 「Serializable」接口的JAVA 标准序列化,还有一种最原始的是按对象的属性类型一个个添加到io流上去。第三种较于前两种对开发者来说不够友好,但优势是冗余信息少,序列化后字节数会更少。而本文所介绍的技术就是基于第三种方式,通过注解和反射技术,自动完成pojo对象编码和解码工作,简化开发人员繁琐的编解码工作,大大提高开发效率。

2、使用说明:

目前本API只支持属性类型为:Integer、String、Double、Boolean、Short、Float、Byte、Long和Date的POJO类。有两种编解码模式:assign和simple。assign是可以通过加注解*@ScanOrder*(序号)来指定任意编码顺序。如果类的属性未加注解,则过滤掉。如下面student所示:

public class Student
{
	@ScanOrder(1)
	private Integer id;
	@ScanOrder(3)
	private String name;
	@ScanOrder(5)
	private Boolean sex;
	@ScanOrder(4)
	private String cellphone;
	@ScanOrder(2)
	private Date birthday;
}

此时编码顺序是:id->birthday->name->cellphone->sex

而simple模式是按反射遍历filed的顺序来编解码的。
编码顺序是: id-> name ->sex ->cellphone->birthday

另外pojo类的属性是采用包装类的,考虑到属性值可能会是null的情况,那么得在编码前加n个字节来表示各个属性是否为null值,其中n=此类的属性个数,比如示例中的Student类,假如sex和birthday属性为null,在simple模式编码前5个字节是:01 01 00 01 00
具体可以参考下面执行结果:

  public static void main(String[] args) throws  Exception{
        System.out.println("-------------assign编解码--------------------");
        PoJoCodec codec=new PoJoCodec();
        codec.dateFormat ="yyyy-MM-dd";
        
        Student s1 = new Student();
		s1.setId(20190001);
		s1.setName("张三");
		s1.setSex(true) ;
		s1.setCellphone("13810012345");
		s1.setBirthday(new Timestamp(System.currentTimeMillis()));
		//编码
        byte[] data=codec.assignCode(s1);
        Utils.print(data,0,data.length);
        //解码
         s1=(Student) codec.assignDecode(Student.class,data);
        System.out.println(s1);
        
        System.out.println("-------------simple编解码--------------------");
        //编码
        data=codec.simpleCode(s1);
        Utils.print(data,0,data.length);
        //解码
        s1=(Student) codec.simpleDecode(Student.class,data);
        System.out.println(s1);

    }

执行结果输出:
-------------assign编解码--------------------
01 01 01 01 01 01 34 13 31 32 30 32 30 2D 31 31
2D 32 34 00 06 E5 BC A0 E4 B8 89 00 0B 31 33 38
31 30 30 31 32 33 34 35 01
学号: 20190001,姓名: 张三,性别: 男,手机: 13810012345,生日: 2020-11-24
-------------simple编解码--------------------
01 01 01 01 01 01 34 13 31 00 06 E5 BC A0 E4 B8
89 01 00 0B 31 33 38 31 30 30 31 32 33 34 35 32
30 32 30 2D 31 31 2D 32 34
学号: 20190001,姓名: 张三,性别: 男,手机: 13810012345,生日: 2020-11-24

编码解释:
01 01 01 01 01 表示属性5个值都不为null;
01 34 13 31 int类型 4个字节 值: 20190001
00 06 short类型 2个字节 表示后面是string长度是6个字节
E5 BC A0 E4 B8 89 string类型 值: 张三
01 byte类型 1个字节 表示性别为男
00 0B short类型 2个字节 表示后面是string长度是11个字节
31 33 38 31 30 30 31 32 33 34 35 string类型 表示13810012345
32 30 32 30 2D 31 31 2D 32 34 string类型 表示 2020-11-24

3、源代码

指定扫描顺序的注解

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ScanOrder {
    int value();
}
/**
 * pojo类二进制编解码器
 */
public class PoJoCodec {
    /**
     * 指定字符集
     */
    public String charsetName="UTF-8";
    public enum Pack {yes,no}

    /**
     * 是否有指定注解顺序
     */
    public enum AssignOrder {YES,NO}
    /**
     * 指定日期格式
     */
    public String dateFormat ="yyyy-MM-dd HH:mm:ss";
    /**
     * 指定编码时内容缓存大小
     */
    public int buffSize =1024;
    private class FieldInfo
    {
        public String key;
        public Object value;
        public int order;
        public Class type;
        public Field field;

        public String getKey() {
            return key;
        }

        public void setKey(String key) {
            this.key = key;
        }

        public Object getValue() {
            return value;
        }

        public void setValue(Object value) {
            this.value = value;
        }

        public int getOrder() {
            return order;
        }

        public void setOrder(int order) {
            this.order = order;
        }

        public <T> Class<T> getType() {
            return type;
        }

        public Field getField() {
            return field;
        }

        public void setField(Field field) {
            this.field = field;
        }

        public void setType(Class type) {
            this.type = type;
        }
    }

    /**
     * 指定顺序的pojp类解码器
     * @param type pojo类类型
     * @param data 待解码的二进制数据
     * @return pojo实例
     * @throws Exception
     */
    public <T> Object assignDecode(Class<T> type, byte[] data) throws  Exception{
        Object pojo=type.newInstance();
        //扫描pojo并排序
        List<FieldInfo> list = scanPoJo(pojo,AssignOrder.YES);
        decode(list,pojo,data);
        return pojo;
    }
    /**
     * 简单的pojp类解码器
     * @param type pojo类类型
     * @param data 待解码的二进制数据
     * @return pojo实例
     * @throws Exception
     */
    public <T> Object simpleDecode(Class<T> type, byte[] data) throws  Exception{
        Object pojo=type.newInstance();
        //扫描pojo并排序
        List<FieldInfo> list = scanPoJo(pojo,AssignOrder.NO);
        decode(list,pojo,data);
        return pojo;
    }

    /**
     * 解码
     * @param list
     * @param pojo
     * @param data
     * @throws Exception
     */
    private  void decode(List<FieldInfo> list ,Object pojo, byte[] data) throws Exception {
        //拆分成两组字节数据,states描述属性值是否为null,Values描述属性的值编码
        ByteBuffer buffer=ByteBuffer.wrap(data);
        byte[] bytes=new byte[list.size()];
        buffer.get(bytes);
        ByteBuffer fieldStates =ByteBuffer.wrap(bytes);
        bytes=new byte[data.length-list.size()];
        buffer.get(bytes);
        ByteBuffer fieldValues=ByteBuffer.wrap(bytes);
        //遍历解码
        for (FieldInfo info : list) {
            //读取包装类状态码,1表示有值,0表示表示null值
            if (fieldStates.get() == (byte) 1) {
                //根据类型解码
                Object value = typeDecode(fieldValues, info.type);
                //判断属性的修饰符,从而选择直接取值还是通过getter取值
                int mod =info.field.getModifiers();
                if(Modifier.isPublic(mod)){
                    info.field.set(pojo, value);
                }else {
                    String methodName=ReflectUtil.setterName(info.field.getName());
                    Method method=ReflectUtil.findMethod(pojo.getClass(),methodName);
                    method.invoke(pojo,value);
                }

            }
        }
    }

    /**
     * 指定顺序的pojo类编码器
     * @param object 待编码的pojo实例
     * @return pojo类二进制编码
     * @throws Exception
     */
    public  byte[] assignCode(Object object) throws Exception {
        if(object==null) {
            return null;
        }
        //排序
        List<FieldInfo> list = scanPoJo(object,AssignOrder.YES);
        return  code(list);

    }

    /**
     * 简单的pojo类编码器
     * @param object 待编码的pojo实例
     * @return pojo类二进制编码
     * @throws Exception
     */
    public  byte[] simpleCode(Object object) throws Exception {
        if(object==null) {
            return null;
        }
        //排序
        List<FieldInfo> list = scanPoJo(object,AssignOrder.NO);
        return  code(list);

    }

    /**
     * 编码
     * @param list
     * @return
     * @throws Exception
     */
    private byte[] code(List<FieldInfo> list ) throws Exception {
        //编码
        ByteBuffer fieldStates =ByteBuffer.allocate(list.size());
        ByteBuffer fieldValues =ByteBuffer.allocate(buffSize);
        for (FieldInfo fieldInfo :list){
            //属性值为null,state为0
            if(fieldInfo.value==null){
                fieldStates.put((byte) 0);
            }else {
                fieldStates.put((byte) 1);
                typeCode(fieldValues, fieldInfo.value, fieldInfo.type);
            }
        }
        //描述属性是否为null和描述属性值的字节数组合并
        int statesSize=fieldStates.position();
        int valuesSize=fieldValues.position();
        ByteBuffer buffer =ByteBuffer.allocate(statesSize+valuesSize);
        buffer.put(fieldStates.array());
        buffer.put(fieldValues.array(),0,valuesSize);
        return buffer.array();
    }

    /**
     * 根据类型解码
     * @param fieldValues pojo类属性值的字节数组缓冲
     * @param type 属性的类型
     * @return 属性的值
     * @throws Exception
     */
    private<T> T typeDecode(ByteBuffer fieldValues,Class<T> type)throws  Exception{
        Object obj=null;
        if(type.isAssignableFrom(Integer.class)){
            obj = fieldValues.getInt();
        }else if(type.isAssignableFrom(String.class)){
            short len= fieldValues.getShort();
            byte[] strbuf = new byte[len];
            fieldValues.get(strbuf, 0, len);
            obj=new String(strbuf,charsetName);
        }else if(type.isAssignableFrom(Double.class)){
            obj= fieldValues.getDouble();
        }else if(type.isAssignableFrom(Boolean.class)){
            obj= fieldValues.get()==0?false:true;
        }else if(type.isAssignableFrom(Short.class)){
            obj=  fieldValues.getShort();
        }else if(type.isAssignableFrom(Float.class)){
            obj=  fieldValues.getFloat();
        }else if(type.isAssignableFrom(Byte.class)){
            obj=  fieldValues.get();
        }else if(type.isAssignableFrom(Date.class)){
            int len= dateFormat.length();
            byte[] strbuf = new byte[len];
            fieldValues.get(strbuf, 0, len);
            String date=new String(strbuf,charsetName);
            SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
            obj=sdf.parse(date);
        }else if(type.isAssignableFrom(Long.class)){
            obj=  fieldValues.getLong();
        }else{
            //此处可以根据自己的需求定制
            throw  new Exception("不支持"+type+"类型");
        }
        return (T)obj;
    }

    /**
     * 根据类型编码
     * @param fieldValues pojo类属性值的字节数组缓冲
     * @param value 属性值
     * @param type 属性类型
     * @throws Exception
     */
    private<T> void typeCode(ByteBuffer fieldValues,Object value, Class<T> type) throws Exception {
        if(type.isAssignableFrom(Integer.class)){
            fieldValues.putInt((int) value);

        }else if(type.isAssignableFrom(String.class)){
            byte[] strbuf = ((String)value).getBytes(charsetName);
            //字符串长度
            fieldValues.putShort( (short) strbuf.length);
            fieldValues.put(strbuf);
        }else if(type.isAssignableFrom(Double.class)){
            fieldValues.putDouble((double) value);
        }else if(type.isAssignableFrom(Boolean.class)){
            fieldValues.put((boolean) value ? (byte)1 : (byte)0);
        }else if(type.isAssignableFrom(Short.class)){
            fieldValues.putShort((short) value);
        }else if(type.isAssignableFrom(Float.class)){
            fieldValues.putFloat((float) value);
        }else if(type.isAssignableFrom(Byte.class)){
            fieldValues.put((byte)value);
        }else if(type.isAssignableFrom(Date.class)){
            SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
            String date=sdf.format((Date) value);
            byte[] strbuf =date.getBytes(charsetName);
            fieldValues.put(strbuf);
        }else if(type.isAssignableFrom(Long.class)){
            fieldValues.putLong((long)value);
        }else{
            //此处可以根据自己的需求定制
            throw  new Exception("不支持"+type+"类型");
        }
    }

    /**
     * 扫描pojo类
     * @param object 待扫描的pojo对象
     * @param assignOrder 是否在pojo类的属性注解排序
     * @return 经过排序的属性信息列表
     * @throws Exception
     */
    private List<FieldInfo> scanPoJo(Object object,AssignOrder assignOrder) throws Exception {
        List<FieldInfo> list=new ArrayList<>();
        Class cls=object.getClass();
        Field[] fields=cls.getDeclaredFields();
        for(Field field:fields)
        {
            boolean bool=field.isAnnotationPresent(ScanOrder.class);
            //指定顺序,属性无注解则过滤掉这轮扫描
            if(assignOrder==AssignOrder.YES && !bool)
            {
               continue;
            }
            FieldInfo fieldInfo =new FieldInfo();
            fieldInfo.key=field.getName();
            fieldInfo.type =field.getType();
            fieldInfo.field=field;
            //判断属性的修饰符,从而选择直接取值还是通过getter取值
            int mod =field.getModifiers();
            if(Modifier.isPublic(mod)){
                fieldInfo.value=field.get(object);
            }else {
                String methodName=ReflectUtil.getterName(field.getName());
                Method method=ReflectUtil.findMethod(cls,methodName);
                fieldInfo.value=method.invoke(object);
            }
            if(bool)
            {
                ScanOrder order=field.getDeclaredAnnotation(ScanOrder.class);
                fieldInfo.order=order.value();
            }
            list.add(fieldInfo);
        }
        if(assignOrder==AssignOrder.YES){
            //排序
            //  list.stream().sorted(Comparator.comparing(MyField::getOrder).reversed())
            return list.stream().sorted(Comparator.comparing(FieldInfo::getOrder))
                    .collect(Collectors.toList());
        }else{
            return list;
        }
    }
}

源码所涉及的工具类ReflectUtil

//根据字段名获取getter方法
public static String getterName(String field)
{
	// "name" -> "getName()"
	char firstChar = Character.toUpperCase(field.charAt(0));
	StringBuffer sbuf = new StringBuffer("get" + field);
	sbuf.setCharAt(3, firstChar);
	return sbuf.toString();	
}
//根据字段名获取setter方法
public static String setterName(String field)
{
	// "name" -> "setName()"
	char firstChar = Character.toUpperCase(field.charAt(0));
	StringBuffer sbuf = new StringBuffer("set" + field);
	sbuf.setCharAt(3, firstChar);
	return sbuf.toString();	
}	
//通过方法的名称寻找相应Method
public static Method findMethod(Class cls, String methodName)
	{
		Method[] methodArray = cls.getDeclaredMethods();
		for(Method method : methodArray)
		{
			if(method.getName().equals(methodName))
			{
				return method;
			}
		}
		return null;
	}
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值