赞
踩
Hessian反序列化问题
众所周知,Hessian框架提供的序列化方式,在性能上要优于Java自己的序列化方式。他将对象序列化,生成的字节数组的数量要相对于Java自带的序列化方式要更简洁。
目前公司的一个项目中,有RPC调用的需要,这里我们使用了公司自己的开源RPC框架Dubbo作为远程调用框架,进行业务方法的调用和对象的序列化。这里,我们没有对Dubbo做出特殊配置,Dubbo在Remoting层组件默认的序列化方式就是采用的Hessian协议处理。但是在真正部署测试时,走到需要远程调用的方式时,报出了一下异常(只截取了最核心的异常堆栈):
从最下面的异常信息可以看出,CountObject这个内部类在对象初始化时,报了参数校验的失败。这个看一下CountObject的出问题的构造函数就一目了然了:
现在在构造函数的第一行的Assert就失败了。可是哪里调用这个构造函数导致失败呢?继续网上翻看异常堆栈给出的信息。可以看出在JavaDeserializer.instantiate中抛出了HessianProtocolException异常。进去看一下Hessian这块的源码如下:
这里结合上面的异常堆栈可以知道,上面出问题的关键是_constructor和_constructorArgs。这两个东东又到底是啥呢?继续来看代码:
从JavaDeserializer的构造方法中可以看出,这里_constructor会被赋予参数最少的那个构造器。再回过头去看看CountObject的构造器(就上面列出来的那一个),不难看出,这里的_constructor就是上面的那个构造器了。
参看上面的getParamArg方法,就可以知道,由于CountObject唯一的一个构造器的两个参数都不是基本类型,所以这里_constructorArgs所包含的值全部是null。
OK,到这里,上面的异常就搞清楚了,Hessian反序列化时,使用反射调用构造函数生成对象时,传入的参数不合法,造成了上面的异常。知道了原因,解决的方法也很简单,就是添加了一个无参的构造器给CountObject,于是上面的问题就解决了。。。
这里,需要注意的是,如果序列化机制使用的是Hessian,序列化的对象又没有提供默认的无参构造器时,需要注意上面类似的问题了。
Java本身反序列化问题
Java本身的反序列化机制虽然性能稍差一些,但本身使用的约束条件相对却要宽松一些,其实只要满足下面两条,一个类对象就是可以完美支持序列化机制了:
对于构造函数本身没有任何约束。这里,Java序列化本身其实也是和new以及Java反射机制“平级”的实例化对象的方式。所以,对于单例模式的场景,还是需要考虑是否会有序列化因素造成的单例失效(因为他实例化对象不依赖于构造器,所以一个private的构造器显然没法阻止他的“胡作非为”)。当然,对于这种情况,也可以自己实现下面的方法:
通过实现上面的方法,自己可以在其中明确指定,放回的对象的实例是哪一个。但对于通过如上方式保证的单例模式至少需要注意一下两点:
鉴于上面所说的稍微复杂的现象,如果单纯的考虑单例的需要,更好的方式是通过枚举来实现,因为枚举至少可以在JVM层面,帮你保证每个枚举实例一定是单例的,即使使用反序列化机制,也无法绕过这个限制,所以可以帮你省不少心。
好了,上面扯的有点远了,关于Java本身的序列化机制,下面写了一个简单的把对象序列化成字节数组,再由字节数组反序列化回来的例子,看完之后应该会更明了一些:
上面代码执行的结果如下:
最后再补充一个Java序列化规范的地址,有时间时再细读一下:http://docs.oracle.com/javase/7/docs/platform/serialization/spec/serial-arch.html
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。