【028】Dubbo3从0到1系列之序列化机制
文章目录
- 一、序列化概述
- 1.1 序列化的意义
- 1.2 序列化对于RPC框架的意义
- 二、Java序列化基础
- 2.1 核心目标
- 2.2 核心API与使用条件
- 2.2.1 关键类与接口
- 2.2.2 基本使用步骤
- 2.2.3 完整示例
一、序列化概述
1.1 序列化的意义
Java 序列化的核心意义在于解决对象状态的跨场景传递与持久化问题,具体体现在以下几个方面:
- 数据持久化:将内存中的对象状态转换为字节流,可写入文件、数据库等存储介质,实现对象状态的长期保存(如程序关闭后,下次启动可通过反序列化恢复对象)。
- 网络传输:在分布式系统中,对象需在不同进程、服务器间传递,序列化可将对象转为字节流通过网络传输,接收方再通过反序列化还原为原始对象,是远程通信(如 RPC、分布式计算)的基础。
- 对象复制:通过序列化 + 反序列化可实现对象的深拷贝(避免引用传递导致的状态关联问题),简化复杂对象的复制逻辑。
1.2 序列化对于RPC框架的意义
Java 序列化是 RPC(远程过程调用)框架实现跨进程通信的核心支撑技术之一,其意义主要体现在以下几个关键方面:
- 解决远程参数与返回值的传输问题
- RPC 的核心目标是让本地进程能像调用本地方法一样调用远程进程的方法。
- 远程调用中,方法的参数(可能是复杂对象)和返回值(也可能是对象)无法直接通过网络传输(网络只能传输字节流),序列化通过将对象转换为标准化的字节流,实现了对象的 “跨进程携带”;
- 反序列化则将字节流还原为远程进程可识别的对象,确保服务端能正确解析参数、客户端能正确处理结果,是远程调用 “数据传递” 的基础。
- 提供跨进程通信的协议基础RPC
- 框架需要定义客户端与服务端的通信协议(如数据格式、解析规则)。
- Java 序列化通过统一的对象编码规范(如类结构、字段值的字节流映射),为 RPC 协议提供了 “对象数据的编码 / 解码标准”,避免了因自定义格式导致的双方解析不一致问题,简化了协议设计。
- 支持复杂对象的远程交互
- 实际业务中,RPC 调用的参数或返回值往往是包含多层引用的复杂对象(如集合、自定义类嵌套)。
- Java 序列化能递归处理对象的引用关系(通过保存对象唯一标识避免重复序列化),确保复杂对象的完整传输,而无需开发者手动处理嵌套结构的编码逻辑。
- **保障版本兼容性(合理使用时)**RPC 框架中,客户端与服务端的类定义可能随业务迭代升级(如新增字段)。
- Java 序列化通过
serialVersionUID机制支持版本控制: - 只要双方类的
serialVersionUID一致,即使结构有小幅变更(如新增字段),仍可正确反序列化,减少了因类升级导致的 RPC 通信失败,提升了框架的兼容性。
- Java 序列化通过
二、Java序列化基础
Java 序列化是将对象的状态(即对象的成员变量值)转换为字节流的过程,而反序列化则是将字节流恢复为原始对象的过程。
这一机制打破了对象仅存在于内存中的限制,使得对象可以跨进程、跨网络传输,或持久化到存储介质中,是 Java 中实现数据持久化、远程通信(如 RPC)等场景的核心技术。
2.1 核心目标
序列化的核心目标是保存对象的 “状态”(而非对象本身的代码),并确保该状态可被准确还原。具体来说:
- 序列化:将对象的成员变量(非静态、非 transient)转换为字节序列。
- 反序列化:将字节序列重新解析为对象,恢复成员变量的值。
2.2 核心API与使用条件
2.2.1 关键类与接口
- java.io.Serializable接口:标记接口(无任何方法),用于告知 JVM “该类的对象可以被序列化”。若类未实现此接口,序列化时会抛出
NotSerializableException。 - ObjectOutputStream:负责将对象序列化为字节流,核心方法
writeObject(Object obj)。 - ObjectInputStream:负责将字节流反序列化为对象,核心方法
readObject()。
2.2.2 基本使用步骤
✅ 步骤一: 类实现**Serializable**接口
🎉 步骤二: 静态常量字段: serialVersionUID
🌻 步骤三: 根据需求决定是否要重写 writeObject()/readObject() 方法,实现自定义序列化。
🎀 步骤四: 调用 java.io.ObjectOutputStream 的 writeObject()/readObject() 进行序列化与反序列化
2.2.3 完整示例
基于jdk实现序列化与反序列化
package cn.tcmeta.dubbo;import lombok.AllArgsConstructor;
import lombok.Data;import java.io.IOException;
import java.io.Serial;
import java.io.Serializable;/*** @author: laoren* @description: 序列化示例* @version: 1.0.0*/
@Data
@AllArgsConstructor
public class User implements Serializable {@Serialprivate static final long serialVersionUID = 3307381424595923425L;private String username;private String address;private Integer age;// transient: 忽略字段, 在对象序列化过程中忽略被其修饰的成员属性变量// 静态字段(static): 静态成员属于类,而非对象,因此不参与序列化private transient String password;// 自定义序列化方法:对password加密后写入(JVM会优先调用此方法)@Serialprivate void writeObject(java.io.ObjectOutputStream out) throws IOException {out.defaultWriteObject(); // 序列化其它字段// 加密passwordvar encryptedPassword = new StringBuffer(password).reverse().toString();System.out.println("加密密码: " + encryptedPassword);out.writeObject(encryptedPassword);}// 自定义反序列化方法:对password解密后恢复(JVM会优先调用此方法)@Serialprivate void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {in.defaultReadObject(); // 反序列化其它字段// 解密passwordvar encryptedPassword = (String) in.readObject();password = new StringBuffer(encryptedPassword).reverse().toString();}
}
- 定义工具类
package cn.tcmeta.dubbo;import java.io.*;/*** 序列化工具类:封装序列化和反序列化方法*/
public class SerializationUtils {/*** 将对象序列化到指定文件** @param obj 待序列化的对象(需实现Serializable)* @param filePath 目标文件路径* @throws IOException 流操作异常*/public static void serialize(Object obj, String filePath) throws IOException {// 使用try-with-resources自动关闭流try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath))) {oos.writeObject(obj);System.out.println("对象已序列化到:" + filePath);}}/*** 从文件反序列化对象** @param filePath 序列化文件路径* @return 反序列化后的对象* @throws IOException 流操作异常* @throws ClassNotFoundException 类找不到异常(反序列化时需确保类存在)*/public static Object deserialize(String filePath) throws IOException, ClassNotFoundException {try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath))) {Object obj = ois.readObject();System.out.println("从" + filePath + "反序列化完成");return obj;}}
}
- 测试
package cn.tcmeta.dubbo;import java.io.IOException;/*** @author: laoren* @description: 测试* @version: 1.0.0*/
public class T {public static void main(String[] args) throws IOException, ClassNotFoundException {User user = new User("再见杰克", "beijing", 18, "123456");SerializationUtils.serialize(user, "data/user.usr");// 反序列化User user1 = (User) SerializationUtils.deserialize("data/user.usr");System.out.println(user1);}
}


