了解Protocol Buffer

首先要知道什么是Protocol Buffer,在编程过程中,当涉及数据交换时,我们往往需要将对象进行序列化然后再传输。常见的序列化的格式有JSON,XML等,这些格式虽然可读性较好,但占用的空间大小并不是最优的。基于此,Google创建了一种名叫Protocol Buffer的序列化格式,它与JSON,XML相比可读性较差,但占用的空间也会更小,在一些对于速度要求比较高的场景中较为常用。

Java序列化Protocol Buffer框架—ProtoStuff

Google对于Protocol Buffer提供了多种语言的实现方法:Java,C++,go和python。但我们在使用时仍然需要去编写可读性不高的.proto文件,然后使用Google提供的实现方法编译成对应的语言,这就提高了我们使用Protocol Buffer的门槛。因此ProtoStuff就诞生了,通过ProtoStuff这个框架,我们能直接将对象通过Protocol Buffer这种序列化的方式转成对应的字节,极大地降低了我们使用Protocol Buffer的使用成本。

实例

首先我们新建一个maven项目,然后添加ProtoStuff的依赖,其中Objenesis是一个用来实例化一个特定类的新对象的Java库。通过该库,我们能在不调用构造函数的情况下实例化一个类的对象。

  1. <dependency>
  2. <groupId>com.dyuproject.protostuff</groupId>
  3. <artifactId>protostuff-core</artifactId>
  4. <version>${protostuff.version}</version>
  5. </dependency>
  6. <dependency>
  7. <groupId>com.dyuproject.protostuff</groupId>
  8. <artifactId>protostuff-runtime</artifactId>
  9. <version>${protostuff.version}</version>
  10. </dependency>
  11. <!-- Objenesis -->
  12. <dependency>
  13. <groupId>org.objenesis</groupId>
  14. <artifactId>objenesis</artifactId>
  15. <version>${objenesis.version}</version>
  16. </dependency>
  17. <!-- Lombok -->
  18. <dependency>
  19. <groupId>org.projectlombok</groupId>
  20. <artifactId>lombok</artifactId>
  21. <version>${lombok.version}</version>
  22. </dependency>

然后我们创建两个POJO来进行序列化的测试

  1. @Data
  2. @Builder
  3. public Class Goods {
  4. private Integer num;
  5. private String name;
  6. private Double price;
  7. }
  1. @Data
  2. @Builder
  3. public Class Repository {
  4. private String name;
  5. private String location;
  6. private List<Goods> goodsList;
  7. }

再之后编写Protocol Buffer序列化的工具类

  1. public Class SerializationUtil {
  2. private static Map<Class<?>, Schema<?>> cacheSchema = new ConcurrentHashMap();
  3. private static Objenesis objenesis = new ObjenesisStd(true);
  4. /**
  5. * 序列化(对象 -> 字节数组)
  6. *
  7. */
  8. public static <T> byte[] serialize(T obj) {
  9. Class<T> cls = (Class<T>) obj.getClass();
  10. LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
  11. try {
  12. Schema<T> schema = getSchema(cls);
  13. return ProtobufIOUtil.toByteArray(obj, schema, buffer);
  14. } catch (Exception e) {
  15. throw new IllegalStateException(e.getMessage(), e);
  16. } finally {
  17. buffer.clear();
  18. }
  19. }
  20. /**
  21. * 反序列化(字节数组 -> 对象)
  22. *
  23. */
  24. public static <T> T deserilize(byte[] data, Class<T> cls) {
  25. try {
  26. T message = objenesis.newInstance(cls);
  27. Schema<T> schema = getSchema(cls);
  28. ProtobufIOUtil.mergeFrom(data, message, schema);
  29. return message;
  30. } catch (Exception e) {
  31. throw new IllegalStateException(e.getMessage(), e);
  32. }
  33. }
  34. @SuppressWarnings("unchecked")
  35. private static <T> Schema<T> getSchema(Class<T> cls) {
  36. Schema<T> schema = (Schema<T>) cacheSchema.get(cls);
  37. if (schema == null) {
  38. schema = RuntimeSchema.createFrom(cls);
  39. cacheSchema.put(cls, schema);
  40. }
  41. return schema;
  42. }
  43. }

最后编写测试类来对序列化工具类进行测试

  1. public Class Test {
  2. public static void main(String[] args) {
  3. Goods phone = Goods.builder().num(10).name("phone").price(1999.99).build();
  4. Goods water = Goods.builder().num(100).name("water").price(1.00).build();
  5. Repository repository = Repository.builder().name("Taobao").location("china").goodsList(Arrays.asList(phone, water)).build();
  6. byte[] data = SerializationUtil.serialize(repository);
  7. System.out.println("序列化结果:" + Arrays.toString(data));
  8. Repository result = SerializationUtil.deserilize(data, Repository.class);
  9. System.out.println("反序列化结果:" + result);
  10. }
  11. }

输出结果:

序列化结果:[10, 6, 84, 97, 111, 98, 97, 111, 18, 5, 99, 104, 105, 110, 97, 26, 18, 8, 10, 18, 5, 112, 104, 111, 110, 101, 25, 41, 92, -113, -62, -11, 63, -97, 64, 26, 18, 8, 100, 18, 5, 119, 97, 116, 101, 114, 25, 0, 0, 0, 0, 0, 0, -16, 63]

反序列化结果:Repository(name=Taobao, location=china, goodsList=[Goods(num=10, name=phone, price=1999.99), Goods(num=100, name=water, price=1.0)])

与JSON的对比

首先导入JSON处理的依赖,这里我们使用jackson来对JSON进行处理

  1. <dependency>
  2. <groupId>com.fasterxml.jackson.core</groupId>
  3. <artifactId>jackson-databind</artifactId>
  4. <version>${jackson.version}</version>
  5. </dependency>

之后修改测试类

  1. public class Test {
  2. public static void main(String[] args) throws IOException {
  3. Goods phone = Goods.builder().num(10).name("phone").price(1999.99).build();
  4. Goods water = Goods.builder().num(100).name("water").price(1.00).build();
  5. Repository repository = Repository.builder().name("Taobao").location("china").goodsList(Arrays.asList(phone, water)).build();
  6. byte[] protobufData = SerializationUtil.serialize(repository);
  7. System.out.println("ProtoBuf序列化结果:" + Arrays.toString(protobufData));
  8. Repository protobufResult = SerializationUtil.deserilize(protobufData, Repository.class);
  9. System.out.println("ProtoBuf反序列化结果:" + protobufResult);
  10. ObjectMapper mapper = new ObjectMapper();
  11. byte[] jsonData = mapper.writeValueAsBytes(repository);
  12. System.out.println("JSON序列化结果:" + Arrays.toString(jsonData));
  13. Repository jsonResult = mapper.readValue(jsonData, Repository.class);
  14. System.out.println("JSON序列化结果:" + jsonResult);
  15. System.out.println();
  16. System.out.println("ProtoBuf序列化后字符串结果:" + new String(protobufData, StandardCharsets.UTF_8));
  17. System.out.println("JSON序列化后字符串结果:" + new String(jsonData, StandardCharsets.UTF_8));
  18. System.out.println();
  19. System.out.println("ProtoBuf序列化长度:" + protobufData.length);
  20. System.out.println("JSON序列化长度:" + jsonData.length);
  21. }
  22. }

输出结果:

ProtoBuf序列化结果:[10, 6, 84, 97, 111, 98, 97, 111, 18, 5, 99, 104, 105, 110, 97, 26, 18, 8, 10, 18, 5, 112, 104, 111, 110, 101, 25, 41, 92, -113, -62, -11, 63, -97, 64, 26, 18, 8, 100, 18, 5, 119, 97, 116, 101, 114, 25, 0, 0, 0, 0, 0, 0, -16, 63]

ProtoBuf反序列化结果:Repository(name=Taobao, location=china, goodsList=[Goods(num=10, name=phone, price=1999.99), Goods(num=100, name=water, price=1.0)])

JSON序列化结果:[123, 34, 110, 97, 109, 101, 34, 58, 34, 84, 97, 111, 98, 97, 111, 34, 44, 34, 108, 111, 99, 97, 116, 105, 111, 110, 34, 58, 34, 99, 104, 105, 110, 97, 34, 44, 34, 103, 111, 111, 100, 115, 76, 105, 115, 116, 34, 58, 91, 123, 34, 110, 117, 109, 34, 58, 49, 48, 44, 34, 110, 97, 109, 101, 34, 58, 34, 112, 104, 111, 110, 101, 34, 44, 34, 112, 114, 105, 99, 101, 34, 58, 49, 57, 57, 57, 46, 57, 57, 125, 44, 123, 34, 110, 117, 109, 34, 58, 49, 48, 48, 44, 34, 110, 97, 109, 101, 34, 58, 34, 119, 97, 116, 101, 114, 34, 44, 34, 112, 114, 105, 99, 101, 34, 58, 49, 46, 48, 125, 93, 125]

JSON序列化结果:Repository(name=Taobao, location=china, goodsList=[Goods(num=10, name=phone, price=1999.99), Goods(num=100, name=water, price=1.0)])

ProtoBuf序列化后字符串结果:

Taobaochina

phone)\���?�@dwater �?

JSON序列化后字符串结果:{"name":"Taobao","location":"china","goodsList":[{"num":10,"name":"phone","price":1999.99},{"num":100,"name":"water","price":1.0}]}

ProtoBuf序列化长度:55

JSON序列化长度:131

从结果来看在可读性上显然JSON更加易读,ProtoBuf序列化后再转为字符串甚至会乱码,但在长度上则显然ProtoBuf更占优势,JSON的长度比ProtoBuf多了一倍多。

️:在使用Jackson进行JSON反序列化时我们需要对我们的POJO类添加有参和无参构造,即添加@NoArgsConstructor @AllArgsConstructor 这两个注解,否则会抛出如下异常:

Exception in thread "main" com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of com.xxx.xxx.Repository (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)

at [Source: (byte[])"{"name":"Taobao","location":"china","goodsList":[{"num":10,"name":"phone","price":1999.99},{"num":100,"name":"water","price":1.0}]}"; line: 1, column: 2]

at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67)

at com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1764)

at com.fasterxml.jackson.databind.DatabindContext.reportBadDefinition(DatabindContext.java:400)

at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1209)

at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1400)

at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:362)

at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:195)

at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:322)

at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4593)

at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3609)

at com.silence.rpc.test.Test.main(Test.java:31)

原因是因为@Builder并不会添加无参构造,而Jackson的反序列化需要无参构造,因为在反序列化的时候,会先初始化对象,此时默认调用的是无参函数,然后再进行赋值,故此我们需要添加@NoArgsConstructor ,如果只添加这个注解,又会导致缺少有参构造,因此我们还需要添加@AllArgsConstructor

Protocol Buffer序列化Java框架-Protostuff的更多相关文章

  1. Protocol Buffer 序列化原理大揭秘 - 为什么Protocol Buffer性能这么好?

    前言 习惯用 Json.XML 数据存储格式的你们,相信大多都没听过Protocol Buffer Protocol Buffer 其实 是 Google出品的一种轻量 & 高效的结构化数据存 ...

  2. Protocol buffer序列化及其在微信蓝牙协议中的应用

    Protocol buffer是Google出品的一种轻便高效的结构化数据存储格式,可对结构化数据进行序列化,并具有语言无关.平台无关等特点,在通信协议和数据存储等领域已经得到广泛的应用.目前其已经提 ...

  3. Protocol Buffer序列化对比Java序列化.

    初识 Protocol Buff是谷歌推出的一种序列化协议. 而Java序列化协议也是一种协议. 两者的目的是, 将对象序列化成字节数组, 或者说是二进制数据, 那么他们之间有什么差异呢. proto ...

  4. Protocol Buffer序列化/反序列化---初体验(java版)

    今天闲遐时学习了 Protocol Buffer 在网上看到了许多资料,其中不泛精品,想要详细了解的请看文章结尾的友情链接,我这里就做加深印象,快速入门的一个完整的demo,仅此而已. 学完你可以得到 ...

  5. Protocol Buffer技术详解(Java实例)

    Protocol Buffer技术详解(Java实例) 该篇Blog和上一篇(C++实例)基本相同,只是面向于我们团队中的Java工程师,毕竟我们项目的前端部分是基于Android开发的,而且我们研发 ...

  6. Android:Google出品的序列化神器Protocol Buffer使用攻略

    习惯用 Json.XML 数据存储格式的你们,相信大多都没听过Protocol Buffer Protocol Buffer 其实 是 Google出品的一种轻量 & 高效的结构化数据存储格式 ...

  7. [java]序列化框架性能对比(kryo、hessian、java、protostuff)

    序列化框架性能对比(kryo.hessian.java.protostuff) 简介:   优点 缺点 Kryo 速度快,序列化后体积小 跨语言支持较复杂 Hessian 默认支持跨语言 较慢 Pro ...

  8. Protocol Buffer使用转换工具将proto文件转换成Java文件流程及使用

    Client与Server的网络通信协议传输使用google protobuf,服务器端使用的是Java 一. Protocol Buffersprotobuf全称Google Protocol Bu ...

  9. protocol buffer 整数序列化

    http://blog.csdn.net/csfreebird/article/details/7624807 varints用于正整数 (无符号整数) varints 是 一个很不错的技术.将一个整 ...

