本文將實現一個包含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.read(clazz.newInstance(), binaryDecoder);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
7 ThriftSerializer
/**
* @version V1.0
* @author: lin_shen
* @date: 2018/11/18
* @Description: Thrift序列化(只能序列化IDL產生的類)
*/
public class ThriftSerializer implements ISerializer {
@Override
public <T> byte[] serialize(T obj) {
try {
TSerializer serializer = new TSerializer(new TBinaryProtocol.Factory());
return serializer.serialize((TBase) obj);
} catch (TException e) {
throw new RuntimeException(e);
}
}
@Override
public <T> T deserialize(byte[] data, Class<T> clazz) {
try {
TBase o = (TBase) clazz.newInstance();
TDeserializer tDeserializer = new TDeserializer();
tDeserializer.deserialize(o, data);
return (T) o;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
8 ProtocolBufferSerializer
/**
* @version V1.0
* @author: lin_shen
* @date: 2018/11/18
* @Description: ProtocolBuffer序列化(只能序列化IDL產生的類)
*/
public class ProtocolBufferSerializer implements ISerializer {
@Override
public <T> byte[] serialize(T obj) {
try {
return (byte[]) MethodUtils.invokeMethod(obj, "toByteArray");
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@Override
public <T> T deserialize(byte[] data, Class<T> cls) {
try {
Object o = MethodUtils.invokeStaticMethod(cls, "getDefaultInstance");
return (T) MethodUtils.invokeMethod(o, "parseFrom", new Object[]{data});
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}