Protobuf1 是一种语言中立、平台无关、可扩展的序列化数据的格式,可用于通信协议,数据存储等。

本文将演示在 Java 语言中如何编写一个 Protobuf 的入级程序,也许你可能并不了解 Protobuf,这没有关系,基于 Protobuf 官方文档的衍生博文已经安排上了,只是限于内容较多,我正在一点点写作中,让我们先来简单实战吧!

注: 本文及后续所有关于 Protobuf 相关文章均采用 Protobuf3 版本,具体为 Protobuf 3.11.0


一、插件

工欲善其事,必先利其器!让我们先在全宇宙第一的 Java IDE 中安装上 Protobuf 的插件。在插件市场中搜索并安装 Protobuf Support,或下载离线插件包手动安装: https://plugins.jetbrains.com/plugin/8277-protobuf-support/ 。

protobuf编译器生成java源码 protobuf-java_protobuf编译器生成java源码

安装完毕后,创建一个空的 Maven 工程。借助于 Protobuf Maven 插件的功劳,使我们不必在本地搭建 Protobuf 环境。直接编辑 Pom 文件:

<?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-plugin2
  • protobuf-maven-plugin 负责将 protobuf 文件转换为 Java 类。<protocArtifact> 内容是 protobuf 转化为 Java 类的执行文件的配置信息,由于 ${os.detected.classifier} 可以自动识别当前环境的操作系统信息,自动适配不同操作系统,因此无需修改。

二、Protobuf 源文件

protobuf-maven-plugin 插件默认会扫描 src/main/proto 下的 protobuf 文件,因此我们需要将 protobuf 文件放置在该目录下。如果你想放置在其他地方,请手动修改 maven 插件的 <protoSourceRoot>3

首先创建一个关于性别的 Protobuf 枚举类 sex_enum.proto,用于标识男女:

syntax = "proto3";
option java_package = "jit.wxs.demo.enums";
option java_outer_classname = "SexEnumProto";

enum SexEnum {
    INVALID = 0;
    MALE = 1;
    FEMALE = 2;
}
  • syntax = "proto3";:指定使用 Protobuf3 进行编译,不写默认使用 Protobuf2 编译。
  • option java_package:指定生成 Java 类后的所属包。
  • option java_outer_classname = "SexEnumProto";:指定生成 Java 类后的类名。

关于枚举类,有以下几点说明:

  • 枚举值序号必须从 0 开始。
  • 序号为 0 的枚举值必须是第一个元素。
  • 为 0 的枚举值是该枚举类的默认值。

然后再创建一个用户 Protobuf 类 user.proto

syntax = "proto3";
option java_package = "jit.wxs.demo.dto";
option java_outer_classname = "UserProto";

import "sex_enum.proto";

message User {
    int32 age = 1;
    string name = 2;
    SexEnum sex = 3;
}
  • import "sex_enum.proto"; 表示将 sex_enum.proto 类引入到当前文件中,以便调用。
  • age:年龄数据类型为 int32,默认值为 0,在 Java 中是 int 类型。
  • name:姓名数据类型是 string,默认值为空串,在 Java 中数据类型是 String 类型。
  • sex:性别数据类型为 SexEnum 类型,默认值为 INVALID。

目录结构如下:

protobuf编译器生成java源码 protobuf-java_Java_02

三、生成 Java 类

在 IDEA 中,双击 Maven Protobuf 插件的 protobuf:complie选项,在项目的 target 文件夹中就会生成 protobuf 文件对应的 Java 类,如下图所示。

protobuf编译器生成java源码 protobuf-java_protobuf_03

四、运行

将生成的两个 Java 类移动(不要复制,否则运行会报文件名重复错误)到项目的 java 目录下(保持 package 的正确性),然后编写一个测试类测试下。

package jit.wxs.demo.test;

import com.google.protobuf.InvalidProtocolBufferException;
import jit.wxs.demo.dto.UserProto;
import jit.wxs.demo.enums.SexEnumProto;

import java.util.Arrays;

/**
 * 测试 ProtoBuf 序列化与反序列化
 * @author jitwxs
 * @date 2019年12月20日 1:22
 */
public class ProtoBufTest {
    public static void main(String[] args) {
        UserProto.User.Builder builder =  UserProto.User.newBuilder();
        builder.setName("zhangsan");
        builder.setAge(18);
        builder.setSex(SexEnumProto.SexEnum.MALE);

        UserProto.User user = builder.build();
        System.out.println("before: " + user.toString());

        byte[] byteArray = user.toByteArray();
        System.out.println("=======");
        System.out.println("User Byte: " + Arrays.toString(byteArray));
        System.out.println("=======");

        try {
            UserProto.User user2 = UserProto.User.parseFrom(byteArray);
            System.out.println("after: " + user2.toString());
        } catch (InvalidProtocolBufferException e) {
            e.printStackTrace();
        }
    }
}

运行结果如下:

before: age: 18
name: "zhangsan"
sex: MALE

=======
User Byte: [8, 18, 18, 8, 122, 104, 97, 110, 103, 115, 97, 110, 24, 1]
=======
after: age: 18
name: "zhangsan"
sex: MALE

最终项目的目录结构如下:

protobuf编译器生成java源码 protobuf-java_protobuf编译器生成java源码_04