500字范文,内容丰富有趣,生活中的好帮手!
500字范文 > Hessian 序列化 反序列化

Hessian 序列化 反序列化

时间:2018-08-14 04:57:13

相关推荐

Hessian 序列化 反序列化

动手点关注干货不迷路👆

背景

问题和思考:

序列化参数有枚举属性,序列化端增加一个枚举,能否正常反序列化?

序列化子类,它和父类有同名参数,反序列化时,同名参数能否能正常赋值?

序列化对象增加参数,反序列化类不增加参数,能否正常反序列化?

用于序列化传输的属性,用包装器比较好,还是基本类型比较好?

为什么要使用序列化和反序列化

程序在运行过程中,产生的数据,不能一直保存在内存中,需要暂时或永久存储到介质(如磁盘、数据库、文件)里进行保存,也可能通过网络发送给协作者。程序获取原数据,需要从介质,或网络传输获得。传输的过程中,只能使用二进制流进行传输。

简单的场景,基本类型数据传输。通过双方约定好参数类型,数据接收方按照既定规则对二进制流进行反序列化。

复杂的场景,传输数据的参数类型可能包括:基本类型、包装器类型、自定义类、枚举、时间类型、字符串、容器等。很难简单通过约定来反序列化二进制流。需要一个通用的协议,共双方使用,进行序列化和反序列化。

三种序列化协议及对比

对比

Fatherfather=newFather();father.name="厨师";ment="川菜馆";father.simpleInt=1;father.boxInt=newInteger(10);father.simpleDouble=1;father.boxDouble=newDouble(10);father.bigDecimal=newBigDecimal(11.5);

运行结果:

jdk序列化结果长度:626,耗时:55jdk反序列化结果:Father{version=0, name='厨师',comment='川菜馆', boxInt=10, simpleInt=1, boxDouble=10.0, simpleDouble=1.0, bigDecimal=11.5}耗时:87hessian序列化结果长度:182,耗时:56hessian反序列化结果:Father{version=0, name='厨师',comment='川菜馆', boxInt=10, simpleInt=1, boxDouble=10.0, simpleDouble=1.0, bigDecimal=11.5}耗时:7Fastjson序列化结果长度:119,耗时:225Fastjson反序列化结果:Father{version=0, name='厨师',comment='川菜馆', boxInt=10, simpleInt=1, boxDouble=10.0, simpleDouble=1.0, bigDecimal=11.5}耗时:69

分析:

jdk 序列化耗时最短,但是序列化结果长度最长,是其它两种的 3 ~ 5 倍。

fastjson 序列化结果长度最短,但是耗时是其它两种的 4 倍左右。

hessian 序列化耗时与 jdk 差别不大,远小于 fastjson 序列化耗时。且与 jdk 相比,序列化结果占用空间非常有优势。另外,hessian 的反序列化速度最快,耗时是其它两种的 1/10。

综上比较,hessian 在序列化和反序列化表现中,性能最优。

Hessian 序列化实战

实验准备

父类

publicclassFatherimplementsSerializable{/***静态类型不会被序列化*/privatestaticfinallongserialVersionUID=1L;/***transient不会被序列化*/transientintversion=0;/***名称*/publicStringname;/***备注*/publicStringcomment;/***包装器类型1*/publicIntegerboxInt;/***基本类型1*/publicintsimpleInt;/***包装器类型2*/publicDoubleboxDouble;/***基本类型2*/publicdoublesimpleDouble;/***BigDecimal*/publicBigDecimalbigDecimal;publicFather(){}@OverridepublicStringtoString(){return"Father{"+"version="+version+",name='"+name+'\''+",comment='"+comment+'\''+",boxInt="+boxInt+",simpleInt="+simpleInt+",boxDouble="+boxDouble+",simpleDouble="+simpleDouble+",bigDecimal="+bigDecimal+'}';}}

子类

publicclassSonextendsFather{/***名称,与father同名属性*/publicStringname;/***自定义类*/publicAttributesattributes;/***枚举*/publicColorcolor;publicSon(){}}

属性-自定义类

publicclassAttributesimplementsSerializable{privatestaticfinallongserialVersionUID=1L;publicintvalue;publicStringmsg;publicAttributes(){}publicAttributes(intvalue,Stringmsg){this.value=value;this.msg=msg;}}

