Java-protobuf

protobuf介绍

就是一种

protobuf简单测试

项目源码见xuanfong1/springLeaning/protobuf

  1. 在项目引入maven/gradle依赖compile 'com.google.protobuf:protobuf-java:3.6.1'

  2. 下载代码生成工具,作用是将file.proto文件转换成其他语言(java/C++/GO/Python/C#/Dart)的文件,eg:这里选择window平台,版本和maven版本一致,因此选择protoc-3.6.1-win32.zip,其他操作系统选择对应平台即可,然后解压,在bin目录可以看到protoc.exe文件,复制重命名protoc-3.6.1-win32.exe为了好区分版本,其他文件用不着

  3. 编写一个测试PersonMsg.proto文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    message Person {

    // ID(必需)
    required int32 id = 1;

    // 姓名(必需)
    required string name = 2;

    // email(可选)
    optional string email = 3;

    // 朋友(集合)
    repeated string friends = 4;
    }
  4. 使用工具进行java代码生成,执行.\protobuf\protoc-3.6.1-win32.exe --java_out=.\protobuf\src\main\java\com\exxk\protobuf\ .\protobuf\src\test\protobuf\PersonMsg.proto

    注意,这里生成的代码PersonMsg.java里面是没有包名的,可以手动加入

  5. ProtobufApplicationTests.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
    53
    54
    55
    import java.io.ByteArrayInputStream;
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.util.List;


    //@RunWith(SpringRunner.class)
    //@SpringBootTest
    public class ProtobufApplicationTests {

    @Test
    public void contextLoads() {
    // 按照定义的数据结构,创建一个Person
    PersonMsg.Person.Builder personBuilder = PersonMsg.Person.newBuilder();
    personBuilder.setId(1);
    personBuilder.setName("叉叉哥");
    personBuilder.setEmail("xxg@163.com");
    personBuilder.addFriends("Friend A");
    personBuilder.addFriends("Friend B");
    PersonMsg.Person xxg = personBuilder.build();

    // 将数据写到输出流,如网络输出流,这里就用ByteArrayOutputStream来代替
    ByteArrayOutputStream output = new ByteArrayOutputStream();
    try {
    xxg.writeTo(output);
    } catch (IOException e) {
    e.printStackTrace();
    }

    // -------------- 分割线:上面是发送方,将数据序列化后发送 ---------------

    byte[] byteArray = output.toByteArray();

    // -------------- 分割线:下面是接收方,将数据接收后反序列化 ---------------

    // 接收到流并读取,如网络输入流,这里用ByteArrayInputStream来代替
    ByteArrayInputStream input = new ByteArrayInputStream(byteArray);

    // 反序列化
    PersonMsg.Person xxg2 = null;
    try {
    xxg2 = PersonMsg.Person.parseFrom(input);
    } catch (IOException e) {
    e.printStackTrace();
    }
    System.out.println("ID:" + xxg2.getId());
    System.out.println("name:" + xxg2.getName());
    System.out.println("email:" + xxg2.getEmail());
    System.out.println("friend:");
    List<String> friends = xxg2.getFriendsList();
    for(String friend : friends) {
    System.out.println(friend);
    }
    }
    }
  6. 设置自动生成包名,修改PersonMsg.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
    25
    26
    //指定编译版本2或3
    syntax = "proto2";
    //当前包名
    package PersonMsg;
    //包路径
    option java_package = "com.exxk.protobuf";
    //类名
    option java_outer_classname = "PersonMsg";

    message Person {

    // ID(必需)
    required int32 id = 1;

    // 姓名(必需)
    required string name = 2;

    // email(可选)
    optional string email = 3;

    // 朋友(集合)
    repeated string friends = 4;
    }

    message car {
    }
  7. 修改命令.\protobuf\protoc-3.6.1-win32.exe --java_out=.\protobuf\src\main\java\ .\protobuf\src\test\protobuf\PersonMsg.proto

protoc gradle插件

插件地址:google/protobuf-gradle-plugin

  1. 在父级build.gradle添加

    1
    2
    3
    4
    5
    6
    7
    8
    buildscript {
    repositories {
    mavenCentral()
    }
    dependencies {
    classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.8'
    }
    }
  2. 在子项目build.gradle添加

    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
    apply plugin: 'com.google.protobuf'

    dependencies {
    compile 'com.google.protobuf:protobuf-java:3.6.1'
    }

    sourceSets {
    main {
    proto {
    // In addition to the default 'src/main/proto'
    //proto输入目录
    srcDir 'src/main/protobuf'
    srcDir 'src/main/protocolbuffers'
    srcDir 'src/main/protocol buffers'
    // In addition to '**/*.proto' (use with caution).
    // Using an extension other than 'proto' is NOT recommended, because when
    // proto files are published along with class files, we can only tell the
    // type of a file from its extension.
    include '**/*.protodevel'
    }
    }
    test {
    proto {
    // In addition to the default 'src/test/proto'
    srcDir 'src/test/protocolbuffers'
    }
    }
    }
    protobuf {
    //输出目录
    generatedFilesBaseDir = "$projectDir/src"
    protoc {
    //protoc编译版本
    artifact = 'com.google.protobuf:protoc:3.0.0'
    }
    }
  3. 然后点击右侧gradleprotobuf->Tasks->other->generateProto编译proto文件生成java文件

protoc maven插件

解决不同平台开发编译问题,功能能实现自动根据不同系统(os/win/linux)调用不同的protoc工具

插件一os72/protoc-jar-maven-plugin

配置更改一直不生效,一直使用最新的3.6.0版本的protoc工具

插件二org.xolstice.maven.plugins/protobuf-maven-plugin

Maven工程处理Protobuf

目录结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
├─src
│ ├─main
│ │ ├─java //proto生成java文件目录
│ │ │ └─com
│ │ │ └─surelive
│ │ │ └─app
│ │ │ └─server
│ │ │ └─protocol
│ │ │ ├─request
│ │ │ └─response
│ │ └─resources
│ │ └─proto //proto文件目录
│ │ ├─request
│ │ └─response
│ └─test
│ └─java
├─pom.xml

编写pom.xml添加插件

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
<project ...>    
....
<dependencies>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>2.5.0</version>
</dependency>

</dependencies>

<build>
<defaultGoal>package</defaultGoal>
<!--识别系统类型-->
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.6.0</version>
</extension>
</extensions>
<plugins>
<!-- protobuf 编译组件 -->
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.6.1</version>
<extensions>true</extensions>
<configuration>
<!--proto源文件目录-->
<protoSourceRoot>${project.basedir}/src/main/proto</protoSourceRoot>
<!--输出目录-->
<outputDirectory>${project.basedir}/src/main/java</outputDirectory>
<!--设置是否在生成java文件之前清空outputDirectory的文件,默认值为true,设置为false时也会覆盖同名文件-->
<clearOutputDirectory>true</clearOutputDirectory>
<!--编译命令及版本,${os.detected.classifier}识别版本号,依赖os-maven-plugin插件-->
<protocArtifact>com.google.protobuf:protoc:2.5.0:exe:${os.detected.classifier}</protocArtifact>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

使用,点击右侧插件里面的protobuf->protobuf:compile或者执行mvn protobuf:compile

注意
  1. 不添加输出目录识别不了多级目录(奇怪)
  2. 设置目录protoSourceRoot目录是,是以该目录为相对路径,因此代码里面的import "response/xxx.proto要加上response二级目录,但是如果可以设置protoSourceRoot为两个或二级目录就不需要修改,clearOutputDirectory设置true,也不会清理其他目录中其他文件