zoukankan      html  css  js  c++  java
  • 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";
        }
    }

    运行结果如下

    1

    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);
        }
    }

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

    QQ截图20160507180528

    注: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);
        }
      }

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

        
         作者: 重名 
         出处: http://www.cnblogs.com/daily-note 
    
  • 相关阅读:
    使用 asp.net mvc和 jQuery UI 控件包
    ServiceStack.Redis 使用教程
    HTC T8878刷机手册
    Entity Framework CodeFirst 文章汇集
    2011年Mono发展历程
    日志管理实用程序LogExpert
    使用 NuGet 管理项目库
    WCF 4.0路由服务Routing Service
    精进不休 .NET 4.0 (1) asp.net 4.0 新特性之web.config的改进, ViewStateMode, ClientIDMode, EnablePersistedSelection, 控件的其它一些改进
    精进不休 .NET 4.0 (7) ADO.NET Entity Framework 4.0 新特性
  • 原文地址:https://www.cnblogs.com/daily-note/p/5469369.html
Copyright © 2011-2022 走看看