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

完

相关推荐
悟天特斯9 小时前
安防一体化:从“各管各的“到“全局联动“
java·大数据·网络·人工智能·物联网
70后写java的老码农9 小时前
Spring Boot 3.x + ShardingSphere 5.x + MyBatis-Plus 3.x:订单分库分表实战(含两个踩坑记录)
java
霸道流氓气质9 小时前
AI 翻译插件:翻译记忆级联复用、术语混合检索与结构化质量门禁的 Vue3 + Spring AI 生产级实战
java·人工智能·spring
斯维赤9 小时前
OCR到底是接入第三方API 还是自己封装?
java·ocr
码智社10 小时前
Redis 哨兵模式详解:从原理到生产实践
java·redis
救救孩子把10 小时前
结构化输出为何“格式正确、答案错误”?JSON Schema 的能力边界
android·java·json·agent·agent服务
silvamux_10 小时前
微头条爆款体平台适配实测:把交付要求变成可计算的指标
java·线性代数·算法·决策树·矩阵
论文复现现场10 小时前
DeepSeek V4.1 Flash 和 V4 Pro 有什么区别?API 价格、性能与迁移建议
java·服务器·人工智能·python·大模型api·deepseek·deepseek v4.1
郭小李11 小时前
转发与重定向
java·服务器·数据库
你看我名字11 小时前
数眼智能 Search + Reader 实战:联网问答怎样避免“搜到了,却引用错了”
android·java·开发语言