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 的实现, 也是通过 "==" 进行比较的, 进行引用的判断

相关推荐
木井巳8 分钟前
【记忆化搜索】最长递增子序列
java·算法·leetcode·深度优先·剪枝·推荐算法
杨杨杨大侠15 分钟前
知识库已经有了,Java 程序员还要做什么?Spring AI RAG 实战
java·openai·ai编程
伊信20 分钟前
Spring AI 基础学习与应用
java·后端
夜郎king28 分钟前
基于 Java + Playwright 实现网站自动访问与数据采集(以腾讯云开发者社区为例)
java·playwright·网页数据获取
Anastasiozzzz32 分钟前
重新定义 Agent 基建:Redis 在现代 AI 与智能体系统中的工程实践
java·人工智能·redis·ai
careathers35 分钟前
【数据结构】队列
java·数据结构
步行cgn39 分钟前
Spring 通过 factory-method 实例化 Bean 详解与常见错误排查
java·后端
lv__pf40 分钟前
seata【实战 msb】
java·开发语言·后端
cxoptics1 小时前
偏振片(起偏器/检偏器)怎么区分?
java
StevenLdh1 小时前
第四期 · 云端同步与冲突仲裁:离线可写、多端一致的工程解法
java·微信小程序