1. 程式人生 > >實現 序列化引擎(支援 JDK預設、Hessian、Json、Protostuff、Xml、Avro、ProtocolBuffer、Thrift等序列化方式)

實現 序列化引擎(支援 JDK預設、Hessian、Json、Protostuff、Xml、Avro、ProtocolBuffer、Thrift等序列化方式)

本文將實現一個包含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 (序列化引擎)
    管理序列化列舉類和實現類的對應關係,並提供通用序列化與反序列化方法入口

序列化方法分類

  1. 不需要 IDL 工具
    1. 需有Serializable介面
      • DefaultJavaSerializer
      • HessianSerializer
    2. 不需有Serializable介面
      • JSONSerializer
      • XmlSerializer
      • ProtoStuffSerializer
  2. 需要 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.