最近在java中使用protobuf,每次序列化反序列化都需要知道具体的Pb对象然后在调用其build().toByteArray()和parseFrom()。然框架使用的是动态代理来反射调用逻辑处理类,也就是需要动态的获取参数对象才能对其序列化反序列化,在开始的做法是做自己定义一个接口,多有的再每个Pb对象都有一个具体的javabean实体类,这实体类实现该接口之后提供序列化和反序列话两个方法由实体类具体实现。比如:
1.接口
2.实体对象
这样虽然是能解决了这个问题,但是发现没有,每个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
PARSER = new com.google.protobuf.AbstractParser() {
@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
private PbInteger(
com.google.protobuf.CodedInputStream input,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
throws com.google.protobuf.InvalidProtocolBufferException {
...
...
}
那我们是就可以通过反射自己来动态的创建这个Pb对象然后调用其parseFrom(buf)
private static AbstractParser doParser(Class type) {
AbstractParser abstractParser = new AbstractParser() {
@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> map = new HashMap<>();
private static com.google.protobuf.Parser doParser(Class type) {
if (map.containsKey(type)) {
return (AbstractParser)map.get(type);
} else {
AbstractParser abstractParser = new AbstractParser() {
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 parser(Class type,byte[] buff) throws InvalidProtocolBufferException {
com.google.protobuf.Parser 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());
}
}
更多文章请关注猪哥亮博客