背景:
项目中看到有同事用到的protobuf,本着学习的精神来看一下这是什么?为什么使用它。先从官网了解了一下:https://developers.google.com/protocol-buffers
是什么?
Google推出的一个序列化的协议。通过定义一个.proto的数据结构化的文件,编译生成一个class,class会暴露getset等方法,方法里面通过二进制的方式进行序列化。
为什么使用它?
现在我自己经常用到的序列化也就是这几种,Serializable、自定义的序列化协议(公司的自研rpc用到)、JSON、XML。那Google的protobuf和他们比有什么优势呢?
- Serializable
java中自带的序列化方式,会将数据序列化为二进制的数据。
优点:操作简单
缺点:《effective Java》中提到了几点:1 序列化时要显示指定版本号,否则类结构变化时会出现反序列化失败 2 反序列化创建对象时,并没有调用构造函数,可能破坏对象内部的依赖关系3 未完
- JSON
JSON在平时中很常用,通过fastJson或者jackson来进行序列化和反序列化。使用简单。protobuf的官网没有和json做对比,过会做一下比较。
- XML
优点:易读,跨平台
缺点:占用空间大,转换与解析性能不高,xml的dom树也比较复杂
- 自定义序列化方式
优点:可根据业务灵活实现
缺点:实现复杂
- protobuf
优点:效率高
缺点:需要安装protoc,略微复杂
语法:
从官网贴了一个例子来解释一下:
syntax = "proto2";
package tutorial;
option java_package = "com.example.tutorial";
option java_outer_classname = "AddressBookProtos";
message Person {
optional string name = 1;
optional int32 id = 2;
optional string email = 3;
enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}
message PhoneNumber {
optional string number = 1;
optional PhoneType type = 2 [default = HOME];
}
repeated PhoneNumber phones = 4;
}
message AddressBook {
repeated Person people = 1;
}
syntax:定义这个proto文件的版本,有proto2与proto3;附proto2与proto3的语法文档,proto2:https://developers.google.com/protocol-buffers/docs/proto proto3:https://developers.google.com/protocol-buffers/docs/proto3
package: 定义这个proto文件的坐标,类似于java的package,保证该文件的唯一。在java中,由于定义了java_package的属性,所以可以省略。但是建议保留,因为其他语言中没有java_package的属性。
java_package:定义了生成的class所在的包。在java中如果没有定义该属性的话,会使用package的值
message: 类似于java的类名,里面定义了一些属性,基础的类型有bool
, int32
, float
, double
, string,也可以定义内部的类型,枚举,例如上面的PhoneType以及PhoneNumber。 =1,=2后面作为字段的id,必须唯一。作为优化,可以将常用的字段放到id为1~15的区间(为什么呢)。每个字段必须被
optional/repeated/required标记:
- optional: 值可为空,可以通过 [detault = 值] 来设置默认值,类似于上面例子的PhoneType。对于基本类型,系统会自动设置默认值,数值类型默认值为0,string为空字符串,boolean为false
- repeated: 数组/列表,类似于list,可以对其进行add/remove等操作。注:操作时通过.add方法来修改,不要通过调用getList后再对其修改,因为getList返回的是UnmodifiableLazyStringList类型,该list不能对其修改结构(add/remove/set等操作),否则会抛异常。
//属性名是scores,通过.addScoures来进行添加,而不是下面的model.getScoresList
model.addScores("FDs");
model.addScores("FDs");
model.addScores("FDs");
ProtocolStringList listsList = model.getScoresList();
listsList.add("会抛异常");
-
required:必填,否则会抛出异常。该属性要谨慎设置。除了人为未设置导致的问题之外,可能因为程序运行异常导致未知的问题,所以尽量不使用该属性。protobuf在proto3版本中已经完全废弃该属性。
使用:
生成java类
通过protoc将.proto生成java文件,每个java文件中包含一个builder,通过此builder进行java对象的创建。需要下载protoc。可以通过brew install protobuf来下载,也可以直接从官网下载:https://github.com/protocolbuffers/protobuf/releases/tag/v3.15.7 。下载之后进行java类的生成,生成java文件有两种方式:
- 通过命令来生成
protoc --java_out=/Users/edz/Desktop/aaaa ./WTableUserBaseInfo1.proto --java_out表示java类的输出目录, ./WTableUserBaseInfo1.proto表示要生成java类的.proto文件。
- 通过idea+maven来进行生成
1 在scr/main下面新建proto目录,将.proto文件放入该目录下;
2 maven中加入proto的插件,其中protoSourceRoot为.proto文件的目录,protocExecutable为安装的protoc的目录
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.5.0</version>
<configuration>
<protoSourceRoot>/Users/edz/IdeaProjects/58/jxedt_scf_jxedtuserscf/service/src/main/proto</protoSourceRoot>
<protocExecutable>/Users/edz/Downloads/protoc-3.15.6-osx-x86_64/bin/protoc</protocExecutable>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
3 通过protobuf:compile进行文件的生成
创建对象及使用:
通过newBuilder来进行对象的创建。
AddressBookProtos.Person.Builder person = AddressBookProtos.Person.newBuilder();
person.setEmail("FDaf");
AddressBookProtos.Person build = person.build();
System.out.println(build.getEmail());
性能对比:
未完待续:
附一下测试的代码:https://download.csdn.net/download/qq_38008721/16580092