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 
    
  • 相关阅读:
    MySQL 允许远程连接
    EeePad刷机
    Ubuntu安装Oracle JDK
    Windows Azure Tips
    查看MySQL数据库大小
    Tomcat 7 DBCP 配置(MySQL)
    几个国内的OpenSource镜像站
    好吧,这是我的第一篇文章。
    安卓软件推荐56冰箱IceBox
    ArrayList 冷门方法
  • 原文地址:https://www.cnblogs.com/daily-note/p/5469369.html
Copyright © 2011-2022 走看看