google protocol buffer全解析------田纪原

img

解压后就能看到看到一个protoc的执行文件,即是我们所需要的编译器。

img

接着我们需要定义一份BasicUsage.proto的描述文件,其结构和我们定义普通的类十分类似。

syntax = “proto3”;

option java_package = “cn.tera.protobuf.model”;
option java_outer_classname = “BasicUsage”;

message Person {
string name = 1;
int32 id = 2;
string email = 3;
}

第一行表示所使用的的语法版本,这里选择的是最新的proto3版本。

syntax = “proto3”;

第三、四行表示最终生成的java的package名和外部class的类名(这里外部class的意思之后会有代码解释)。

option java_package = “cn.tera.protobuf.model”;
option java_outer_classname = “BasicUsage”;

之后紧接着的就是我们所定义的模型,其中大部分都是我们所熟悉的内容。

这里需要特别注意,特别注意,特别注意的是,在字段的后面都跟着一个"= X",这里并不是指这个字段的值,而是表示这个字段的“序号”,和正确地编码与解码息息相关,在我看来是protocol buffer的灵魂,之后会有详细的说明

message Person {
string name = 1;
int32 id = 2;
string email = 3;
}

有了编译器和.poto描述文件,我们就可以生成java模型文件了

编译指令

protoc -I= S R C D I R − − j a v a o u t = SRC_DIR --java_out= SRCDIRjavaout=DST_DIR $SRC_DIR/BasicUsage.proto

-I :表示工作目录,如果不指定,则就是当前目录

–java_out:表示输出.java文件的目录

这里我比较习惯将.proto文件放到java项目中,并且将.java文件直接生成到相应的package文件夹中,即前文的java_package参数,这样在使用的时候就可以不用再手动复制文件了

protoc -I=/protocol_buffer/protobuf/proto --java_out=/protocol_buffer/protobuf/src/main/java/ /protocol_buffer/protobuf/proto/BasicUsage.proto

项目的目录结构如下图,其中BasicUsage的class文件就是生成出来的

img

以上都是准备工作,接着我们就要进入代码相关部分

引入maven依赖

com.google.protobuf protobuf-java 3.9.1 com.google.protobuf protobuf-java-util 3.9.1

接着我们创建一个Test方法

/**

  • protobuf的基础使用
    */
    @Test
    void basicUse() {
    //创建一个Person对象
    BasicUsage.Person person = BasicUsage.Person.newBuilder()
    .setId(5)
    .setName(“tera”)
    .setEmail(“tera@google.com”)
    .build();
    System.out.println("Person’s name is " + person.getName());

//编码
//此时我们就可以通过我们想要的方式传递该byte数组了
byte[] bytes = person.toByteArray();

//将编码重新转换回Person对象
BasicUsage.Person clone = null;
try {
//解码
clone = BasicUsage.Person.parseFrom(bytes);
System.out.println("The clone’s name is " + clone.getName());
} catch (InvalidProtocolBufferException e) {
}

//引用是不同的
System.out.println(“==:” + (person == clone));
//equals方法经过了重写,所以equals是相同的
System.out.println(“equals:” + person.equals(clone));

//修改clone中的值
clone = clone.toBuilder().setName(“clone”).build();
System.out.println("The clone’s new name is " + clone.getName());
}

在Test方法中,我们可以看到,访问Person类是需要通过BasicUsage.Person进行访问,这就是我们前面在定义.proto文件时指定的java_outer_classname参数

因为在一个.proto文件中,我们可以定义多个类,而多个.proto文件也可以定义相同的类名,因此用这个java_outer_classname进行区分,可以认为是.proto的package名

这里需要注意几个点:

protobuf的对象的实例化和赋值必须通过newBuilder()返回的Builder对象进行,实例化最终对象需要通过build()方法。

BasicUsage.Person person = BasicUsage.Person.newBuilder()
.setId(5)
.setName(“tera”)
.setEmail(“tera@google.com”)
.build();

对象实例化完成之后就只能调用get方法而无法set,如果需要set值,则必须将其转换回Builder对象才行。

clone = clone.toBuilder().setName(“clone”).build();

而对象的编码和解码,则分别通过toByteArray()方法和parseFrom()方法 。

byte[] bytes = person.toByteArray();

BasicUsage.Person.parseFrom(bytes);

以上就是protocol buffer的基本使用方式,其实除了赋值比较麻烦意外,其他操作都很方便(如果我们需要在普通的模型中实现.setXX().setYY()这种连续操作,还得另外加个注解呢),特别是对于需要深度clone的对象,protocol buffer也是一个很好的选择,可以避免很多clone引用的问题。

4.protocol buffer模型解析

当然,了解了基础使用,源码的研究自然也是不能少的,不过遵照着循序渐进的原则,我们先看下生成的模型文件中有些什么

查看Person的类,此时的你是不是吓了一跳,这么简单的一个类的代码竟然有这么多!为了不凑字数,我这里就不贴全了,有兴趣的同学自己去生成一个看看全貌,总计836行代码

下面主要看下几个主要部分

1).BasicUsage

主类名是BasicUsage,其余所有的类都作为了该主类的内部类,所以访问Person时,需要通过BasicUsage.Person访问

public final class BasicUsage {

}

2).PersonOrBuilder接口

PersonOrBuilder接口,定义了Person对象所有字段的get方法以及其对应的字节的get方法

public interface PersonOrBui

  • 23
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值