序列化中的多态问题

659 阅读3分钟

一. 引入 及 参考文献

       看到一篇文章 是实际应用中遇到的问题 收藏一下

       利用序列化方法序列化对象 再 反序列化之后 得到的对象 是否能维持对象的多态特征的问题。

二. 问题描述

       服务端拿到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类对象 无法体现多态啦 主要还得是看解析方法解析时到底用没用到原始类型信息。