当前位置: 首页 > 科技观察

Hessian序列化和反序列化

时间:2023-03-14 15:54:05 科技观察

背景问题和思考:序列化参数有枚举属性,如果在序列化端加一个枚举,是否可以正常反序列化?序列化子类时,它具有与父类同名的参数。反序列化时,同名参数是否可以正常赋值?序列化对象添加参数,反序列化类不添加参数。能正常反序列化吗?对序列化传输属性使用包装器或原始类型更好吗?为什么要使用序列化和反序列化程序?在运行过程中,产生的数据不可能一直保存在内存中,需要暂时或永久保存在介质(如磁盘、数据库、文件)中进行存储,也可能通过网络进行传输。给合作者。程序获取的原始数据需要从媒体或网络传输中获取。在传输过程中,只能使用二进制流进行传输。简单的场景,基本类型的数据传输。双方就参数类型达成一致后,数据接收方根据既定规则对二进制流进行反序列化。在复杂的场景下,传递数据的参数类型可能包括:基本类型、包装类型、自定义类、枚举、时间类型、字符串、容器等,很难按照约定简单反序列化一个二进制流。双方需要一个共同的协议来序列化和反序列化。三种序列化协议及jdk序列化协议特性对比(jdk自带)1.序列化:除static和transient类型2.特性:强类型,安全性高,序列化结果携带类型信息3.反序列化:基于Field机制4.应用场景:深拷贝fastjson(第三方实现)1.可读性好,占用空间小2.特点:弱类型,序列化结果不携带类型信息,可读性强。存在一些安全问题3、反序列化:基于Field机制,兼容Bean机制4、应用场景:消息、透传对象hessian(第三方实现)1、序列化:除static和transient类型2、特点:强类型,体积小,跨语言,序列化结果携带类型信息3.反序列化:基于Field机制,兼容Bean机制4.应用场景:RPC比较fatherfather=newFather();father.name="chef";father.comment="川菜馆";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='chef',comment='川菜馆',boxInt=10,simpleInt=1,boxDouble=10.0,simpleDouble=1.0,bigDecimal=11.5}耗时:87hessian序列化结果长度:182,耗时:56hessian反序列化结果:Father{version=0,name='chef',comment='川菜馆',boxInt=10,simpleInt=1,boxDouble=10.0,simpleDouble=1.0,bigDecimal=11.5}耗时:7Fastjson序列化结果长度:119,耗时:225Fastjson反序列化结果:Father{version=0,name='chef',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;/***名称*/public字符串名称;/***注释*/public字符串注释;/***类型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+'}';}}subclasspublicclassSonextendsFather{/***名称,与父亲同名的属性*/publicStringname;/***自定义类*/publicAttributesattributes;/***enumeration*/publicColorcolor;publicSon(){}}attribute-customclasspublicclassAttributesimplementsSerializable{privatestaticfinallongserialVersionUID=1L;publicintvalue;publicStringmsg;publicAttributes(){}publicAttributes(intvalue,Stringmsg){this.value=value;this.msg=msg;}}enumerationpublicenumColor{RED(1,"red"),YELLOW(2,"yellow");publicintvalue;publicStringmsg;Color(){}Color(intvalue,Stringmsg){this.value=value;this.msg=msg;}}使用的对象和属性设置Sonson=newSon();son.name="Chef";//父子类有相同的name字段,只给子类属性赋值son.comment="川菜馆";son.simpleInt=1;son.boxInt=新整数(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日制作,查看命令:%!XXD00000000:430764746F2E536F6E9A046E616D6504C.Dto.son..AME.00000010:6E6507636F6D65626F78Name.BOX000000000000000020:4966E7496E7496E7496E7496E796E7.496E796E796.6d706c65496e7409626fInt.simpleInt.bo00000030:78446f75626c650c73696d706c65446fxDouble.simpleDo00000040:75626c650a6174747269627574657305uble.attributes.00000050:636f6c6f720a626967446563696d616ccolor.bigDecimal00000060:6002e58ea8e5b8884e03e5b79de88f9c`........N.......00000070:e9a6869a915d0a5c430e64746f2e4174.....].\C.dto.At00000080:7472696275746573920576616c756503贡品..值.00000090:6d7367619b0568656c6c6f430964746fmsga..helloC.dto000000a0:2e436f6c6f7291046e616d6562035245.Color..nameb.RE000000b0:4443146a6176612e6d6174682e426967DC.java.math.Big000000c0:446563696d616c910576616c75656304十进制..valuec.000000d0:31312e350a11.5。这个分析可以拆解成如下结构:参考hessian官方文档,链接:http://hessian.caucho.com/doc/hessian-serialization.html序列化原则序列化规则:被序列化的类必须实现Serializable接口static属性和瞬态变量不会被序列化。序列化后,枚举类型存储枚举变量的名称。序列化结果的结构:类定义开始标识C->类名长度+类名->属性数量->(一个接一个)属性名长度+属性名->开始实例化标识->(按顺序属性名,一个一个设置)属性值(发现一个属性是一个对象,循环这个过程)反序列化流行示意图:解释:这是之前的序列化文件,通过这个结构就可以理解反序列化过程。解释:读完“C”就知道接下来是一个类的定义,然后开始读类名、属性个数和每个属性的名称。并将此类的定义缓存到_classDefs列表中。解释:读取序列化文件并获取类名后,将加载该类并生成该类的反序列化器。这里会生成一个_fieldMap,key是这个类在反序列化端的所有属性的名称,value是该属性对应的反序列化器。说明:当读取到一个以6开头的2位十六进制数时,就开始类的实例化和赋值。遗留问题解决:增加枚举类型,反序列化无法正常读取。原因:枚举类型的序列化结果中,枚举属性对应的值为枚举名称。反序列化时,通过枚举类名+枚举名反射生成一个枚举对象。找不到枚举名会报错。反序列化为子类型时,无法正常赋值同名属性值。序列化对象添加参数,反序列化可以正常运行。原因:反序列化时,先通过类名加载同名类,生成同名类的反序列化器,将同名类的每个属性对应的反序列化器存放在一个地图。反序列化二进制文件时,通过读取的属性名获取map中对应的反序列化器。如果不可用,默认值为NullFieldDeserializer.DESER。该读取值时,只读取值,不进行设置操作。当序列化和反序列化都使用对象类型时,更改属性类型。如果序列化器不传输数据,则序列化结果为'N',可以正常反转。序列化。但是对于oneside是一个基本类型,改变属性类型后,由于hessian对基本类型使用了不同的取值范围,所以无法正常序列化。