枚举

publicenumColor{RED(1,"red"),YELLOW(2,"yellow");publicintvalue;publicStringmsg;Color(){}Color(intvalue,Stringmsg){this.value=value;this.msg=msg;}}

使用到的对象及属性设置

Sonson=newSon();son.name="厨师";//父子类同名字段,只给子类属性赋值ment="川菜馆";son.simpleInt=1;son.boxInt=newInteger(10);son.simpleDouble=1;son.boxDouble=newDouble(10);son.bigDecimal=newBigDecimal(11.5);son.color=Color.RED;son.attributes=newAttributes(11,"hello");

运行结果分析

使用 Hessian 序列化,结果写入文件,使用 vim 打开。使用 16 进制方式查看,查看命令:%!xxd

00000000:430764746f2e536f6e9a046e616d6504C.dto.Son..name.00000010:ment.box00000020:496e740973696d706c65496e7409626fInt.simpleInt.bo00000030:78446f75626c650c73696d706c65446fxDouble.simpleDo00000040:75626c650a6174747269627574657305uble.attributes.00000050:636f6c6f720a626967446563696d616ccolor.bigDecimal00000060:6002e58ea8e5b8884e03e5b79de88f9c`.......N.......00000070:e9a6869a915d0a5c430e64746f2e4174.....].\C.dto.At00000080:7472696275746573920576616c756503tributes..value.00000090:6d7367619b0568656c6c6f430964746fmsga..helloC.dto000000a0:2e436f6c6f7291046e616d6562035245.Color..nameb.RE000000b0:4443146a6176612e6d6174682e426967DC.java.math.Big000000c0:446563696d616c910576616c75656304Decimal..valuec.000000d0:31312e350a11.5.

对其中的十六进制数逐个分析,可以拆解为一下结构:参考 hessian 官方文档,链接:/doc/hessian-serialization.html

序列化原理

序列化规则:

被序列化的类必须实现了 Serializable 接口

静态属性和 transient 变量,不会被序列化。

枚举类型在序列化后,存储的是枚举变量的名字

序列化结果的结构:类定义开始标识 C -> 类名长度+类名 -> 属性数量 -> (逐个)属性名长度+属性名 -> 开始实例化标识 -> (按照属性名顺序,逐个设置)属性值(发现某个属性是一个对象,循环这个过程)

反序列化

通俗原理图:

解释:这是前边的序列化文件,可以对着这个结构理解反序列化的过程。

解释:读取到“C”之后,它就知道接下来是一个类的定义,接着就开始读取类名,属性个数和每个属性的名称。并把这个类的定义缓存到一个_classDefs 的 list 里。

解释:通过读取序列化文件,获得类名后,会加载这个类,并生成这个类的反序列化器。这里会生成一个_fieldMap,key 为反序列化端这个类所有属性的名称,value 为属性对应的反序列化器。

解释:读到 6 打头的 2 位十六进制数时,开始类的实例化和赋值。

遗留问题解答:

增加枚举类型,反序列化不能正常读取。

原因:枚举类型序列化结果中,枚举属性对应的值是枚举名。反序列化时,通过枚举类类名+枚举名反射生成枚举对象。枚举名找不到就会报错。

反序列化为子类型,同名属性值无法正常赋值。

序列化对象增加参数,反序列化可以正常运行。

原因:反序列化时,是先通过类名加载同名类,并生成同名类的反序列化器,同名类每个属性对应的反序列化器存储在一个 map 中。在反序列化二进制文件时,通过读取到的属性名,到 map 中获取对应的反序列化器。若获取不到,默认是 NullFieldDeserializer.DESER。待到读值的时候,仅读值,不作 set 操作

序列化和反序列化双方都使用对象类型时,更改属性类型,若序列化方不传输数据,序列化结果是‘N’,能正常反序列化。但是对于一方是基本类型,更改属性类型后,因为 hessian 对于基本类型使用不同范围的值域,所以无法正常序列化。

参考文档:

/p/44787200

/1131/

hessian 官方文档:序列化规则/doc/hessian-serialization.html#anchor10

ASCII 编码对照表/

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。