背景
在使用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。