初探Protostuff的使用
最近在学习RPC,看到了一个叫做Protostuff的库,是基于谷歌Protocal Buffer的序列化库,之前了解过Protocol Buffer,对学习了一些资料后,写了个demo,记录下来。
什么是Protocol Buffer?
Protocol Buffer是谷歌出品的一种数据交换格式,独立于语言和平台,类似于json。Google提供了多种语言的实现:java、c++、go和python。对象序列化城Protocol Buffer之后可读性差,但是相比xml,json,它占用小,速度快。适合做数据存储或 RPC 数据交换格式。
Java序列化库 - Protostuff
相对我们常用的json来说,Protocol Buffer门槛更高,因为需要编写.proto文件,再把它编译成目标语言,这样使用起来就很麻烦。但是现在有了protostuff之后,就不需要依赖.proto文件了,他可以直接对POJO进行序列化和反序列化,使用起来非常简单。
实战
新建一个SpringBoot的项目,再引入Protostuff的依赖
<dependency> <groupId>io.protostuff</groupId> <artifactId>protostuff-core</artifactId> <version>${protostuff.version}</version> </dependency> <dependency> <groupId>io.protostuff</groupId> <artifactId>protostuff-runtime</artifactId> <version>${protostuff.version}</version> </dependency>
先编写两个POJO,再把它们嵌套起来,这里使用了lombok的@Data注解和@Builder注解,@Data可以自动生成getter setter,@Builder注解可以让我们通过更加优雅的构建者模式来创建对象。
@Data @Builder public class User { private String id; private String name; private Integer age; private String desc; }
@Data @Builder public class Group { private String id; private String name; private User user; }
接下来编写Protostuff序列化工具类
public class ProtostuffUtils { /** * 避免每次序列化都重新申请Buffer空间 */ private static LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE); /** * 缓存Schema */ private static Map<Class<?>, Schema<?>> schemaCache = new ConcurrentHashMap<>(); /** * 序列化方法,把指定对象序列化成字节数组 * * @param obj * @param <T> * @return */ @SuppressWarnings("unchecked") public static <T> byte[] serialize(T obj) { Class<T> clazz = (Class<T>) obj.getClass(); Schema<T> schema = getSchema(clazz); byte[] data; try { data = ProtostuffIOUtil.toByteArray(obj, schema, buffer); } finally { buffer.clear(); } return data; } /** * 反序列化方法,将字节数组反序列化成指定Class类型 * * @param data * @param clazz * @param <T> * @return */ public static <T> T deserialize(byte[] data, Class<T> clazz) { Schema<T> schema = getSchema(clazz); T obj = schema.newMessage(); ProtostuffIOUtil.mergeFrom(data, obj, schema); return obj; } @SuppressWarnings("unchecked") private static <T> Schema<T> getSchema(Class<T> clazz) { Schema<T> schema = (Schema<T>) schemaCache.get(clazz); if (Objects.isNull(schema)) { //这个schema通过RuntimeSchema进行懒创建并缓存 //所以可以一直调用RuntimeSchema.getSchema(),这个方法是线程安全的 schema = RuntimeSchema.getSchema(clazz); if (Objects.nonNull(schema)) { schemaCache.put(clazz, schema); } } return schema; } }
验证序列化功能
@SpringBootApplication public class Application implements CommandLineRunner { public static void main(String[] args) { SpringApplication.run(Application.class, args); } @Override public void run(String... strings) throws Exception { //创建一个user对象 User user = User.builder().id("1").age(20).name("张三").desc("programmer").build(); //创建一个Group对象 Group group = Group.builder().id("1").name("分组1").user(user).build(); //使用ProtostuffUtils序列化 byte[] data = ProtostuffUtils.serialize(group); System.out.println("序列化后:" + Arrays.toString(data)); Group result = ProtostuffUtils.deserialize(data, Group.class); System.out.println("反序列化后:" + result.toString()); } }
可以看到控制台打印出如下数据,说明序列化和反序列化成功
序列化后:[10, 1, 49, 18, 7, -27, -120, -122, -25, -69, -124, 49, 27, 10, 1, 49, 18, 6, -27, -68, -96, -28, -72, -119, 24, 20, 34, 10, 112, 114,
111, 103, 114, 97, 109, 109, 101, 114, 28] 反序列化后:Group(id=1, name=分组1, user=User(id=1, name=张三, age=20, desc=programmer))
最后,代码在这里地址,欢迎star。
参考
原文地址:https://blog.csdn.net/oppo5630/article/details/80173520