第四章
增加Kryo序列化器,上一章中使用JSON序列化器有一个问题,就是如果反序列化的是对象中有Object类型的属性,反序列化会出错,通常会把Object属性直接反序列化为String类型,因此还需要其他参数辅助序列化,并且JSON序列化器是基于字符串(JSON串)的,占用空间大并且速度慢。
Kryo是一个快速高效的Java对象序列化器,特点就是高性能、高效、易用。它是基于字节的序列化,对空间利用率高,在网络传输时可以减小体积,并且在序列化时记录属性对象的类型信息,因此在反序列化时不会出现类型错误的情况。
引入依赖
需要引入Kryo依赖
<dependency>
<groupId>com.esotericsoftware</groupId>
<artifactId>kryo</artifactId>
<version>4.0.2</version>
</dependency>
这里把Kryo作为默认的序列化器来使用,这里使用过switch的方式根据用户传入的编号返回对应的序列化器,这样其实不太友好,如果序列化器多的话,用户还要记忆每个序列化器对应的编号,后续可能会优化序列化器的使用方式。
public interface CommonSerializer {
byte[] serializer(Object object);
Object deserialize(byte[] bytes, Class<?> clazz) throws Exception;
int getCode();
static CommonSerializer getByte(int code) {
switch (code) {
case 0:
return new KryoSerializer();
case 1:
return new JsonSerializer();
default:
return null;
}
}
}
KryoSerializer
接下来就是序列化器的实现了
public class KryoSerializer implements CommonSerializer {
private static final Logger logger = LoggerFactory.getLogger(KryoSerializer.class);
private static final ThreadLocal<Kryo> kryoThreadLocal = ThreadLocal.withInitial(() -> {
Kryo kryo = new Kryo();
kryo.register(RpcResponse.class);
kryo.register(RpcRequest.class);
kryo.setReferences(true);
kryo.setRegistrationRequired(false);
return kryo;
});
@Override
public byte[] serializer(Object object) {
try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
Output output = new Output(byteArrayOutputStream)) {
Kryo kryo = kryoThreadLocal.get();
kryo.writeObject(output, object);
kryoThreadLocal.remove();
return output.toBytes();
} catch (IOException e) {
logger.error("序列化时有错误发生:", e);
throw new SerializeException("序列化时有错误发生");
}
}
@Override
public Object deserialize(byte[] bytes, Class<?> clazz) throws Exception {
try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
Input input = new Input(byteArrayInputStream)) {
Kryo kryo = kryoThreadLocal.get();
Object o = kryo.readObject(input, clazz);
kryoThreadLocal.remove();
return o;
}
}
@Override
public int getCode() {
return SerializerCode.valueOf("KRYO").getCode();
}
}
根据官网的教程,因为kryo不是线程安全的,因此使用的时候可以用ThreadLocal或者池化技术来创建Kryo,序列化时,先创建一个OutPut对象(是Kryo中的概念),然后使用writeObject方法吧object写入到output中,在调用output的getByte方法就可以获得到对应的字节数组。而反序列化就是从input对象中读调用readObject方法,只需要传入对象的类型,不需要传入每一个属性的类型信息。
|