最近在java中使用protobuf,每次序列化反序列化都需要知道具體的Pb對象然後在調用其build().toByteArray()和parseFrom()。然框架使用的是動態代理來反射調用邏輯處理類,也就是需要動態的獲取參數對象才能對其序列化反序列化,在開始的做法是做自己定義一個接口,多有的再每個Pb對象都有一個具體的javabean實體類,這實體類實現該接口之後提供序列化和反序列話兩個方法由實體類具體實現。比如:
1.接口
2.實體對象
3.序列化
4.反序列化
這樣雖然是能解決了這個問題,但是發現沒有,每個Pb類都要對應一個實體類,而且每個類都要屬性拷貝賦值才能使用,這樣除了麻煩之外還類臃腫,那有沒有什麼辦法可以解決這個問題呢?(沒有這章博客不是白寫了嗎)
解決方案
1,使用Protostuff
Protostuff的使用很簡單就是使用Protostuff的工具類可以直接序列化反序列化javabean對象,這樣就可以不用寫proto文件就解決了上面的兩個問題。但是由於前端使用的Unity3D,原本Unity3D的C#原生也是支持Protostuff序列化反序列化出來的,但是又由於用了ILRuntime做熱更新框架。只能使用proto文件生成的C#進行使用,否則如果後端java使用protostuff而前端使用proto的話一些基本的數據類型可以相通交互,然而如果是包裝類型或者集合類型那這兩個序列化出來的字節數組數據是不一樣的也就是說不能使用這種方式進行交互。那麼就只能另找捷徑自己找方法。
那麼通過baidu了一大堆都只是告訴你怎麼基礎使用protobuff進行序列化反序列化,這對於我來說沒什麼屁用啊。於是自己看了下proto生成的java源碼(主要看build().toByteArray()和parseFrom(buf)兩個方法的實現就可以了)
2.自己寫一個工具類
1.序列化
通過源碼發現parseFrom(buf)最終調用了其生成源碼內部的一個com.google.protobuf.Parser常量PARSER的parseFrom(buf)
public static net.yt.commons.utils.pbcreate.primitive.Primitive.PbBoolean parseFrom(byte[] data)
throws com.google.protobuf.InvalidProtocolBufferException {
return PARSER.parseFrom(data);
}
而這個PARSER又是怎麼創建出來的呢
private static final com.google.protobuf.Parser<PbBoolean>
PARSER = new com.google.protobuf.AbstractParser<PbBoolean>() {
@java.lang.Override
public PbBoolean parsePartialFrom(
com.google.protobuf.CodedInputStream input,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws com.google.protobuf.InvalidProtocolBufferException {
return new PbBoolean(input, extensionRegistry);
}
};
是通過new一個com.google.protobuf.AbstractParser<MessageType extends MessageLite>而在裏面的new PbBoolean(input, extensionRegistry)我們點進去看是一個私有的構造器
private PbInteger(
com.google.protobuf.CodedInputStream input,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws com.google.protobuf.InvalidProtocolBufferException {
...
...
}
那我們是就可以通過反射自己來動態的創建這個Pb對象然後調用其parseFrom(buf)
private static <M extends MessageLite> AbstractParser<M> doParser(Class<M> type) {
AbstractParser<M> abstractParser = new AbstractParser<M>() {
@Override
public M parsePartialFrom(
CodedInputStream input,
ExtensionRegistryLite extensionRegistry)
throws InvalidProtocolBufferException {
try {
Constructor c = type.getDeclaredConstructor(CodedInputStream.class, ExtensionRegistryLite.class);
c.setAccessible(true);
M obj = (M) c.newInstance(input, extensionRegistry);
return obj;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
};
return abstractParser;
}
這樣就可以在使用的時候以泛型的方式傳入參數動態創建AbstractParser對象,然後就可以調用其parseFrom(buf),但是這裏使用了反射所以在性能方面肯定有犧牲。那我們在看看Pb的源碼發現這個Pb對象的AbstractParser是一個常量值也就是整個運行期間有且只有一個,這樣我們就可以使用緩存來解決這個每次使用都要反射創建的從而導致性能低下的問題,完整代碼會在最後放出
2.反序列化,
其實這個因爲在要使用的時候就知道其是什麼具體的對象所以直接調用其build().toByteArray(),但是我這也寫了一個動態反序列化的工具,通過Pb源碼我們發現其都會實現MessageLite.Builder的接口並提供了Message build();方法所以我們只需要將工具類的反序列化方法接收參數設置爲com.google.protobuf.Message.Builder接口類型就可以了
public static byte[] toByteArray(com.google.protobuf.Message.Builder builder){
return builder.build().toByteArray();
}
完整工具代碼
/**
* @author 豬哥亮
* @email [email protected]
* @date 2020/6/21 17:26
* @blog zglblog.cn
* 描述:
*/
public class BufferSerializableUtil {
private static Map<Class, Parser<MessageLite>> map = new HashMap<>();
private static <M extends MessageLite> com.google.protobuf.Parser<M> doParser(Class<M> type) {
if (map.containsKey(type)) {
return (AbstractParser)map.get(type);
} else {
AbstractParser<M> abstractParser = new AbstractParser<M>() {
public M parsePartialFrom(CodedInputStream input, ExtensionRegistryLite extensionRegistry) {
try {
Constructor c = type.getDeclaredConstructor(CodedInputStream.class, ExtensionRegistryLite.class);
c.setAccessible(true);
M obj = (MessageLite)c.newInstance(input, extensionRegistry);
return obj;
} catch (Exception var5) {
var5.printStackTrace();
return null;
}
}
};
map.put(type, abstractParser);
return abstractParser;
}
}
public static <M extends MessageLite> M parser(Class<M> type,byte[] buff) throws InvalidProtocolBufferException {
com.google.protobuf.Parser<M> mParser = doParser(type);
return mParser.parseFrom(buff);
}
public static byte[] toByteArray(com.google.protobuf.Message.Builder builder){
return builder.build().toByteArray();
}
public static void main(String[] args) throws InvalidProtocolBufferException {
Primitive.PbInteger.Builder builder = Primitive.PbInteger.newBuilder();
builder.setValue(100);
byte[] bytes = toByteArray(builder);
Primitive.PbInteger parser = parser(Primitive.PbInteger.class, bytes);
System.out.println(parser.getValue());
}
}
更多文章請關注豬哥亮博客