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

其中注釋倆句是筆者加的,在此原因也找到了,問題很好解決,這部分源碼也不是很難,這些全是自己翻源碼看的,可能我的理解也不完全對,如果不對誰看見了,歡迎交流。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM