protocol buffers 是一种语言无关、平台无关、可扩展的序列化结构数据的方法,它可用于通信协议、数据存储等。
protocol buffers 是一种灵活,高效,自动化机制的结构数据序列化方法-可类比 XML,但是比 XML 更小(3 ~ 10倍)、更快(20 ~ 100倍)、更为简单。你可以定义数据的结构,然后使用特殊生成的源代码轻松的在各种数据流中使用各种语言进行编写和读取结构数据。你甚至可以更新数据结构,而不破坏根据旧数据结构编译而成并且已部署的程序。 如何工作?
你可以通过在 .proto 文件中定义 protocol buffer message 类型,来指定你想如何对序列化信息进行结构化。每一个 protocol buffer message 是一个信息的小逻辑记录,包含了一系列的 name-value 对。这里有一个非常基础的 .proto 文件样例,它定义了一个包含 “APE_Proto” 相关信息的 message,随便定义了几个字段:
syntax = "proto3"; option java_package = "com.test.protobuf"; option java_outer_classname = "Photo"; message Photo { double Longitude = 1; int64 Port = 2; string PlaceCode = 3; required int32 id = 4; optional string email = 5; repeated PhoneNumber phone = 6; }正如你所见,message 格式很简单 - 每种 message 类型都有一个或多个具有唯一编号的字段,每个字段都有一个名称和一个值类型,其中值类型可以是数字(整数或浮点数),布尔值,字符串,原始字节,甚至(如上例所示)其它 protocol buffer message 类型,这意味着允许你分层次地构建数据。你可以指定 optional 字段,required 字段和 repeated 字段。 option java_package是包名,他将在编译后生成在option java_package指定的包名下 option java_outer_classname是生成的类名。 类型 string,名为 stringVal 的 optional 可选字段,字段编号为 1,此字段可出现 0 或 1 次 类型 bytes,名为 bytesVal 的 optional 可选字段,字段编号为 2,此字段可出现 0 或 1 次 类型 EmbeddedMessage(自定义的内嵌 message 类型),名为 embeddedExample1 的 optional 可选字段,字段编号为 3,此字段可出现 0 或 1 次 类型 int32,名为 repeatedInt32Val 的 repeated 可重复字段,字段编号为 4,此字段可出现 任意多次(包括 0) 类型 string,名为 repeatedStringVal 的 repeated 可重复字段,字段编号为 5,此字段可出现 任意多次(包括 0)
proto3 已舍弃 required 字段,optional 字段也无法显示使用(因为缺省默认就设置为 optional)
1,安装插件
2,添加maven依赖
<dependency> <groupId>io.protostuff</groupId> <artifactId>protostuff-runtime</artifactId> <version>1.4.0</version> </dependency> <dependency> <groupId>com.google.protobuf</groupId> <artifactId>protobuf-java</artifactId> <version>3.5.1</version> </dependency> <dependency> <groupId>io.protostuff</groupId> <artifactId>protostuff-core</artifactId> <version>1.4.0</version> </dependency>3,添加插件
<build> <extensions> <extension> <groupId>kr.motd.maven</groupId> <artifactId>os-maven-plugin</artifactId> <version>1.5.0.Final</version> </extension> </extensions> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <plugin> <groupId>org.xolstice.maven.plugins</groupId> <artifactId>protobuf-maven-plugin</artifactId> <version>0.5.0</version> <configuration> <protocArtifact> com.google.protobuf:protoc:3.1.0:exe:${os.detected.classifier} </protocArtifact> <pluginId>grpc-java</pluginId> </configuration> <executions> <execution> <goals> <goal>compile</goal> <goal>compile-custom</goal> </goals> </execution> </executions> </plugin> </plugins> </build>4,建立.proto文件 5,编写message
syntax = "proto3"; option java_package = "com.test.demo";//包名 option java_outer_classname = "MessageBuf";//类名 message Message { string msg = 1; //消息内容 int64 type = 2; //消息类型 double lon = 3; //经度 double lat = 4; //纬度 }6,编译生成Java文件 第一步: 第二步复制此java文件到指定包下使用: 复制到此处的包名要与上面包名一致,否则就要修改。 7,写我们要用的java本地对象
package com.demo.protobuf; import lombok.Data; import java.io.Serializable; /** * @author LiHaitao * @description Message: * @date 2019/11/1 15:40 **/ @Data public class Message implements Serializable { private String msg; //消息内容 private Integer type; //消息类型 private double lon; //经度 private double lat; //纬度 }8,protobuf工具类
package com.demo.protobuf; import io.protostuff.LinkedBuffer; import io.protostuff.ProtostuffIOUtil; import io.protostuff.Schema; import io.protostuff.runtime.RuntimeSchema; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.objenesis.Objenesis; import org.springframework.objenesis.ObjenesisStd; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * ProtoBufUtil 转换工具类 * */ public class ProtoBufUtil { private static Logger log = LoggerFactory.getLogger(ProtoBufUtil.class); private static Map<Class<?>, Schema<?>> cachedSchema = new ConcurrentHashMap<Class<?>, Schema<?>>(); private static Objenesis objenesis = new ObjenesisStd(true); @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; } public ProtoBufUtil() { } @SuppressWarnings({"unchecked"}) public static <T> byte[] serializer(T obj) { Class<T> cls = (Class<T>) obj.getClass(); LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE); try { Schema<T> schema = getSchema(cls); return ProtostuffIOUtil.toByteArray(obj, schema, buffer); } catch (Exception e) { log.error("protobuf序列化失败"); throw new IllegalStateException(e.getMessage(), e); } finally { buffer.clear(); } } public static <T> T deserializer(byte[] bytes, Class<T> clazz) { try { T message = (T) objenesis.newInstance(clazz); Schema<T> schema = getSchema(clazz); ProtostuffIOUtil.mergeFrom(bytes, message, schema); return message; } catch (Exception e) { log.error("protobuf反序列化失败"); throw new IllegalStateException(e.getMessage(), e); } } }9,使用练习
package com.demo.protobuf; import com.google.protobuf.InvalidProtocolBufferException; /** * @author LiHaitao * @description Test: * @date 2019/10/31 19:37 **/ public class Test { /** * message xxx { * // 字段规则:required -> 字段只能也必须出现 1 次 * // 字段规则:optional -> 字段可出现 0 次或1次 * // 字段规则:repeated -> 字段可出现任意多次(包括 0) * // 类型:int32、int64、sint32、sint64、string、32-bit .... * // 字段编号:0 ~ 536870911(除去 19000 到 19999 之间的数字) * 字段规则 类型 名称 = 字段编号; * } * * @param args */ public static void main(String[] args) throws InvalidProtocolBufferException { /** * 创建.proto文件,定义数据结构 */ Message message = new Message(); message.setLat(12.22); message.setLon(23.22); message.setMsg("你好!"); message.setType(1L); //序列化message对象,进行传输使用 byte[] serializer = ProtoBufUtil.serializer(message); //通过解析字节,转化为buf对象 MessageBuf.Message messageBuf = MessageBuf.Message.parseFrom(serializer); //再通过buf对象转化成使用对象message Message messageNow = new Message(); messageNow.setType(messageBuf.getType()); messageNow.setMsg(messageBuf.getMsg()); messageNow.setLon(messageBuf.getLon()); messageNow.setLat(messageBuf.getLat()); System.out.println(messageNow); //直接利用protobuf的工具类反序列化 Message deserializer = ProtoBufUtil.deserializer(serializer, Message.class); System.out.println(deserializer); } }