本文作者:程序员飞云
1. 自定义序列化、反序列化接口
import java.io.IOException;
/** * 序列化器接口 */public interface Serializer {
/** * 序列化 * * @param object * @param <T> * @return * @throws IOException */ <T> byte[] serialize(T object) throws IOException;
/** * 反序列化 * * @param bytes * @param type * @param <T> * @return * @throws IOException */ <T> T deserialize(byte[] bytes, Class<T> type) throws IOException;}2. JDK序列化、反序列化实现
ObjectOutputStream、ObjectInputStream
import java.io.*;
/** * JDK 序列化器 */public class JdkSerializer implements Serializer {
/** * 序列化 * * @param object * @param <T> * @return * @throws IOException */ @Override public <T> byte[] serialize(T object) throws IOException { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream); objectOutputStream.writeObject(object); objectOutputStream.close(); return outputStream.toByteArray(); }
/** * 反序列化 * * @param bytes * @param type * @param <T> * @return * @throws IOException */ @Override public <T> T deserialize(byte[] bytes, Class<T> type) throws IOException { ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); ObjectInputStream objectInputStream = new ObjectInputStream(inputStream); try { return (T) objectInputStream.readObject(); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } finally { objectInputStream.close(); } }}3. JSON序列化
优点:
- 易读性好、可读性强,便于调试
- 跨语言支持广泛
缺点:
1. 序列化后的数据量比较大,JSON采用文本格式存储数据,需要额外的键、值、数据结构1. 不能处理复杂的数据结构。1. 需要考虑对象转换类型兼容。比如Object数组序列化会丢失类型问题。1. 引入jackson依赖解析json数据
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.18.1</version> </dependency>2. 编写序列化配置
public class JsonSerializer implements Serializer { private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
@Override public <T> byte[] serialize(T obj) throws IOException { return OBJECT_MAPPER.writeValueAsBytes(obj); }
@Override public <T> T deserialize(byte[] bytes, Class<T> classType) throws IOException { T obj = OBJECT_MAPPER.readValue(bytes, classType); return obj; }}3. 处理Object数组
目前有两个实体RpcRequest、RpcResponse,里面包含了Object数组
package com.fly.model;
import lombok.AllArgsConstructor;import lombok.Builder;import lombok.Data;import lombok.NoArgsConstructor;
import java.io.Serializable;
/** * rpc请求 */@Data@AllArgsConstructor@NoArgsConstructor@Builderpublic class RpcRequest implements Serializable {
/** * 服务名称 */ private String serviceName;
/** * 服务方法 */ private String methodName;
/** * 参数列表 */ private Object[] args;
/** * 参数类型 */ private Class<?>[] parameterTypes;}package com.fly.model;
import lombok.AllArgsConstructor;import lombok.Builder;import lombok.Data;import lombok.NoArgsConstructor;
import java.io.Serializable;
/** * RPC 响应 */@Data@Builder@AllArgsConstructor@NoArgsConstructorpublic class RpcResponse implements Serializable {
/** * 响应数据 */ private Object data;
/** * 响应数据类型(预留) */ private Class<?> dataType;
/** * 响应信息 */ private String message;
/** * 异常信息 */ private Exception exception;
}/** * Json 序列化器 */public class JsonSerializer implements Serializer { private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
@Override public <T> byte[] serialize(T obj) throws IOException { return OBJECT_MAPPER.writeValueAsBytes(obj); }
@Override public <T> T deserialize(byte[] bytes, Class<T> classType) throws IOException { T obj = OBJECT_MAPPER.readValue(bytes, classType); if (obj instanceof RpcRequest) { return handleRequest((RpcRequest) obj, classType); } if (obj instanceof RpcResponse) { return handleResponse((RpcResponse) obj, classType); } return obj; }
/** * 由于 Object 的原始对象会被擦除,导致反序列化时会被作为 LinkedHashMap 无法转换成原始对象,因此这里做了特殊处理 * * @param rpcRequest rpc 请求 * @param type 类型 * @return {@link T} * @throws IOException IO异常 */ private <T> T handleRequest(RpcRequest rpcRequest, Class<T> type) throws IOException { Class<?>[] parameterTypes = rpcRequest.getParameterTypes(); Object[] args = rpcRequest.getArgs();
// 循环处理每个参数的类型 for (int i = 0; i < parameterTypes.length; i++) { Class<?> clazz = parameterTypes[i]; // 如果类型不同,则重新处理一下类型 if (!clazz.isAssignableFrom(args[i].getClass())) { byte[] argBytes = OBJECT_MAPPER.writeValueAsBytes(args[i]); args[i] = OBJECT_MAPPER.readValue(argBytes, clazz); } } return type.cast(rpcRequest); }
/** * 由于 Object 的原始对象会被擦除,导致反序列化时会被作为 LinkedHashMap 无法转换成原始对象,因此这里做了特殊处理 * * @param rpcResponse rpc 响应 * @param type 类型 * @return {@link T} * @throws IOException IO异常 */ private <T> T handleResponse(RpcResponse rpcResponse, Class<T> type) throws IOException { // 处理响应数据 byte[] dataBytes = OBJECT_MAPPER.writeValueAsBytes(rpcResponse.getData()); rpcResponse.setData(OBJECT_MAPPER.readValue(dataBytes, rpcResponse.getDataType())); return type.cast(rpcResponse); }}4. Hessian序列化
官网:https://hessian.caucho.com/

优点:
- 自描述序列化类型。不依赖外部描述文件或者接口定义,用一个字节表示常用的基础类型,极大缩短二进制流。
- 语言无关,支持脚本语言
- 协议简单,比Java原生序列化高效
- 相比hessian1,hessian2中增加了压缩编码,其序列化二进制流大小是Java序列化的50%,序列化耗时是Java序列化的30%,反序列化耗时是Java序列化的20%。
缺点:
- 性能相对于JSON略低,需要将对象转换为二进制格式
- 必须要实现Serializable接口,限制了可序列化的对象范围
Dubbo默认采用的Hessian2,基于Hessian改进
1. 引入依赖
<!-- 序列化 --><!-- https://mvnrepository.com/artifact/com.caucho/hessian --><dependency> <groupId>com.caucho</groupId> <artifactId>hessian</artifactId> <version>4.0.66</version></dependency>2. Hessian序列化
package com.fly.serializer;
import com.caucho.hessian.io.HessianInput;import com.caucho.hessian.io.HessianOutput;
import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.IOException;
/** * Hessian 序列化器 */public class HessianSerializer implements Serializer { @Override public <T> byte[] serialize(T object) throws IOException { ByteArrayOutputStream bos = new ByteArrayOutputStream(); HessianOutput ho = new HessianOutput(bos); ho.writeObject(object); return bos.toByteArray(); }
@Override public <T> T deserialize(byte[] bytes, Class<T> tClass) throws IOException { ByteArrayInputStream bis = new ByteArrayInputStream(bytes); HessianInput hi = new HessianInput(bis); return (T) hi.readObject(tClass); }}Kryo序列化
优点:
- 高性能,序列化和反序列化速度快。
- 支持循环引用和自定义序列化器,适用于复杂的对象结构。
- 无需实现 Serializable接口,可以序列化任意对象。
缺点:
- 只支持java
- 序列化格式不易调试和理解
1. 引入依赖
<!-- https://mvnrepository.com/artifact/com.esotericsoftware/kryo --><dependency> <groupId>com.esotericsoftware</groupId> <artifactId>kryo</artifactId> <version>5.6.0</version></dependency>2. Kryo序列化
Kryo本身是线程不安全的,需要使用ThreadLocal保证每个线程单独的Kryo对象实例。
package com.fly.serializer;
import com.esotericsoftware.kryo.Kryo;import com.esotericsoftware.kryo.io.Input;import com.esotericsoftware.kryo.io.Output;
import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;
/** * Kryo 序列化器 */public class KryoSerializer implements Serializer { /** * kryo 线程不安全,使用 ThreadLocal 保证每个线程只有一个 Kryo */ private static final ThreadLocal<Kryo> KRYO_THREAD_LOCAL = ThreadLocal.withInitial(() -> { Kryo kryo = new Kryo(); // 设置动态动态序列化和反序列化类,不提前注册所有类(可能有安全问题) kryo.setRegistrationRequired(false); return kryo; });
@Override public <T> byte[] serialize(T obj) { ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); Output output = new Output(byteArrayOutputStream); KRYO_THREAD_LOCAL.get().writeObject(output, obj); output.close(); return byteArrayOutputStream.toByteArray(); }
@Override public <T> T deserialize(byte[] bytes, Class<T> classType) { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes); Input input = new Input(byteArrayInputStream); T result = KRYO_THREAD_LOCAL.get().readObject(input, classType); input.close(); return result; }}5. Protobuf
优点:
· 高效的二进制序列化,序列化后的数据量极小。 · 跨语言支持,并且提供了多种语言的实现库。 · 支持版本化和向前/向后兼容性。
缺点:
· 配置相对复杂,需要先定义数据结构的消息格式。 · 对象的序列化格式不易读懂,不便于调试。
评论