Protostuff自定義序列化(Delegate)解析


背景

在使用Protostuff進行序列化的時候,不幸地遇到了一個問題,就是Timestamp作為字段的時候,轉換出現問題,通過Protostuff轉換后的結果都是1970-01-01 08:00:00,這就造成了Timestamp不能夠序列化。於是Google了一番,得知可以用Delegate來解決這個問題。

原來的代碼

ProtobufferCodec類

import java.lang.reflect.Constructor;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import io.protostuff.LinkedBuffer;
import io.protostuff.ProtostuffIOUtil;
import io.protostuff.Schema;
import io.protostuff.runtime.RuntimeSchema;

public class ProtobufferCodec implements Codec {

    private static Map<Class<?>, Schema<?>> cachedSchema = new ConcurrentHashMap<>();

    public ProtobufferCodec() {

    }

    @Override
    public short getId() {
        return Codecs.PROTOBUFFER_CODEC;
    }

    @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;
    }

    @Override
    public <T> byte[] encode(T obj) {
        if (obj == null) {
            return null;
        }
        Class<T> cls = (Class<T>) obj.getClass();
        LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
        try {
            Schema<T> schema = getSchema(cls);
            byte[] bytes = ProtostuffIOUtil.toByteArray(obj, schema, buffer);
            return bytes;
        } catch (Exception e) {
            throw new IllegalStateException(e.getMessage(), e);
        } finally {
            buffer.clear();
        }
    }

    @Override
    public <T> T decode(byte[] bytes, Class<T> clazz) {
        if (bytes == null || bytes.length == 0) {
            return null;
        }
        try {
            Constructor<T> constructor = clazz.getConstructor();
            constructor.setAccessible(true);
            T message = constructor.newInstance();
            Schema<T> schema = getSchema(clazz);
            ProtostuffIOUtil.mergeFrom(bytes, message, schema);
            return message;
        } catch (Exception e) {
            throw new IllegalStateException(e.getMessage(), e);
        }
    }

}

Codec接口

/**
 * 編解碼器
 * @author jiujie
 * @version $Id: Codec.java, v 0.1 2016年3月31日 上午11:39:14 jiujie Exp $
 */
public interface Codec {

    /**
     * 編解碼器ID,用於標識編解碼器
     * @author jiujie
     * 2016年3月31日 上午11:38:39
     * @return
     */
    public short getId();

    /**
     * 把對象數據結構編碼成一個DataBuffer
     * @param <T>
     */
    public <T> byte[] encode(T obj);

    /**
     * 把DataBuffer解包構造一個對象
     * @param <T>
     */
    public <T> T decode(byte[] bytes, Class<T> clazz);
}

修改后的代碼

import java.sql.Timestamp;
import java.util.concurrent.ConcurrentHashMap;

import io.protostuff.LinkedBuffer;
import io.protostuff.ProtostuffIOUtil;
import io.protostuff.Schema;
import io.protostuff.runtime.DefaultIdStrategy;
import io.protostuff.runtime.Delegate;
import io.protostuff.runtime.RuntimeEnv;
import io.protostuff.runtime.RuntimeSchema;

/**
 * ProtoBuffer編解碼
 * @author jiujie
 * @version $Id: ProtobufferCodec.java, v 0.1 2016年7月20日 下午1:52:41 jiujie Exp $
 */
public class ProtobufferCodec implements Codec {

    /** 時間戳轉換Delegate,解決時間戳轉換后錯誤問題 @author jiujie 2016年7月20日 下午1:52:25 */
    private final static Delegate<Timestamp>                    TIMESTAMP_DELEGATE = new TimestampDelegate();

    private final static DefaultIdStrategy                      idStrategy         = ((DefaultIdStrategy) RuntimeEnv.ID_STRATEGY);

    private final static ConcurrentHashMap<Class<?>, Schema<?>> cachedSchema       = new ConcurrentHashMap<>();

    static {
        idStrategy.registerDelegate(TIMESTAMP_DELEGATE);
    }

    public ProtobufferCodec() {
    }

    @Override
    public short getId() {
        return Codecs.PROTOBUFFER_CODEC;
    }

    @SuppressWarnings("unchecked")
    public static <T> Schema<T> getSchema(Class<T> clazz) {
        Schema<T> schema = (Schema<T>) cachedSchema.get(clazz);
        if (schema == null) {
            schema = RuntimeSchema.createFrom(clazz, idStrategy);
            cachedSchema.put(clazz, schema);
        }
        return schema;
    }

    @Override
    public <T> byte[] encode(T obj) {
        if (obj == null) {
            return null;
        }
        @SuppressWarnings("unchecked")
        Class<T> cls = (Class<T>) obj.getClass();
        LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
        try {
            Schema<T> schema = getSchema(cls);
            byte[] bytes = ProtostuffIOUtil.toByteArray(obj, schema, buffer);
            return bytes;
        } catch (Exception e) {
            throw new IllegalStateException(e.getMessage(), e);
        } finally {
            buffer.clear();
        }
    }

    @Override
    public <T> T decode(byte[] bytes, Class<T> clazz) {
        if (bytes == null || bytes.length == 0) {
            return null;
        }
        try {
            Schema<T> schema = getSchema(clazz);
            //改為由Schema來實例化解碼對象,沒有構造函數也沒有問題
            T message = schema.newMessage();
            ProtostuffIOUtil.mergeFrom(bytes, message, schema);
            return message;
        } catch (Exception e) {
            throw new IllegalStateException(e.getMessage(), e);
        }
    }

}

TimestampDelegate類

import java.io.IOException;
import java.sql.Timestamp;

import io.protostuff.Input;
import io.protostuff.Output;
import io.protostuff.Pipe;
import io.protostuff.WireFormat.FieldType;
import io.protostuff.runtime.Delegate;

/**
 * protostuff timestamp Delegate
 * @author jiujie
 * @version $Id: TimestampDelegate.java, v 0.1 2016年7月20日 下午2:08:11 jiujie Exp $
 */
public class TimestampDelegate implements Delegate<Timestamp> {
    
    public FieldType getFieldType() {
        return FieldType.FIXED64;
    }

    public Class<?> typeClass() {
        return Timestamp.class;
    }

    public Timestamp readFrom(Input input) throws IOException {
        return new Timestamp(input.readFixed64());
    }

    public void writeTo(Output output, int number, Timestamp value,
                        boolean repeated) throws IOException {
        output.writeFixed64(number, value.getTime(), repeated);
    }

    public void transfer(Pipe pipe, Input input, Output output, int number,
                         boolean repeated) throws IOException {
        output.writeFixed64(number, input.readFixed64(), repeated);
    }

}

使用方法場景,及注意事項

使用方法:

實現Delegage接口,並在IdStrategy策略類中注冊該Delegate。

使用場景:

當需要序列化的類的字段中有transient聲明序列化時會過濾字段,導致還原時丟失信息的場景,或者一些需要高度自定義數據格式的場景下,可以使用Delegate來序列化與反序列化。

注意事項:

 

這個對象必須是另一個對象的字段時,這個Delegate才會生效,如果直接用Timestamp來轉換,則還是不生效,這個問題與源碼的實現有關,源碼是檢測對象的字段來調用Delegate的,如果本身直接過來序列化的時候,則不會觸發Delegate。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM