dubbo序列化的一点注意

最近工作中遇见了一个小问题,在此记录一下,大致是这样的,有一父类,有一个属性traceId,主要是记录日志号,这样可以把所有日志串起来,利于排查问题,所有的pojo对象继承于此,但是其中一同事在子类pojo中也增加了这一个属性,在消费者端给traceId设置了值,但经过序列化解析后,提供者端这个traceId时,值为空,解决问题很简单啊,把子类中的traceId属性去掉搞定。

虽然问题很好解决但是这让笔者很懵逼啊,什么状况,都清楚地,实例化的子类,私有属性,取的肯定是实例设定的值,虽然我对此深信不疑,但是这还是让我怀疑了我自己,于是写了如下一些代码的验证这个问题。

1.先把问题抛出来一下。

Consumer端代码

@Setter
@Getter
@ToString
public class BaseBean implements Serializable {
    private String xxx;
    private String yyy;
    private Integer zzz;
}
@Setter
@Getter
@ToString
public class Bean extends BaseBean {
    private String xxx;
    private String yyy;
    private Integer zzz;
    private String myStr;
}
public class Consumer {

    public static void main(String[] args) throws Exception {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
                new String[] { "applicationContext.xml" });
        context.start();

        DemoService demoService = (DemoService) context.getBean("demoService");
        Bean bean = new Bean();
        bean.setMyStr("123");
        bean.setXxx("xxx");
        bean.setYyy("yyy");
        bean.setZzz(789);
        String hello = demoService.serTest(bean);
        System.out.println(hello);
        System.in.read();
}

Provider端代码

public class DemoServiceImpl implements DemoService {
    public String serTest(Bean bean) {
        System.out.println(bean);
        return "123";
    }
}

运行结果如下

2.Java中序列化就真的会出现这样的问题?

代码如下

public class TestSeriali {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Bean bean = new Bean();
        bean.setMyStr("123");
        bean.setXxx("xxx");
        bean.setYyy("yyy");
        bean.setZzz(789);
        ObjectOutputStream out  = new ObjectOutputStream(new FileOutputStream("chongming"));
        out.writeObject(bean);
        System.out.println("序列化完毕..");
        out.close();

        ObjectInputStream in = new ObjectInputStream(new FileInputStream("chongming"));
        Bean beanResult = (Bean) in.readObject();
        System.out.println("反序列化完毕..");
        System.out.println(beanResult);
    }
}

这段代码很显然父类三个属性,子类四个属性,其中三个与父类相同。代码运行结果如下

注:dubbo支持的其余集中序列化方式也做了验证,结果都是一样的,在这里就略过了。

这段代码证实了笔者一直的想法还是对的,但是问题就是出在dubbo的反序列化了。好吧翻翻dubbo的反序列化的源码吧,看看到底是咋回事

3.具体原因研究

代码比较多,挑几点重要的记录下,首先反序列化的类是JavaSerializer。

这个类的构造方法里调用了这样的方法getFieldMap,把里面本类和父类的所有方法放到一个fieldMap里,因为是HashMap,为了保证方法名不覆盖,这个方法里做了一个操作就是fieldMap.get(field.getName()) != null,有的话就继续循环下去不覆盖,这样的话如果有同名的方法,那只有子类的方法在里面。还有这个类Hessian2Input要说下,其中的方法readObjectInstance,它会取到本类和父类的所有方法放到一个数组fieldNames下,这些说完了说到这里面反序列化的方法JavaSerializer的readObject,是按fieldNames数组循环取值,在流里面挨个取出来,一直赋给本类的set方法,先是有值的,到父类时,取到的为空,就把本类的值覆盖了。到这里原因就清楚了。

主要的代码贴下来好了,如下

JavaSerializer构造方法及getFieldMap方法,获取到fieldMap

