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;
}

协议包含两种数据类型:PersonAddressBook。生成代码后(后续会说明),这些类将成为 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 这种二进制数据格式的描述与存储标准。我们完成了以下操作:

  1. 创建简单协议
  2. 生成符合协议的 Java 实例
  3. 使用 protobuf 进行对象序列化与反序列化

所有示例代码和代码片段可在 GitHub 项目 中找到。这是一个 Maven 项目,可以直接导入运行。


原始标题:Introduction to Google Protocol Buffer | Baeldung

» 下一篇: Javassist 介绍