1. 概述
本文将介绍 Google Protocol Buffer(简称 protobuf)—— 一种著名的语言无关二进制数据格式。我们可以通过定义协议文件,然后基于该协议生成 Java、C++、C#、Go 或 Python 等语言的代码。
⚠️ 本文是 protobuf 格式本身的入门教程,如果需要了解如何在 Spring Web 应用中使用该格式,请参考这篇文章。
2. 定义 Maven 依赖
在 Java 中使用 protobuf,需要添加 protobuf-java 的 Maven 依赖:
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>${protobuf.version}</version>
</dependency>
<properties>
<protobuf.version>3.2.0</version>
</properties>
3. 定义协议
先看一个简单示例。我们可以用 protobuf 格式定义一个非常基础的协议:
message Person {
required string name = 1;
}
这个协议定义了一个 Person
类型的消息,仅包含一个必填字段 name
(string 类型)。
再看一个更复杂的协议示例,假设我们需要存储人员详细信息:
package protobuf;
option java_package = "com.baeldung.protobuf";
option java_outer_classname = "AddressBookProtos";
message Person {
required string name = 1;
required int32 id = 2;
optional string email = 3;
repeated string numbers = 4;
}
message AddressBook {
repeated Person people = 1;
}
协议包含两种数据类型:Person
和 AddressBook
。生成代码后(后续会说明),这些类将成为 AddressBookProtos
类的内部类。
关键字说明:
- ✅
required
:必填字段,创建对象时未设置会抛出异常 - ✅
optional
:可选字段,无需设置 - ✅
repeated
:可变长度的数组类型
所有字段都带索引(如 =1
)。索引为 1 的字段会保存在二进制文件的第一个位置,索引为 2 的字段紧随其后,以此类推。这让我们能更好地控制字段在内存中的布局。
4. 从 Protobuf 文件生成 Java 代码
定义好协议文件后,就可以生成代码了。
首先需要在机器上 安装 protobuf。安装完成后,通过执行 protoc
命令生成代码:
protoc -I=. --java_out=. addressbook.proto
protoc
命令会根据 addressbook.proto
文件生成 Java 输出文件。参数说明:
-I
:指定.proto
文件所在目录--java_out
:指定生成类的输出目录
生成的类将包含:
- 消息的 setter/getter 方法
- 构造器和构建器
- 序列化/反序列化的工具方法
5. 创建 Protobuf 消息实例
使用生成的代码创建 Person
实例非常简单:
String email = "michael.program@example.com";
int id = new Random().nextInt();
String name = "Michael Program";
String number = "01234567890";
AddressBookProtos.Person person =
AddressBookProtos.Person.newBuilder()
.setId(id)
.setName(name)
.setEmail(email)
.addNumbers(number)
.build();
assertEquals(person.getEmail(), email);
assertEquals(person.getId(), id);
assertEquals(person.getName(), name);
assertEquals(person.getNumbers(0), number);
通过 newBuilder()
方法创建流式构建器,设置所有必填字段后调用 build()
方法即可创建 Person
实例。
6. 序列化与反序列化 Protobuf
创建 Person
实例后,我们通常需要将其序列化为二进制格式保存到磁盘。假设我们创建一个 AddressBook
实例并添加人员信息:
AddressBookProtos.AddressBook addressBook
= AddressBookProtos.AddressBook.newBuilder().addPeople(person).build();
FileOutputStream fos = new FileOutputStream(filePath);
addressBook.writeTo(fos);
执行后,对象会被序列化为二进制格式并保存到磁盘。要从磁盘加载数据并反序列化为 AddressBook
对象,使用 mergeFrom()
方法:
AddressBookProtos.AddressBook deserialized
= AddressBookProtos.AddressBook.newBuilder()
.mergeFrom(new FileInputStream(filePath)).build();
assertEquals(deserialized.getPeople(0).getEmail(), email);
assertEquals(deserialized.getPeople(0).getId(), id);
assertEquals(deserialized.getPeople(0).getName(), name);
assertEquals(deserialized.getPeople(0).getNumbers(0), number);
7. 总结
本文介绍了 Google Protocol Buffer 这种二进制数据格式的描述与存储标准。我们完成了以下操作:
- 创建简单协议
- 生成符合协议的 Java 实例
- 使用 protobuf 进行对象序列化与反序列化
所有示例代码和代码片段可在 GitHub 项目 中找到。这是一个 Maven 项目,可以直接导入运行。