本文提供相关源码,请放心食用,详见网页侧边栏或底部,有疑问请评论或 Issue
Protobuf
是一种语言中立、平台无关、可扩展的序列化数据的格式,可用于通信协议,数据存储等。
本文将演示在 Java 语言中如何编写一个 Protobuf 的入级程序,也许你可能并不了解 Protobuf,这没有关系,基于 Protobuf 官方文档的衍生博文已经安排上了,只是限于内容较多,我正在一点点写作中,让我们先来简单实战吧!
注: 本文及后续所有关于 Protobuf 相关文章均采用 Protobuf3 版本,具体为 Protobuf 3.11.0
。
一、插件
自 IDEA 2021.3 起,增加了对 Protobuf 和 gRPC 的原生插件支持,不需要再额外安装插件。
创建一个空的 Maven 工程。借助于 Protobuf Maven 插件的功劳,使我们不必在本地搭建 Protobuf 环境。直接编辑 Pom 文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> ... <properties> <protobuf.version>3.11.0</protobuf.version> </properties>
<dependencies> <dependency> <groupId>com.google.protobuf</groupId> <artifactId>protobuf-java</artifactId> <version>${protobuf.version}</version> </dependency> </dependencies>
<build> <extensions> <extension> <groupId>kr.motd.maven</groupId> <artifactId>os-maven-plugin</artifactId> <version>1.6.2</version> </extension> </extensions> <plugins> <plugin> <groupId>org.xolstice.maven.plugins</groupId> <artifactId>protobuf-maven-plugin</artifactId> <version>0.6.1</version> <configuration> <protocArtifact>com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier}</protocArtifact> </configuration> </plugin> </plugins> </build> </project>
|
首先我们引入了 protobuf-java
的 Java 包依赖,然后引入了两个 Maven 插件:
os-maven-plugin
获取当前运行环境的系统信息,作为辅助插件。
protobuf-maven-plugin
负责将 protobuf 文件转换为 Java 类。<protocArtifact>
内容是 protobuf 转化为 Java 类的执行文件的配置信息,由于 ${os.detected.classifier}
可以自动识别当前环境的操作系统信息,自动适配不同操作系统,因此无需修改。
二、Protobuf 源文件
protobuf-maven-plugin
插件默认会扫描 src/main/proto
下的 protobuf 文件,因此我们需要将 protobuf 文件放置在该目录下。如果你想放置在其他地方,请手动修改 maven 插件的 <protoSourceRoot>
属性。
首先创建一个关于性别的 Protobuf 枚举类 enums.proto
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| syntax = "proto3";
option java_package = "com.github.jitwxs.sample.protobuf"; option java_outer_classname = "EnumMessageProto";
enum SexEnum { DEFAULT_SEX = 0; MALE = 1; FEMALE = 2; }
enum SubjectEnum { DEFAULT_SUBJECT = 0; CHINESE = 1; MATH = 2; ENGLISH = 3; }
|
syntax = "proto3";
:指定使用 Protobuf3 进行编译,不写默认使用 Protobuf2 编译。
option java_package
:指定生成 Java 类后的所属包。
option java_outer_classname = "EnumMessageProto";
:指定生成 Java 类后的类名。
关于枚举类,有以下几点说明:
- 枚举值序号必须从 0 开始。
- 序号为 0 的枚举值必须是第一个元素。
- 为 0 的枚举值是该枚举类的默认值。
然后再创建一个用户 Protobuf 类 user.proto
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| syntax = "proto3";
import "enums.proto";
option java_package = "com.github.jitwxs.sample.protobuf"; option java_outer_classname = "MessageProto";
message User { int32 age = 1; string name = 2; SexEnum sex = 3; map<int32, GradeInfo> grade = 4; repeated ParentUser parent = 5; }
message GradeInfo { double score = 1; int32 rank = 2; }
message ParentUser { string relation = 1; string tel = 2; }
|
import "enums.proto";
表示将 enums.proto 类引入到当前文件中,以便调用。
age
:年龄数据类型为 int32,默认值为 0,在 Java 中是 int 类型。
name
:姓名数据类型是 string,默认值为空串,在 Java 中数据类型是 String 类型。
sex
:性别数据类型为 SexEnum 类型,默认值为 INVALID。
parent
: 父母的信息数据,类型为 List。
三、生成 Java 类
在 IDEA 中,双击 Maven Protobuf 插件的 protobuf:complie
选项,在项目的 target 文件夹中就会生成 protobuf 文件对应的 Java 类,如下图所示。
四、运行
编写一个序列号和反序列化的测试类测试下,测试通过。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
|
public class ByteConvertTest { @Test public void testSerialize() { final MessageProto.User user = randomUser();
byte[] byteArray = user.toByteArray();
System.out.println(Arrays.toString(byteArray)); }
@Test public void testDeserialize() throws InvalidProtocolBufferException { final MessageProto.User user1 = randomUser();
byte[] byteArray = user1.toByteArray();
MessageProto.User user2 = MessageProto.User.parseFrom(byteArray);
assertEquals(user1, user2); } }
private MessageProto.User randomUser() { final Map<Integer, MessageProto.GradeInfo> gradeInfoMap = new HashMap<>();
for (EnumMessageProto.SubjectEnum subjectEnum : EnumMessageProto.SubjectEnum.values()) { if (subjectEnum == EnumMessageProto.SubjectEnum.DEFAULT_SUBJECT || subjectEnum == EnumMessageProto.SubjectEnum.UNRECOGNIZED) { continue; }
gradeInfoMap.put(subjectEnum.getNumber(), MessageProto.GradeInfo.newBuilder() .setScore(RandomUtils.nextDouble(0, 100)) .setRank(RandomUtils.nextInt(1, 50)) .build()); }
final List<MessageProto.ParentUser> parentUserList = Arrays.asList( MessageProto.ParentUser.newBuilder().setRelation("father").setTel(RandomStringUtils.randomNumeric(13)).build(), MessageProto.ParentUser.newBuilder().setRelation("mother").setTel(RandomStringUtils.randomNumeric(13)).build() );
return MessageProto.User.newBuilder() .setName(RandomStringUtils.randomAlphabetic(5)) .setAge(RandomUtils.nextInt(1, 80)) .setSex(EnumMessageProto.SexEnum.forNumber(RandomUtils.nextInt(1, 2))) .putAllGrade(gradeInfoMap) .addAllParent(parentUserList) .build(); }
|
以上就是 protobuf 的 hello-wrold 程序,限于篇幅还有许多其他的功能在代码仓库中,请自行下载后结合单测进行深入学习,详见仓库中 README 描述。