Google Protocol Buffer 简介
1. 概述
在本文中,我们将研究Google 协议缓冲区 (protobuf)——一种众所周知的与语言无关的二进制数据格式。我们可以使用协议定义一个文件,然后使用该协议,我们可以生成 Java、C++、C#、Go 或 Python 等语言的代码。
这是格式本身的介绍性文章;如果您想了解如何在 Spring Web 应用程序中使用该格式,请查看这篇文章 。
2. 定义 Maven 依赖
要使用协议缓冲区是 Java,我们需要向protobuf-java 添加一个 Maven 依赖项:
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>${protobuf.version}</version>
</dependency>
<properties>
<protobuf.version>3.2.0</protobuf.version>
</properties>
3. 定义协议
让我们从一个例子开始。我们可以用 protobuf 格式定义一个非常简单的协议:
message Person {
required string name = 1;
}
这是一个简单的Person类型消息的协议,它只有一个必填字段 - 具有string类型的名称。
让我们看一下定义协议的更复杂的例子。假设我们需要以 protobuf 格式存储人员详细信息: 包protobuf;
package protobuf;
option java_package = "com.blogdemo.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类中的内部类。
当我们想要定义一个必需的字段时——这意味着创建一个没有该字段的对象会导致Exception,我们需要使用required关键字。
使用optional关键字创建字段意味着不需要设置该字段。repeated关键字是可变大小的数组类型。
所有字段都被索引——用数字 1 表示的字段将被保存为二进制文件中的第一个字段。标记为 2 的字段将被保存,依此类推。这使我们能够更好地控制字段在内存中的布局方式。
4. 从 Protobuf 文件生成 Java 代码
一旦我们定义了一个文件,我们就可以从中生成代码。
首先,我们需要在我们的机器上安装 protobuf 。完成此操作后,我们可以通过执行protoc命令生成代码:
protoc -I=. --java_out=. addressbook.proto
protoc命令将从我们的addressbook.proto文件生成 Java 输出文件。-I选项指定proto文件所在的目录。java-out指定将在其中创建生成的类的目录。
生成的类将具有我们定义的消息的设置器、获取器、构造器和构建器。它还将有一些实用方法用于保存 protobuf 文件并将它们从二进制格式反序列化为 Java 类。
5. 创建 Protobuf 定义消息的实例
我们可以轻松地使用生成的代码来创建Person类的 Java 实例:
String email = "blogdemo@itcodingman.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类的一个实例并向该对象添加一个人。
接下来,我们想将该文件保存在磁盘上——在自动生成的代码中有一个writeTo() 方法,我们可以使用它:
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);