122 Hession,FastJson,ObjectInputStream的序列化反序列化相同引用的处理

前言

这是最近碰到的一个问题

dubbo 客户端这边和服务器交互了一个参数 类型A

类型A 引用了类型 B 的字段 field1, field2

然后 dubbo客户端 在发起 dubbo 调用之前, 具体的参数 类型A, 设置 field1, field2 是同一个 类型B 实例

然后 dubbo 服务器 在收到请求之后 反序列化 类型A实例, 拿到的 field1, field2 也是统一个对象

然后 这时候存在 具体的业务, 需要 分别修改 field1, field2 的其他字段, 这之后更新就是 apply 到同一个 类型B实例 了, 然后 导致了 业务问题

dubbo 默认使用的是 hession 序列化, 反序列化, 这里 来探讨一下 这个的问题

Hession 的序列化/反序列化同一个实例实现

实体设计如下, People 实体

复制代码
public class People extends SuperObject implements Serializable {

    private String name;

    private Course course1;

    private Course course2;

    public People() {
    }

    public People(String name, Course course1, Course course2) {
        this.name = name;
        this.course1 = course1;
        this.course2 = course2;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Course getCourse1() {
        return course1;
    }

    public void setCourse1(Course course1) {
        this.course1 = course1;
    }

    public Course getCourse2() {
        return course2;
    }

    public void setCourse2(Course course2) {
        this.course2 = course2;
    }
}

Course 实体

复制代码
public class Course extends SuperObject implements Serializable {

    private String name;

    private String desc;

    public Course() {
    }

    public Course(String name, String desc) {
        this.name = name;
        this.desc = desc;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }
}

SuperObject 实体

复制代码
public class SuperObject {

    public SuperObject() {
        int x = 0;
    }

}

整体 hession 序列化 反序列化 测试用例如下

复制代码
public class Test08HessionSerialize {

    // Test08HessionSerialize
    public static void main(String[] args) throws Exception {

        Course chinese = new Course("chinese", "required");
        People people = new People("xiaoming", chinese, chinese);

        // os
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        Hessian2ObjectOutput os = new Hessian2ObjectOutput(baos);
        os.writeObject(people);
        os.flushBuffer();

        // is
        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        Hessian2ObjectInput is = new Hessian2ObjectInput(bais);
        Object deserializedPeople = is.readObject();
        int x = 0;

    }

}

反序列化的时候 readRef 的实现

我们重点关注一下 设置 People 实例的 course1, course2 字段的部分

如下是先设置 course2 字段

如下是先设置 course1 字段, 可以看到这里的 course1 的实例 和上面 course2 的示例是同一个示例

看一下 course1 字段的实例的读取的地方, 就是定义了一个 refs 类型, 然后 从已有的 refs 中获取对象, 因此 course1, course2 拿到的对象实例 是同一个

序列化的时候 writeRef 的实现

这里写 course1 的时候, 上下文判断是一个上下文已有的实例, write ref

判断标准就是 refs 中是否存在, refs 中存放的是已有的对象的实例映射

对于已有的实例, 直接写出 BC_REF 标记 和对应的索引

具体判断两个对象是否相同, 是根据 "==" 进行引用的判断

ObjectInput/OutputStream 的序列化/反序列化同一个实例实现

测试代码如下

复制代码
public class Test08ObjectStreamSerialize {

    // Test08HessionSerialize
    public static void main(String[] args) throws Exception {

        Course chinese = new Course("chinese", "required");
        People people = new People("xiaoming", chinese, chinese);

        // os
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream os = new ObjectOutputStream(baos);
        os.writeObject(people);
        os.close();

        // is
        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        ObjectInputStream is = new ObjectInputStream(bais);
        Object deserializedPeople = is.readObject();
        int x = 0;

    }

}

反序列化的时候 readRef 的实现

同样也是通过 ref标记 + 索引进行实现

具体的读取方式如下, TC_REFERENCE + refIndex 获取到目标对象, 然后相应个上级

拿到时候, objValues 就是 People 的三个字段, 可以看到 course1, course2 是相同的实例

序列化的时候 writeRef 的实现

写 People 中的 Course 的地方, 从 handles 中 lookup 如果找到, 表示在已有的实例列表中

lookup 的实现, 也是通过 "==" 进行比较的, 进行引用的判断

具体的写 ref 的地方, 写出 TC_REFERENCE + refIndex

FastJson 的序列化/反序列化同一个实例实现

测试代码如下

复制代码
public class Test08FastJsonSerialize {

    // Test08HessionSerialize
    public static void main(String[] args) throws Exception {

        Course chinese = new Course("chinese", "required");
        People people = new People("xiaoming", chinese, chinese);

        // os
        String serializedStr = JSON.toJSONString(people);

        // is
        Object deserializedPeople = JSON.parseObject(serializedStr, People.class);
        int x = 0;

    }

}

这块的代码, 就不太好跟了, 是一些 运行时的 class 进行的处理, 创建对象, 设置属性 等等

这是第一个 Course 实例创建的地方

这是第二个 Course 实例创建的地方

这是最终反序列化之后的对象, 可以看到 第二个 Course 对象貌似是被抛弃了

反序列化的时候 readRef 的实现

dump 一下 People 的反序列化的类的 class

反序列化 course1, course2 的地方如下

设置 course1, course2 的地方如下, 可以看到 我们核心关注的流程是在 course1/2_asm_deser.deserialize 中

fastjson 这边 读取 ref 的地方在这里, ref 为 $.course1, 是一个 jsonpath

然后 从中解析出该对象为 course1

这部分的实现的支持 就是 fastjson 自己支持的 ref 的相关处理了, 因为 JSON.toJSONString 的结果中 course2 为 ref

JSON.toJSONString(people) 的结果为

序列化的时候 writeRef 的实现

从 references 中获取目标 引用对象

get 的实现, 也是通过 "==" 进行比较的, 进行引用的判断

相关推荐
酷柚易汛2 小时前
酷柚易汛ERP 2025-12-26系统升级日志
java·前端·数据库·php
侠客行03172 小时前
Mybatis入门到精通 一
java·mybatis·源码阅读
消失的旧时光-19433 小时前
微服务的本质,其实是操作系统设计思想
java·大数据·微服务
Coder_Boy_3 小时前
基于SpringAI的智能平台基座开发-(四)
java·人工智能·spring boot·langchain·springai
码界奇点3 小时前
基于Spring Boot的内容管理系统框架设计与实现
java·spring boot·后端·车载系统·毕业设计·源代码管理
墨雪不会编程4 小时前
C++【string篇1遍历方式】:从零开始到熟悉使用string类
java·开发语言·c++
蒂法就是我4 小时前
有一张表,只有一个字段没有插入主建,能插入成功吗? 隐藏的 rowid除了在这里用到还在哪里用到了?
java
a努力。4 小时前
字节Java面试被问:系统限流的实现方式
java·开发语言·后端·面试·职场和发展·golang
独自破碎E4 小时前
Java中的Exception和Error有什么区别?
java·开发语言