1. 程式人生 > >[轉]dubbo序列化物件的一個坑

[轉]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);
    }
  }

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


作者: 重名 

出處: http://www.cnblogs.com/daily-note

此文為轉載,如有侵權請聯絡刪除