前言:
之前使用protobuf工具来解析表格数据和定制网络协议,但是为了网络安全和压缩数据大小,有时候需要对数据进行序列化,这就需要设计一个序列化工具类来完成序列化和反序列化的操作。
框架的对比:
Java中几个常用的序列化框架对比,包括:kryo 、Hessian 、Protostuff 、Protostuff-Runtime 和java.io :
框架 |
优点 |
缺点 |
kryo |
速度快,序列化后体积小 |
跨语言支持比较复杂 |
Hessian |
默认支持跨语言 |
速度比较慢 |
java.io |
JDK自带功能,使用方便,可序列化所有类 |
速度慢,占空间大 |
Protostuff |
速度快,基于protobuf |
需要静态编译 |
Protostuff-Runtime |
无需静态编译,但序列化之前需要预先传入Schema |
不支持无默认构造函数的类,反序列化时需要用户自己初始化序列化后的对象,而此工具只负责对初始化后的对象进行赋值 |
1.详细分析:
protobuf 的一个缺点是需要数据结构的预编译过程,首先要编写 .proto 格式的配置文件,再通过 protobuf 提供的工具生成各种语言响应的代码。由于java具有反射和动态代码生成的能力,这个预编译过程不是必须的,可以在代码执行时来实现。有个 protostuff插件 已经实现了这个功能。
protostuff 基于Google protobuf,但是提供了更多的功能和更简易的用法。其中,protostuff-runtime 实现了无需预编译对Java bean进行protobuf序列化/反序列化的能力。protostuff-runtime 的局限是序列化前需预先传入schema ,反序列化不负责对象的创建只负责复制,因而必须提供默认构造函数。此外,protostuff 还可以按照protobuf的配置序列化成json/yaml/xml等格式。
2.坑点解决:
没经过修改过的 protostuff ,性能方法还是有些缺陷,这里我在一篇关于 轻量级分布式 RPC 框架 博客中找到了据说是当前性能最优的 优化版Protostuff 的使用案例。
在原生的 Protostuff 中通过反射实例化java类的时候,是通过使用 Class.newInstance() 方法来实现的,而前提就是这个类java类必须提供默认构造函数。
假如希望在java类不提供默认构造函数的时候也能实现反射实例化,可以选择使用 objenesis 来实例化java类,使用方式可以参考 objenesis官网。
3.框架选择:
综合上述的分析,最终我还是选择了 Protostuff 框架来完成Protobuf数据的序列化,关于不支持无默认构造函数类序列化的缺陷接下来通过使用 objenesis 也会得到解决。
自定义序列化工具类
这里我们创建此工具类,取名为 SerializationUtil ,使用Protostuff 来序列化和反序列化Protobuf数据:
1.库引入:
首先要在pom.xml里添加com.dyuproject.protostuff 和objenesis 的jar包:
<dependency>
<groupId>com.dyuproject.protostuff</groupId>
<artifactId>protostuff-core</artifactId>
<version>1.0.8</version>
</dependency>
<dependency>
<groupId>com.dyuproject.protostuff</groupId>
<artifactId>protostuff-runtime</artifactId>
<version>1.0.8</version>
</dependency>
<!-- Objenesis -->
<dependency>
<groupId>org.objenesis</groupId>
<artifactId>objenesis</artifactId>
<version>2.1</version>
</dependency>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
2.工具类编写:
主要包含两个核心的函数:序列化函数 Serializer 和反序列化函数 Deserializer :
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.objenesis.Objenesis;
import org.objenesis.ObjenesisStd;
import com.dyuproject.protostuff.LinkedBuffer;
import com.dyuproject.protostuff.ProtostuffIOUtil;
import com.dyuproject.protostuff.Schema;
import com.dyuproject.protostuff.runtime.RuntimeSchema;
public class SerializationUtil {
private static Map<Class<?>, Schema<?>> cachedSchema = new ConcurrentHashMap<>();
private static Objenesis objenesis = new ObjenesisStd(true);
private SerializationUtil() {
}
@SuppressWarnings("unchecked")
private static <T> Schema<T> getSchema(Class<T> cls) {
Schema<T> schema = (Schema<T>) cachedSchema.get(cls);
if (schema == null) {
schema = RuntimeSchema.createFrom(cls);
if (schema != null) {
cachedSchema.put(cls, schema);
}
}
return schema;
}
@SuppressWarnings("unchecked")
public static <T> byte[] serialize(T obj) {
Class<T> cls = (Class<T>) obj.getClass();
LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
try {
Schema<T> schema = getSchema(cls);
return ProtostuffIOUtil.toByteArray(obj, schema, buffer);
} catch (Exception e) {
throw new IllegalStateException(e.getMessage(), e);
} finally {
buffer.clear();
}
}
public static <T> T deserialize(byte[] data, Class<T> cls) {
try {
T message = (T) objenesis.newInstance(cls);
Schema<T> schema = getSchema(cls);
ProtostuffIOUtil.mergeFrom(data, message, schema);
return message;
} catch (Exception e) {
throw new IllegalStateException(e.getMessage(), e);
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
上面是引入 objenesis 之后的版本,优点就是支持没有提供默认构造函数的java类的实例化,可以看到这里通过 Class<T> cls = (Class<T>) obj.getClass(); 来实例化java类的。ConcurrentHashMap 是适用于高并发的Map数据结构。
3.工具类调用:
随便定义一个Protobuf的协议类,假设为User ,下面就是具体的序列化工具使用操作:
序列化:
byte[] data = SerializationUtil.serialize(user,User.class);
反序列化:
User user2 = SerializationUtil.deserialize(data,User.class);
参考资料:
|