前言:
之前使用protobuf工具來(lái)解析表格數(shù)據(jù)和定制網(wǎng)絡(luò)協(xié)議,但是為了網(wǎng)絡(luò)安全和壓縮數(shù)據(jù)大小,有時(shí)候需要對(duì)數(shù)據(jù)進(jìn)行序列化,這就需要設(shè)計(jì)一個(gè)序列化工具類(lèi)來(lái)完成序列化和反序列化的操作。
框架的對(duì)比:
Java中幾個(gè)常用的序列化框架對(duì)比,包括:kryo、Hessian、Protostuff、Protostuff-Runtime和java.io:
| 框架 |
優(yōu)點(diǎn) |
缺點(diǎn) |
| kryo |
速度快,序列化后體積小 |
跨語(yǔ)言支持比較復(fù)雜 |
| Hessian |
默認(rèn)支持跨語(yǔ)言 |
速度比較慢 |
| java.io |
JDK自帶功能,使用方便,可序列化所有類(lèi) |
速度慢,占空間大 |
| Protostuff |
速度快,基于protobuf |
需要靜態(tài)編譯 |
| Protostuff-Runtime |
無(wú)需靜態(tài)編譯,但序列化之前需要預(yù)先傳入Schema |
不支持無(wú)默認(rèn)構(gòu)造函數(shù)的類(lèi),反序列化時(shí)需要用戶(hù)自己初始化序列化后的對(duì)象,而此工具只負(fù)責(zé)對(duì)初始化后的對(duì)象進(jìn)行賦值 |
1.詳細(xì)分析:
protobuf 的一個(gè)缺點(diǎn)是需要數(shù)據(jù)結(jié)構(gòu)的預(yù)編譯過(guò)程,首先要編寫(xiě) .proto 格式的配置文件,再通過(guò) protobuf 提供的工具生成各種語(yǔ)言響應(yīng)的代碼。由于java具有反射和動(dòng)態(tài)代碼生成的能力,這個(gè)預(yù)編譯過(guò)程不是必須的,可以在代碼執(zhí)行時(shí)來(lái)實(shí)現(xiàn)。有個(gè) protostuff插件 已經(jīng)實(shí)現(xiàn)了這個(gè)功能。
protostuff 基于Google protobuf,但是提供了更多的功能和更簡(jiǎn)易的用法。其中,protostuff-runtime 實(shí)現(xiàn)了無(wú)需預(yù)編譯對(duì)Java bean進(jìn)行protobuf序列化/反序列化的能力。protostuff-runtime的局限是序列化前需預(yù)先傳入schema,反序列化不負(fù)責(zé)對(duì)象的創(chuàng)建只負(fù)責(zé)復(fù)制,因而必須提供默認(rèn)構(gòu)造函數(shù)。此外,protostuff 還可以按照protobuf的配置序列化成json/yaml/xml等格式。
2.坑點(diǎn)解決:
沒(méi)經(jīng)過(guò)修改過(guò)的 protostuff,性能方法還是有些缺陷,這里我在一篇關(guān)于 輕量級(jí)分布式 RPC 框架 博客中找到了據(jù)說(shuō)是當(dāng)前性能最優(yōu)的 優(yōu)化版Protostuff 的使用案例。
在原生的 Protostuff 中通過(guò)反射實(shí)例化java類(lèi)的時(shí)候,是通過(guò)使用 Class.newInstance() 方法來(lái)實(shí)現(xiàn)的,而前提就是這個(gè)類(lèi)java類(lèi)必須提供默認(rèn)構(gòu)造函數(shù)。
假如希望在java類(lèi)不提供默認(rèn)構(gòu)造函數(shù)的時(shí)候也能實(shí)現(xiàn)反射實(shí)例化,可以選擇使用 objenesis 來(lái)實(shí)例化java類(lèi),使用方式可以參考 objenesis官網(wǎng)。
3.框架選擇:
綜合上述的分析,最終我還是選擇了 Protostuff 框架來(lái)完成Protobuf數(shù)據(jù)的序列化,關(guān)于不支持無(wú)默認(rèn)構(gòu)造函數(shù)類(lèi)序列化的缺陷接下來(lái)通過(guò)使用 objenesis 也會(huì)得到解決。
自定義序列化工具類(lèi)
這里我們創(chuàng)建此工具類(lèi),取名為 SerializationUtil,使用Protostuff來(lái)序列化和反序列化Protobuf數(shù)據(jù):
1.庫(kù)引入:
首先要在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.工具類(lèi)編寫(xiě):
主要包含兩個(gè)核心的函數(shù):序列化函數(shù) Serializer 和反序列化函數(shù) 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 之后的版本,優(yōu)點(diǎn)就是支持沒(méi)有提供默認(rèn)構(gòu)造函數(shù)的java類(lèi)的實(shí)例化,可以看到這里通過(guò) Class<T> cls = (Class<T>) obj.getClass(); 來(lái)實(shí)例化java類(lèi)的。ConcurrentHashMap是適用于高并發(fā)的Map數(shù)據(jù)結(jié)構(gòu)。
3.工具類(lèi)調(diào)用:
隨便定義一個(gè)Protobuf的協(xié)議類(lèi),假設(shè)為User,下面就是具體的序列化工具使用操作:
序列化:
byte[] data = SerializationUtil.serialize(user,User.class);
反序列化:
User user2 = SerializationUtil.deserialize(data,User.class);
參考資料:
|