Hessian序列化的一个潜在问题

Posted 风走了,雨停了

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Hessian序列化的一个潜在问题相关的知识,希望对你有一定的参考价值。

一. 最近的用rpc框架的时候,当用hessian序列化对象是一个对象继承另外一个对象的时候,当一个属性在子类和有一个相同属性的时候,反序列化后子类属性总是为null。

 

二. 示例代码:

DTO对象

public class User implements Serializable {
    private String username ;
    private String password;
    private Integer age;
}
public class UserInfo extends User {
    private String username;
}

 

序列化代码

      UserInfo user = new UserInfo();
        user.setUsername("hello world");
        user.setPassword("buzhidao");
        user.setAge(21);
ByteArrayOutputStream os
= new ByteArrayOutputStream(); //Hessian的序列化输出 HessianOutput ho = new HessianOutput(os); ho.writeObject(user); byte[] userByte = os.toByteArray(); ByteArrayInputStream is = new ByteArrayInputStream(userByte); //Hessian的反序列化读取对象 HessianInput hi = new HessianInput(is); UserInfo u = (UserInfo) hi.readObject(); System.out.println("姓名:" + u.getUsername()); System.out.println("年龄:" + u.getAge());

 

输出结果:

    姓名:null 

    年龄:21

 

 三.  一看这个结果一开始的反应就是不应该啊,后来自己带着好奇查看了网上资料终于找到了原因。

       1. hessian序列化的时候会取出对象的所有自定义属性,相同类型的属性是子类在前父类在后的顺序。

       2. hessian在反序列化的时候,是将对象所有属性取出来,存放在一个map中   key = 属性名  value是反序列类,相同名字的会以子类为准进行反序列化。

       3. 相同名字的属性 在反序列化的是时候,由于子类在父类前面,子类的属性总是会被父类的覆盖,由于java多态属性,在上述例子中父类 User.username = null

 

 四、 下面是关键源码分析 ,hessian版本是4.0.7

     1.序列化

     当序列化对象是一个java自定对象时,默认的序列化类是 UnsafeSerializer

     调用writeObject

 public void writeObject(Object obj, AbstractHessianOutput out)
    throws IOException
  {
    if (out.addRef(obj)) {
      return;
    }
    
    Class<?> cl = obj.getClass();

    int ref = out.writeObjectBegin(cl.getName());

    if (ref >= 0) {
      writeInstance(obj, out);
    }
    else if (ref == -1) {
      writeDefinition20(out);
      out.writeObjectBegin(cl.getName());
      writeInstance(obj, out);
    }
    else {
      writeObject10(obj, out);
    }
  }
 以上代码会调用 writeObject10(obj, out);
 protected void writeObject10(Object obj, AbstractHessianOutput out)
    throws IOException
  {
    for (int i = 0; i < _fields.length; i++) {
      Field field = _fields[i];

      out.writeString(field.getName());

      _fieldSerializers[i].serialize(out, obj);
    }

    out.writeMapEnd();
  }

 

2.反序列化的时候

当反序列化时,默认的反序列化类是 UnsafeSerializer

 会首先根据反序列化类型,创建一个map 

 

  protected HashMap<String,FieldDeserializer> getFieldMap(Class<?> cl)
  {
    HashMap<String,FieldDeserializer> fieldMap
      = new HashMap<String,FieldDeserializer>();

    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;

        // XXX: could parameterize the handler to only deal with public
        try {
          field.setAccessible(true);
        } catch (Throwable e) {
          e.printStackTrace();
        }

        Class<?> type = field.getType();
        FieldDeserializer deser;

        if (String.class.equals(type)) {
          deser = new StringFieldDeserializer(field);
        }
        else if (byte.class.equals(type)) {
          deser = new ByteFieldDeserializer(field);
        }
        。。。。。。

fieldMap.put(field.getName(), deser); } } return fieldMap; }

 如果是String类型的属性,使用的是StringFieldDeserializer   

  StringFieldDeserializer(Field field)
    {
      _field = field;
      _offset = _unsafe.objectFieldOffset(_field);  //这个会把属性对象对象的偏移量设置好
    }

 

接下来会对每个属性用map对应序列化方式进行反序列化和赋值

  public Object readMap(AbstractHessianInput in, Object obj)
    throws IOException
  {
    try {
      int ref = in.addRef(obj);

      while (! in.isEnd()) {
        Object key = in.readObject();

        FieldDeserializer deser = (FieldDeserializer) _fieldMap.get(key);

        if (deser != null)
          deser.deserialize(in, obj);
        else
          in.readObject();
      }

      in.readMapEnd();

      Object resolve = resolve(in, obj);

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

      return resolve;
    } catch (IOException e) {
      throw e;
    } catch (Exception e) {
      throw new IOExceptionWrapper(e);
    }
  }

这个是StringFieldDeserializer 反序列化,由于名字相同的属性,反序列化是第一个子类,往后父类的发现map中有就会忽略,所以在属性序列化的时候,先序列化子类的,接着是父类的,但是他们在对象中的偏移量是一样的(用的是同一个反序列化类),所以相同名字的属相,子类总是会被父类覆盖掉。

    @SuppressWarnings("restriction")
    void deserialize(AbstractHessianInput in, Object obj)
      throws IOException
    {
      String value = null;
      
      try {
        value = in.readString();

        _unsafe.putObject(obj, _offset, value);
      } catch (Exception e) {
        logDeserializeError(_field, obj, value, e);
      }
    }

 

五. 总结

    使用hessian序列化时,一定要注意子类和父类不能有同名字段

 

以上是关于Hessian序列化的一个潜在问题的主要内容,如果未能解决你的问题,请参考以下文章

HessianSpring整合Hessian

Hessian 2.0 序列化协议 - Hessian 2.0 Serialization Protocol 翻译

Hessian2序列化问题

Dubbo Hessian2序列化问题

优雅代码12-hessiankryojson序列化对比

hessian序列化