java protobuf入门

protobuf入门


为什么用protobuf

怎么序列化和反序列化一个数据结构

  1. java的自带序列化和反序列化,但是不能在不同的编程语言系统之间进行传输
  2. 可以用简单的string表示,比如“name:csdn”,但是不能描述复杂的数据结构
  3. 也可以使用xml,xml为很多语言提供了工具,不需要重新开发;但是xml读取一个属性需要遍历整个dom树,这会耗费大量性能,以至于使读取属性变得复杂。

protobuf 可以很好的解决上述问题,它需要定义.proto文件来描述数据结构

.proto 文件 定义

在src/main/proto (默认路径) 新建addressbook.proto 文件


syntax = "proto2";
//syntax 版本,不写默认proto2,目前有proto2 和proto3,必须位于第一行,不能有注释
//包名,和java的包名一样为了防止类的name冲突,如果你明确指定了包名,也建议放在一个正常的java包里,防止在不同系统间 类名冲突
package tutorial;
//指定了自动生成的类放在哪个包里,如果没有指定将会用 上面的package
option java_package = "com.example.tutorial";
//如果不指定默认叫做 MyProto.java
option java_outer_classname = "AddressBookProtos";
//message可以嵌套,像java的内部类
message Person {
// ‘=1’ 是在二进制文件中的唯一标识,0-15比16以上的少一个字节,在同一个message中相同字段名可以用同一个标识
//required 表示这个字段必须被赋值,否则将不能进行初始化,没有赋值初始化会报RuntimeException, 反序列化会报IOException,其它的和option一样,慎用requeried, 如果一个字段需要变成option,则所有的序列化和反序列都需要变化   注意:在proto3版本中 required,optional 已取消
  required string name = 1;  
  required int32 id = 2;
  //option 可以不被赋值,不被赋值将会使用默认值,数据的默认值是0,boolean的是false,string的是空字符串,嵌入message的默认值是 "default instance" 或者"prototype"
  optional string email = 3;

  enum PhoneType {
    MOBILE = 0;
    HOME = 1;
    WORK = 2;
  }

  message PhoneNumber {
    required string number = 1;
    optional PhoneType type = 2 [default = HOME];
  }
//可以被使用多次,在序列化时会被当成一个动态数组
  repeated PhoneNumber phones = 4;
}

message AddressBook {
  repeated Person people = 1;
}

.proto转换为java bean

使用命令转换

下载系统对应的版本(window可以不安装)https://github.com/protocolbuffers/protobuf/releases/tag/v3.9.1

C:\Users\46133\Downloads\protoc-3.9.1-win64\bin\protoc.exe  -I=E:\idea_study\src\main\proto\ --java_out=E:\idea_study\src\main\ E:\idea_study\src\main\proto\addressbook.proto

C:\Users\46133\Downloads\protoc-3.9.1-win64\bin\protoc.exe 下载解压的文件
-I=E:\idea_study\src\main\proto\ proto文件所在的文件夹
–java_out=E:\idea_study\src\main\ 输出java 类文件到哪个目录
E:\idea_study\src\main\proto\addressbook.proto 编辑哪个proto的文件

使用idea插件转换

  1. idea 安装Protobuf Support 插件链接
    注意:把.proto文件转换为javaBean时 ,.proto文件的默认包名是src/main/proto
  2. 添加maven依赖
 <properties>
       <grpc.version>1.6.1</grpc.version>
       <protobuf.version>3.3.0</protobuf.version>
   </properties>

<dependencies>
       <dependency>
           <groupId>io.grpc</groupId>
           <artifactId>grpc-netty</artifactId>
           <version>${grpc.version}</version>
           <scope>provided</scope>
       </dependency>
       <dependency>
           <groupId>io.grpc</groupId>
           <artifactId>grpc-protobuf</artifactId>
           <version>${grpc.version}</version>
           <scope>provided</scope>
       </dependency>
       <dependency>
           <groupId>io.grpc</groupId>
           <artifactId>grpc-stub</artifactId>
           <version>${grpc.version}</version>
           <scope>provided</scope>
       </dependency>
       <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.5.0.Final</version>
           </extension>
       </extensions>
       <plugins>

           <plugin>
               <groupId>org.xolstice.maven.plugins</groupId>
               <artifactId>protobuf-maven-plugin</artifactId>
               <version>0.5.0</version>
               <configuration>
                   <protocArtifact>com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier}
                   </protocArtifact>
                   <pluginId>grpc-java</pluginId>
                   <pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}
                   </pluginArtifact>
               </configuration>
               <executions>
                   <execution>
                       <goals>
                           <goal>compile</goal>
                           <goal>compile-custom</goal>
                       </goals>
                   </execution>
               </executions>
           </plugin>            
       </plugins>
   </build>
  1. idea编译一下项目(或 mvn compile 命令),会把默认路径 src/main/proto下面的.proto文件生成java 类

  2. 生成的java 包、类名对应 addressbook.proto 中的

option java_package = "com.example.tutorial";  //包名
option java_outer_classname = "AddressBookProtos"; //类名
  1. 生成的类有对应的get 和set 方法,也有builder,一些常用的api
 AddressBookProtos.Person john =
                AddressBookProtos.Person.newBuilder()
                        .setId(1234)
                        .setName("John Doe")
                        .setEmail("jdoe@example.com")
                        .addPhones(
                                AddressBookProtos.Person.PhoneNumber.newBuilder()
                                        .setNumber("555-4321")
                                        .setType(AddressBookProtos.Person.PhoneType.HOME))
                        .build();

        System.out.println(john.getPhonesCount());
api
  1. 标准api

  2. isInitialized() 查看所有的required 字段是否被赋值

  3. toString() 转换成可读性比较友好的字符串,debug时可以用

  4. mergeFrom(Message other) 合并另一个message,如果是字段非集合就覆盖,集合就合并,repeated就关联,(只有在builder中才能使用)

  5. clear() 清空该字段

  6. 序列化API

  7. byte[] toByteArray(); 转换为byte[]

  8. static Person parseFrom(byte[] data); byte[] 转换为对象

  9. void writeTo(OutputStream output) 序列化对象到输出流

  10. static Person parseFrom(InputStream input) 从输入流读取对象

  11. 输入到文件

 AddressBookProtos.Person john =
               AddressBookProtos.Person.newBuilder()
                       .setId(1234)
                       .setName("John Doe")
                       .setEmail("jdoe@example.com")
                       .addPhones(
                               AddressBookProtos.Person.PhoneNumber.newBuilder()
                                       .setNumber("555-4321")
                                       .setType(AddressBookProtos.Person.PhoneType.HOME))
                       .build();

       String filePath = System.getProperty("user.dir") + File.separator + "protobuf";
       FileOutputStream output = new FileOutputStream(filePath);
       john.writeTo(output);
       output.close();
       System.out.println("输出文件流到" + filePath);
  1. 从文件读取 (读取上面输出到文件的)
 String filePath = System.getProperty("user.dir") + File.separator + "protobuf";    
FileInputStream inputStream = new FileInputStream(filePath);
Person person = Person.newBuilder().mergeFrom(inputStream).build();
System.out.println(person.toString());

probobuf扩展

有一天你的类需要进行修改,调用方不修改类的情况下,一定要遵守以下几条

  1. 不能改变已有字段 的tag number
  2. 不能增加和删除 required 字段
  3. 可以删除optional or repeated fields.
  4. 可以增加optional or repeated fields ,但是必须使用新的tag number,(从来没有使用过的,包括删除的字段,建议删除字段不要删除,注释掉)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

走着路睡觉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值