bug说明


子类和父类中有同名的字段时,hessian反序列化会丢失字段值。 父类Person

public class Person implements Serializable {
    private static final long serialVersionUID = 1L;
    private String name;
    private int age;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("Person{");
        sb.append("name='").append(name).append('\'');
        sb.append(", age=").append(age);
        sb.append('}');
        return sb.toString();
    }
}

子类PersonEx

public class PersonEx  extends  Person{
    private static final long	serialVersionUID	= 1L;
    private String	name;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("PersonEx{");
        sb.append("name='").append(name).append('\'');
        sb.append('}');
        sb.append(super.toString());
        return sb.toString();
    }
}

当我们序列化如下对象后

  PersonEx p = new PersonEx();
  p.setName("XXX");
  p.setAge(28);

后,反序列化回来的PersonEx对象getName()==null.

原因分析


造成这个问题的原因如下: hessian序列化机制,他通过反射把所有字段名和值(整个传输对象图)读取出来,见JavaSerializer#writeObject:

   	if (ref == -1) {
	//写入需要反射的字段名
      writeDefinition20(out);
      out.writeObjectBegin(cl.getName());
   	}
	//写入值
	writeInstance(obj, out);

按照此规则,现在序列化结构如下(忽略其他映射关系):

name|age|name|Person|XXX|28|null

反序列化时,读取字段名,在set字段值:

  1. 读取到name字段,调用setName("XXX")
  2. 再次读取name字段,调用setName(null)

结果就囧了。。。。

解决办法


1.修改hessian代码

修改了hessian的序列化机制,当类的父对象出现相同的字段名时,跳过对此字段的处理。

 //添加成员变量propertiesNameSet	 
   	 private HashSet<String> propertiesNameSet = Sets.newHashSet();
 //在JavaSerializer构造器里,遍历字段时,添加检查
 if (propertiesNameSet.contains(field.getName())) {
       continue;
 } else {
      propertiesNameSet.add(field.getName());
 }

hessian在反序列化时有同名字段的判断com.caucho.hessian.io.JavaDeserializer#getFieldMap保证fieldMap中不出现同名的字段。

2.修改同名字段问题

出现了同名字段,也可以认为是我们代码设计出问题了,没有理解清楚继承结构。在子类中删除同名的字段,就避免这个bug。

小结

现在的序列化框架基本上都会遇到此问题,Kryo中的com.esotericsoftware.kryo.serializers.CompatibleFieldSerializer也没有对此问题进行处理.作为框架开发者,要尽量减少框架使用者犯错误的机会,也就是所谓的防御性编程吧.

Kryo pull request ,防止因为子类和父类中有同名field name而导致的问题.

2016年05月Reading Notes

## 服务发现服务发现用于动态感知服务提供方地址,并提供服务路由分发策略能力。### 客户端发现客户端从注册中心获取服务列表,客户端监听服务列表的变化,客户端通过路由策略选择合适的服务端地址。服务端在停服务时,需要先通知客户端不要发送新请求过来,等服务端把当前请求处理完后,...… Continue reading

2016年05月Reading Notes

Published on June 19, 2016

2016年05月Reading Notes

Published on May 11, 2016