實現 序列化引擎(支援 JDK預設、Hessian、Json、Protostuff、Xml、Avro、ProtocolBuffer、Thrift等序列化方式)
阿新 • • 發佈:2019-01-10
本文將實現一個包含JDK預設、Hessian、Json、Protostuff、Xml、Avro、ProtocolBuffer、Thrift等序列化方式的序列化引擎。使用者可以提供使用 SerializerEngine 方便快捷地切換序列化方法實現。
文章目錄
一 使用效果
執行程式碼如下:
@Data
public static class Person {
String name;
int age;
Date birth;
}
public static void main(String[] args){
Person person=new Person();
person.setName("li");
person.setAge(18);
person.setBirth(new Date());
out.println(person) ;
out.println("--------- Json序列化 ------------");
byte[] personBytes=SerializerEngine.serialize(person, SerializeTypeEnum.JSONSerializer);
out.println(new String(personBytes));
out.println("--------- Json反序列化 ---------------------");
person=SerializerEngine.deserialize(personBytes,Person.class,SerializeTypeEnum.JSONSerializer);
out.println(person);
out.println("---------- Xml序列化 -------------------");
personBytes=SerializerEngine.serialize(person, SerializeTypeEnum.XmlSerializer);
out.println(new String(personBytes));
out.println("---------- Xml反序列化 ------------------");
person=SerializerEngine.deserialize(personBytes,Person.class,SerializeTypeEnum.XmlSerializer);
out.println(person);
}
結果為:
二 總體結構
1 結構圖
2 元件介紹
- ISerializer (序列化介面)
定義序列化和反序列化方法 - impl (序列化介面實現類)
實現序列化和反序列化方法 - SerializeTypeEnum (序列化列舉類)
一個不重複的數字code對應一個列舉類 - SerializerEngine (序列化引擎)
管理序列化列舉類和實現類的對應關係,並提供通用序列化與反序列化方法入口
序列化方法分類
- 不需要 IDL 工具
- 需有Serializable介面
- DefaultJavaSerializer
- HessianSerializer
- 不需有Serializable介面
- JSONSerializer
- XmlSerializer
- ProtoStuffSerializer
- 需有Serializable介面
- 需要 IDL 工具
- AvroSerializer
- ThriftSerializer
- ProtocolBufferSerializer
序列化方法場景推薦
如果不使用IDL的服務間傳輸的話,建議使用 ProtoStuffSerializer ,如果有可讀性要求或是基於Ajax和移動APP通訊的話就使用 JSONSerializer。
3 相互關係
使用者直接使用 SerializerEngine 進行序列化和反序列化,通過傳入 SerializeTypeEnum 指定序列化方法,SerializeTypeEnum和序列化方法的關係由 SerializerEngine 維護,序列化方法實現類例項 由SerializerEngine儲存和管理。
三 結構實現
1 ISerializer
/**
* @version V1.0
* @author: lin_shen
* @date: 18-11-12
* @Description: 序列化介面
*/
public interface ISerializer {
/**
* 序列化
* @param obj 序列化物件
* @param <T> 序列化物件原始型別
* @return 位元組陣列
*/
<T> byte[] serialize(T obj);
/**
* 反序列化
* @param data 序列化位元組陣列
* @param clazz 原始型別的類物件
* @param <T> 原始型別
* @return
*/
<T> T deserialize(byte[] data, Class<T> clazz);
}
2 SerializeTypeEnum
注意這裡每種序列化方式都對應了一個數字
/**
* @version V1.0
* @author: lin_shen
* @date: 18-11-18
* @Description: 序列化列舉類
*/
public enum SerializeTypeEnum {
/**
* Java預設序列化
*/
DefaultJavaSerializer(0),
/**
* Hessian序列化
*/
HessianSerializer(1),
/**
* Json序列化(基於Jackson)
*/
JSONSerializer(2),
/**
* Protostuff序列化
*/
ProtoStuffSerializer(3),
/**
* Xml序列化
*/
XmlSerializer(4),
/**
* Avro序列化,需藉助IDL
*/
AvroSerializer(5),
/**
* ProtocolBuffer序列化,需藉助IDL
*/
ProtocolBufferSerializer(6),
/**
* Thrift序列化,需藉助IDL
*/
ThriftSerializer(7);
private int code;
SerializeTypeEnum(int code) {
this.code = code;
}
public static SerializeTypeEnum queryByCode (int code) {
for (SerializeTypeEnum type : values()) {
if(type.getCode()==code){
return type;
}
}
return null;
}
public int getCode() {
return code;
}
}
3 SerializerEngine
利用 SERIALIZER_MAP 管理 列舉和實現類的關係,提供可以指定序列化型別的入口方法
/**
* @version V1.0
* @author: lin_shen
* @date: 18-11-18
* @Description: 序列化引擎
*/
public class SerializerEngine {
private static final Map<SerializeTypeEnum, ISerializer> SERIALIZER_MAP = Maps.newConcurrentMap();
static {
SERIALIZER_MAP.put(SerializeTypeEnum.DefaultJavaSerializer, new DefaultJavaSerializer());
SERIALIZER_MAP.put(SerializeTypeEnum.HessianSerializer, new HessianSerializer());
SERIALIZER_MAP.put(SerializeTypeEnum.JSONSerializer, new JSONSerializer());
SERIALIZER_MAP.put(SerializeTypeEnum.XmlSerializer, new XmlSerializer());
SERIALIZER_MAP.put(SerializeTypeEnum.ProtoStuffSerializer, new ProtoStuffSerializer());
//以下三類不能使用普通的java bean,需藉助IDL
SERIALIZER_MAP.put(SerializeTypeEnum.AvroSerializer, new AvroSerializer());
SERIALIZER_MAP.put(SerializeTypeEnum.ThriftSerializer, new ThriftSerializer());
SERIALIZER_MAP.put(SerializeTypeEnum.ProtocolBufferSerializer, new ProtocolBufferSerializer());
}
public static <T> byte[] serialize(T obj, SerializeTypeEnum serializeTypeEnum) {
ISerializer serializer = SERIALIZER_MAP.get(serializeTypeEnum);
return serializer.serialize(obj);
}
public static <T> T deserialize(byte[] data, Class<T> clazz, SerializeTypeEnum serializeTypeEnum) {
ISerializer serializer = SERIALIZER_MAP.get(serializeTypeEnum);
return serializer.deserialize(data, clazz);
}
}
四 各種序列化方法實現
1 DefaultJavaSerializer
效率低,侷限於Java
/**
* @version V1.0
* @author: lin_shen
* @date: 18-11-18
* @Description: Java預設序列化(需有Serializable介面)
*/
public class DefaultJavaSerializer implements ISerializer {
@Override
public <T> byte[] serialize(T obj) {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream)){
objectOutputStream.writeObject(obj);
} catch (Exception e) {
throw new RuntimeException(e);
}
return byteArrayOutputStream.toByteArray();
}
@Override
public <T> T deserialize(byte[] data, Class<T> clazz) {
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(data);
try {
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
return (T) objectInputStream.readObject();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
2 HessianSerializer
使用方法和DefaultJavaSerializer類似,但效率高,而且跨語言,在大部分場景下可取代DefaultJavaSerializer(在某些情況下支援沒有Java原生的好)
/**
* @version V1.0
* @author: lin_shen
* @date: 18-11-18
* @Description: Hessian序列化(需有Serializable介面)
*/
public class HessianSerializer implements ISerializer {
@Override
public byte[] serialize(Object obj) {
try(ByteArrayOutputStream os = new ByteArrayOutputStream()) {
HessianOutput ho = new HessianOutput(os);
ho.writeObject(obj);
return os.toByteArray();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public <T> T deserialize(byte[] data, Class<T> clazz) {
try(ByteArrayInputStream is = new ByteArrayInputStream(data)) {
HessianInput hi = new HessianInput(is);
return (T) hi.readObject();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
3 JSONSerializer
跨語言,可讀性好,效率相對沒那麼高,這裡用Jackson實現,可以完成很多定製
/**
* @version V1.0
* @author: lin_shen
* @date: 18-11-18
* @Description: Json序列化(基於jackson實現)
*/
public class JSONSerializer implements ISerializer {
/**
* ObjectMapper可配置json序列化規則,該類的建立需要消耗較多資源,故應配置為類成員物件
*/
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
static {
//允許欄位名不帶引號(這不符合JSON標準,但在JS中合法)
OBJECT_MAPPER.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
//允許使用單引號代替雙引號(這不符合JSON標準,但這一些JSON生成器中合法)
OBJECT_MAPPER.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
//允許攜帶不加引號的控制字元(即ASCII碼小於32的),不符合JSON標準,故預設為false
OBJECT_MAPPER.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);
//允許出現未定義處理方法(沒有對應的setter方法或其他的處理器)的未知欄位
OBJECT_MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}
@Override
public <T> byte[] serialize(T obj) {
try {
String json = OBJECT_MAPPER.writeValueAsString(obj);
return json.getBytes();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public <T> T deserialize(byte[] data, Class<T> clazz) {
String json = new String(data);
try {
return (T) OBJECT_MAPPER.readValue(json, clazz);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
4 XmlSerializer
大部分情況下可被Json取代
/**
* @author liyebing created on 17/1/21.
* @version $Id$
*/
public class XmlSerializer implements ISerializer {
private static final XStream xStream = new XStream(new DomDriver());
@Override
public <T> byte[] serialize(T obj) {
return xStream.toXML(obj).getBytes();
}
@Override
public <T> T deserialize(byte[] data, Class<T> clazz) {
String xml = new String(data);
return (T) xStream.fromXML(xml);
}
}
5 ProtoStuffSerializer
基於protostuff的protocolbuffer序列化工具,各方面效率都高,且不需要藉助IDL(介面描述語言)
/**
* @version V1.0
* @author: lin_shen
* @date: 2018/11/18
* @Description: protobuf序列化工具(基於protostuff)
*/
public class ProtoStuffSerializer implements ISerializer {
/**
* 用於快取類物件與Schema的對應關係,避免重複建立Schema
*/
private static final Map<Class<?>, Schema<?>> CACHED_SCHEMA = new ConcurrentHashMap<>();
/**
* 用於高效便捷地生成類例項,而無需構造方法支援
*/
private static final Objenesis OBJENESIS = new ObjenesisStd(true);
/**
* 序列化(物件 -> 位元組陣列)
*/
@Override
@SuppressWarnings("unchecked")
public <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();
}
}
/**
* 反序列化(位元組陣列 -> 物件)
*/
@Override
public <T> T deserialize(byte[] data, Class<T> cls) {
try {
T message = OBJENESIS.newInstance(cls);
Schema<T> schema = getSchema(cls);
ProtostuffIOUtil.mergeFrom(data, message, schema);
return message;
} catch (Exception e) {
throw new IllegalStateException(e.getMessage(), e);
}
}
@SuppressWarnings("unchecked")
private <T> Schema<T> getSchema(Class<T> cls) {
Schema<T> schema = (Schema<T>) CACHED_SCHEMA.get(cls);
if (schema == null) {
schema = RuntimeSchema.createFrom(cls);
CACHED_SCHEMA.put(cls, schema);
}
return schema;
}
}
6 AvroSerializer
需要藉助IDL
/**
* @version V1.0
* @author: lin_shen
* @date: 2018/11/18
* @Description: AvroSerializer(只能序列化IDL產生的類)
*/
public class AvroSerializer implements ISerializer {
@Override
public <T> byte[] serialize(T obj) {
try {
DatumWriter userDatumWriter = new SpecificDatumWriter(obj.getClass());
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
BinaryEncoder binaryEncoder = EncoderFactory.get().directBinaryEncoder(outputStream, null);
userDatumWriter.write(obj, binaryEncoder);
return outputStream.toByteArray();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public <T> T deserialize(byte[] data, Class<T> clazz) {
try {
DatumReader userDatumReader = new SpecificDatumReader(clazz);
BinaryDecoder binaryDecoder = DecoderFactory.get().directBinaryDecoder(new ByteArrayInputStream(data), null);
return (T) userDatumReader.