使用Hessian2的踩坑记录

3,221 阅读2分钟

项目使用的是蚂蚁的SOFARPC,序列化方式使用的是Hessian,有一次一个类继承父类,但是子类中也写了一个和父类一样的属性,导致取值时,该属性的值为空

事例代码

public static void main(String[] args) throws IOException {
        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());
    }


    static class User implements Serializable{
        private String username ;
        private String password;
        private Integer age;

        public String getUsername() {
            return username;
        }

        public void setUsername(String username) {
            this.username = username;
        }

        public String getPassword() {
            return password;
        }

        public void setPassword(String password) {
            this.password = password;
        }

        public Integer getAge() {
            return age;
        }

        public void setAge(Integer age) {
            this.age = age;
        }
    }

    static class UserInfo extends User{
        private String username;

        @Override
        public String getUsername() {
            return username;
        }

        @Override
        public void setUsername(String username) {
            this.username = username;
        }
    }

输出结果:

姓名:null
年龄:21

导致这种结果的原因

    1. hessian序列化的时候会取出对象的所有自定义属性,相同类型的属性是子类在前父类在后的顺序。
    1. hessian在反序列化的时候,是将对象所有属性取出来,存放在一个map中 key = 属性名 value是反序列类,相同名字的会以子类为准进行反序列化。
    1. 相同名字的属性 在反序列化的是时候,由于子类在父类前面,子类的属性总是会被父类的覆盖,由于java多态属性,在上述例子中父类 User.username = null

源码分析

1、序列化 当序列化对象是一个java自定对象时,默认的序列化类是 UnsafeSerializer,调用writeObject

public void writeObject(Object object)
        throws IOException {
        if (object == null) {
            writeNull();
            return;
        }

        Serializer serializer;

        serializer = _serializerFactory.getSerializer(object.getClass());

        serializer.writeObject(object, this);
    }

2、反序列化

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