随机推荐

  1. Flink Sql 之 Calcite Volcano优化器(源码解析)

    Calcite作为大数据领域最常用的SQL解析引擎,支持Flink , hive,  kylin , druid等大型项目的sql解析 同时想要深入研究Flink sql源码的话calcite也是必备 ...

  2. docker初探和基础搭建

    个人博客 docker中文手册 Docker 是什么? docker是一个开源的软件部署解决方案: docker也是轻量级的应用容器框架: docker可以打包.发布.运行任何的应用. 在我的粗浅理解 ...

  3. python编写学习助手0

    项目原因 为了解决学习知识后不及时复习而导致遗忘的问题,准备写一个桌面助手,采用艾宾浩斯记忆法,对每次学习的内容排布复习计划. 第一步是做出最简单的文本列表,里面是待办事项,每个复习待办事项都会有符合 ...

  4. java的加载与执行原理详解

    java程序从开发到最终运行经历了什么? (31) 编译期: 第一步:在硬盘某个位置(随意),新建一个xxx.java文件 第二步:使用记事本或者其他文本编辑器例如EditPlus打开xxx.java ...

  5. Alpha-技术规格说明书

    项目 内容 这个作业属于哪个课程 2021春季软件工程(罗杰 任健) 这个作业的要求在哪里 团队项目-计划-功能规格说明书 一.架构与技术栈 1.整体架构 本项目的整体架构如上图所示.下面我们将对涉及 ...

  6. Vue接收后端传过来excel表格的文件流并下载

    题外话:当接收文件流时要确定文件流的类型,但也有例外就是application/octet-stream类型,主要是只用来下载的类型,这个类型简单理解意思就是通用类型类似 var .object.ar ...

  7. 使用nexus搭建一个docker私服

    使用nexus搭建docker私服 一.需求: 二.实现步骤 1.编写`docker-compose`文件,实现`nexus`的部署 2.修改/usr/lib/systemd/system/docke ...

  8. lib库无法加载的情况分析

    最近升级vs2017的时候遇到无法加载库的问题,在网上查找问题,网上给出可能有三种情况导致该问题:路径是否正确:库依赖是否齐全:库版本是否正确.最直接的方法就是用depends软件去查询,是否有模块有 ...

  9. 难搞的C语言指针你搞懂了多少

    C语言指针说难不难但是说容易又是最容易出错的地方,因此不管是你要做什么只要用到C指针你就跳不过,今天咱们就以 十九个例子来给大家简单的分析一下指针的应用,最后会有C语言视频资料提供给大家更加深入的参考 ...

  10. Jquery校验中国身份证号码是否正确

    在项目中使用表单时经常会涉及到身份证号码是否正确的校验,下面看看应该中国二代身份证号码应该怎么用Jquery校验呢? 二代身份证校验码的计算方法 二代身份证由17位数字和一位校验码组成,那么校验方法是 ...