一. 引入 及 参考文献
看到一篇文章 是实际应用中遇到的问题 收藏一下
利用序列化方法序列化对象 再 反序列化之后 得到的对象 是否能维持对象的多态特征的问题。
二. 问题描述
服务端拿到request对象后,根据request.getClass()获取request实际对象类型,向下转型后执行对应逻辑。
那么这里就存在一个问题,已经向上转型的对象,在经过RPC的序列化与反序列化后的对象,到底能否正常的再向下转型,并且正常的调用到服务端接口?
三. 结论
Dubbo默认的hessian支持请求/响应类为Object.class的多态, Java自带的实现了serialize接口的序列化也支持。而json xml等序列化方法的泛型接口经过类型擦除后,就是Object.class。选用目前默认的hessian序列化可行。
其他序列化不支持的本质原因为:未传入或未使用对象真实的类型做反序列化。
从整个链路开始分析,如果说序列化协议传入了java的className,服务端则可以通过Class.forName()来加载到对应的对象类型,实现序列化,目前各个协议中,是否带有序列化Class类信的统计如下
序列化方法
是否带有序列化类信息
是否支持多态
Hessian/Hessian2
有
只有hessian支持Object.class的多态
JDK自带序列化
有
是
Json/Xml
无
否
Protobuf
无
目前实现,Request&Response传入的实际类型未使用
四. Hessian序列化分析
Hessian作为Dubbo默认的序列化方式 是支持多态的 能反应对象的实际类型。
Dubbo协议帧包含头部信息和body信息两部分 ,其中header中主要是Dubbo自身的session/status相关信息,body为实际的业务信息。
request部分的body信息如下:
内容
类型
说明
Dubbo Version
String
如:2.5.10
Service Interface
String
如:com.ctrip.framework.cdubbo.demo.api.DubboDemoService
Service Version
String
如:1.0.0
Method Name
String
如:hello
ParameterType
String
如:Lcom/ctrip/framework/cdubbo/demo/api/HelloRequestType;
Arguments
Object
请求对象,如:HelloRequestType
Attachments
Map<String,String>
隐式参数Map
可见request时,已经把ParameterType带给了服务端,服务端会拿到此Class信息,进行反序列化。则不需要传入实际对象类型,具体代码段为DecodeableRpcInvocation中的decode方法:
下图分别为框架的protobuf序列化与反序列化方法,可见response的序列化写入了对象的真实class信息,但是,和request的反序列化一样,此处真实的class name未使用。但是实际上在readObject(Class cls)中,传入的是接口的Return Type的Class信息。
Hessian为什么支持接口类型为Object.class的多态:
因为如果exceptClass为Object.class,此处会使用真实的对象类型作为反序列化的class,不会使用接口的参数类型。
五. 总结
最后很快的总结几句,序列化和反序列化是 到底是否能体现类的多态特性体现在两点:
1. 序列化之后的编码信息是否带有对象的真实类信息
2. 反序列化解码时 是否使用了对象的真实类信息 还是说使用的期待返回值类信息
就例如Person类的Object的子类 现在声明Object object = new Pesron(…);
在序列化时是否加上了Person类这个信息 一同传递 如果没有 那么客户端自然无法体现这个多态信息;
其次 在反序列化时 将方法的返回值声明为Object类 如果反序列化时直接使用了这个返回值类型 而没有使用传递过来的真实类型Person 当然就只是一个Object类对象 无法体现多态啦 主要还得是看解析方法解析时到底用没用到原始类型信息。