一. 最近的用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序列化的一个潜在问题的更多相关文章

  1. Java序列化与Hessian序列化的区别

    Java序列化: Java序列化会把要序列化的对象类的元数据和业务数据全部序列化为字节流,而且是把整个继承关系上的东西全部序列化了.它序列化出来的字节流是对那个对象结构到内容的完全描述,包含所有的信息 ...

  2. Hessian 序列化和反序列化实现

    先聊聊 Java的序列化,Java官方的序列化和反序列化的实现被太多人吐槽,这得归于Java官方序列化实现的方式. 1.Java序列化的性能经常被吐槽.2.Java官方的序列化后的数据相对于一些优秀的 ...

  3. 关于C# XML序列化的一个BUG的修改

    关于C# XML序列化的一个BUG的修改 在我前一篇博客中提到用XML序列化作为数据库的一个方案,@拿笔小心 提到他们在用XML序列化时,遇到了一个比较严重的bug,即XML不闭合,系统不能正确的加载 ...

  4. java中的序列化与反序列化,还包括将多个对象序列化到一个文件中

    package Serialize; /** * Created by hu on 2015/11/7. */ //实现序列化必须实现的接口,这就是一个空接口,起到标识的作用 import java. ...

  5. DjangoRestFramework的外键反向关系序列化的一个问题

    先用文档中的样例: Models定义: class Album(models.Model): album_name = models.CharField(max_length=100) artist ...

  6. Hessian序列化

    当子类定义了和父类同名的属性时,经过hessian传输,会导致该属性值丢失.因为hessian发送二进制数据时,子类数据在前,父类数据在后.接收二进制数据时,子类数据在前,父类数据在后.所以对于同名字 ...

  7. Xml,Json,Hessian,Protocol Buffers序列化对比

    简介 这篇博客主要对Xml,Json,Hessian,Protocol Buffers的序列化和反序列化性能进行对比,Xml和Json的基本概念就不说了. Hessian:Hessian是一个轻量级的 ...

  8. Dubbo序列化多个CopyOnWriteArrayList对象变成同一对象的一个大坑!!

    环境: win10 + jdk 1.8 + dubbo 2.5.10 问题描述: 当一个对象(此对象内包含多个CopyOnWriteArrayList对象) 作为参数调用RPC接口后, 服务提供者拿到 ...

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

    Hessian是一种轻量.快速的web协议,在微服务场景下经常被使用. Hessian协议实际上包含两种含义: 1. Web网络通信远程调用服务,具体可以参考:http://hessian.cauch ...

随机推荐

  1. 去除桌面SVN问号

    由于误操作,桌面文件全部带着问号. 找到一种比较方便的方法解决. 1.显示受保护文件.文件夹win7系统 - 随便打开一个文件夹 -  在菜单栏里点 工具 - 文件夹选项 - 查看 以下如图中选择 2 ...

  2. c#4.8-4.11学习总结

    4.8讲的是static 关键字.它用于修饰类 ,字段 ,属性,方法和构造方法等.被它修饰的类称为静态类,成员称为静态成员.  先说静态字段,它是普通字段前面加个static,它不属于任何对象,只属于 ...

  3. java详细剖析

    1·类型加载主动初始化和被动初始化两种,通过访问静态变量或者给静态变量赋值都是可以使类初始化,如果有继承关系,所依赖的父类都会被动初始化. 2·如果在类的静态变量中添加final关键字,那这个变量就会 ...

  4. tcprstat测试服务器请求响应时间

    tcprstat使用,tcprstat必须使用root权限才能使用 tcprstat -p 3306 -t 1 -n 5 -l 192.168.52.250 centos统计分析所有的IP地址

  5. Oracle数据泵远程导入数据

    查看现存镜像目录 select * from dba_directories; 创建镜像目录 create or replace directory my_dir as 'local_dir' ; 把 ...

  6. SpringCloud第六步:搭建分布式配置中心

    什么是配置中心 在分布式系统中,由于服务数量巨多,为了方便服务配置文件统一管理,实时更新,所以需要分布式配置中心组件.在Spring Cloud中,有分布式配置中心组件spring cloud con ...

  7. pymongo基础

    PyMongo是MongoDB数据库的python模块 MongoDB是由C++语音编写的非关系型数据库,是一个基于分布式文件存储的开源数据库系统. win10 安装 4.0 使用官网的配置 使用 n ...

  8. xcode10对应的xcode command line tool编译的坑

    众所周知,xcode10新增的编译系统new build system会不支持一些老项目的编译,一般的做法是在Xcode编译配置Xcode->File->Project Settings/ ...

  9. python基础之 正则表达式,re模块

    1.正则表达式 正则表达式:是字符串的规则,只是检测字符串是否符合条件的规则而已 1.检测某一段字符串是否符合规则 2.将符合规则的匹配出来re模块:是用来操作正则表达式的 2.正则表达式组成 字符组 ...

  10. !!在js中的用法

    var obj = ""; //undefinedconsole.log(!!obj);  //false(强制转换成Boolean类型)