public JavaDeserializer(Class cl)
  {
    _type = cl;
    _fieldMap = getFieldMap(cl);
.......
protected HashMap getFieldMap(Class cl)
  {
    HashMap fieldMap = new HashMap();

    for (; cl != null; cl = cl.getSuperclass()) {
      Field []fields = cl.getDeclaredFields();
      for (int i = 0; i < fields.length; i++) {
        Field field = fields[i];

        if (Modifier.isTransient(field.getModifiers())
        || Modifier.isStatic(field.getModifiers()))
          continue;
        else if (fieldMap.get(field.getName()) != null)
          continue;
......
Hessian2Input的readObjectInstance
private Object readObjectInstance(Class cl, ObjectDefinition def)
    throws IOException
  {
    String type = def.getType();
    String []fieldNames = def.getFieldNames();
......

JavaSerializer的readObject,这个贴的全一点

public Object readObject(AbstractHessianInput in,
               Object obj,
               String []fieldNames)
    throws IOException
  {
    try {
      int ref = in.addRef(obj);

      for (int i = 0; i < fieldNames.length; i++) {
        String name = fieldNames[i];
        
        //重名的话,取出的都是私有的属性
        FieldDeserializer deser = (FieldDeserializer) _fieldMap.get(name);

        if (deser != null)
      // 当in读到父类时,把本类的属性值覆盖掉了
      deser.deserialize(in, obj);
        else
          in.readObject();
      }

      Object resolve = resolve(obj);

      if (obj != resolve)
    in.setRef(ref, resolve);

      return resolve;
    } catch (IOException e) {
      throw e;
    } catch (Exception e) {
      throw new IOExceptionWrapper(obj.getClass().getName() + ":" + e, e);
    }
  }

其中注释俩句是笔者加的,在此原因也找到了,问题很好解决,这部分源码也不是很难,这些全是自己翻源码看的,可能我的理解也不完全对,如果不对谁看见了,欢迎交流。

时间: 2024-08-27 20:08:04

dubbo序列化的一点注意的相关文章

.NET序列化的一点技巧(附Demo)

阅读目录 介绍 详细 处理 结论 Demo下载 介绍 序列化是将对象状态转换为可保持或传输的形式的过程.序列化的补集是反序列化,后者将流转换为对象.这两个过程一起保证数据易于存储和传输. .NET Framework 提供了两个序列化技术: 二进制序列化保持类型保真,这对于多次调用应用程序时保持对象状态非常有用.例如,通过将对象序列化到剪贴板,可在不同的应用程序之间共享对象.您可以将对象序列化到流.磁盘.内存和网络等.远程处理使用序列化,"按值"在计算机或应用程序域之间传递对象. XM

.NET序列化的一点技巧

介绍 序列化是将对象状态转换为可保持或传输的形式的过程.序列化的补集是反序列化,后者将流转换为对象.这两个过程一起保证数据易于存储和传输. .NET Framework 提供了两个序列化技术: 二进制序列化保持类型保真,这对于多次调用应用程序时保持对象状态非常有用.例如,通过将对象序列化到剪贴板,可在不同的应用程序之间共享对象.您可以将对象序列化到流.磁盘.内存和网络等.远程处理使用序列化,“按值”在计算机或应用程序域之间传递对象. XML 序列化只序列化公共属性和字段,并且不保持类型保真.当您

dubbo学习 三 dubbox概述

当当网根据自身的需求,对dubbo进行了扩展就叫成了dubbox.具体的使用方法可以参照官网各种例子:http://dangdangdotcom.github.io/dubbox/ 支持rest风格远程调用 之前了解过restful服务具体是什么,resteasy也了解过,所以看到就可以理解. 基于非常成熟的之前看过的rest实现jboss-resteasy框架,他把这个框架集成到了dubbo里面实现了rest风格的远程调用,这样就大大简化了服务的开发和更多类型的消费者系统.也使dubbo可以对

转:比较spring cloud和dubbo,各自的优缺点是什么

原文:https://blog.csdn.net/u010664947/article/details/80007767 dubbo由于是二进制的传输,占用带宽会更少 springCloud是http协议传输,带宽会比较多,同时使用http协议一般会使用JSON报文,消耗会更大 dubbo的开发难度较大,原因是dubbo的jar包依赖问题很多大型工程无法解决 springcloud的接口协议约定比较自由且松散,需要有强有力的行政措施来限制接口无序升级 dubbo的注册中心可以选择zk,redis

java原生序列化和Kryo序列化性能比较

简介 最近几年,各种新的高效序列化方式层出不穷,不断刷新序列化性能的上限,最典型的包括: 专门针对Java语言的:Kryo,FST等等 跨语言的:Protostuff,ProtoBuf,Thrift,Avro,MsgPack等等 这些序列化方式的性能多数都显著优于hessian2(甚至包括尚未成熟的dubbo序列化).有鉴于此,我们为dubbo引入Kryo和FST这 两种高效Java序列化实现,来逐步取代hessian2.其中,Kryo是一种非常成熟的序列化实现,已经在Twitter.Group

基于dubbo框架下的RPC通讯协议性能测试

一.前言 Dubbo RPC服务框架支持丰富的传输协议.序列化方式等通讯相关的配置和扩展.dubbo执行一次RPC请求的过程大致如下:消费者(Consumer)向注册中心(Registry)执行RPC请求,注册中心分配服务URL并路由到具体服务提供方(Provider),消费者和服务提供方建立网络连接,服务提供方在本地创建连接池对象并提供远程服务,对于长连接类型协议(如dubbo协议)将保持连接,减少握手认证,调用过程中可以避免频繁建立和断开连接导致的性能开销,保持长连接需要有心跳包的发送,所以

java.io.Serializable 序列化接口

什么是序列化.反序列化? Serialization(序列化)是一种将对象以一连串的字节描述的过程: 反序列化deserialization是一种将这些字节重建成一个对象的过程. 序列化通俗一点说就是能将Java对象转化成文本文件的一种方式. 什么情况下需要序列化 ?a)当你想把的内存中的对象保存到一个文件中或者数据库中时候:b)当你想用套接字在网络上传送对象的时候:c)当你想通过RMI传输对象的时候: 如何实现序列化 ? 将需要序列化的类实现Serializable接口就可以了. public

dubbu 官方参考手册~备案(防止哪天阿里一生气把dubbo给删除了)

首页  ||  下载  ||  用户指南  ||  开发者指南  ||  管理员指南  ||  培训文档  ||  常见问题解答  ||  发布记录  ||  发展路线  ||  社区 English | 中文 用户指南 入门 背景 需求 架构 用法 快速启动 服务提供者 服务消费者 依赖 必需依赖 缺省依赖 可选依赖 成熟度 功能成熟度 策略成熟度 配置 Xml配置 属性配置 注解配置 API配置 示例 启动时检查 集群容错 负载均衡 线程模型 直连提供者 只订阅 只注册 静态服务 多协议 多

dubbo用户指南

用户指南 入门 背景 需求 架构 用法 快速启动 服务提供者 服务消费者 依赖 必需依赖 缺省依赖 可选依赖 成熟度 功能成熟度 策略成熟度 配置 Xml配置 属性配置 注解配置 API配置 示例 启动时检查 集群容错 负载均衡 线程模型 直连提供者 只订阅 只注册 静态服务 多协议 多注册中心 服务分组 多版本 分组聚合 参数验证 结果缓存 泛化引用 泛化实现 回声测试 上下文信息 隐式传参 异步调用 本地调用 参数回调 事件通知 本地存根 本地伪装 延迟暴露 并发控制 连接控制 延迟连接 粘