参考:https://developers.google.cn/protocol-buffers/docs/javatutorial
目录
1、定义protobuf 文件
定义一个protobuf 格式的文件,addressbook.proto
syntax = "proto2";
package tutorial;
option java_package = "com.example.tutorial";
option java_outer_classname = "AddressBookProtos";
message Person {
required string name = 1;
required int32 id = 2;
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;
}
上述文件说明:
package :这有助于防止不同项目之间的命名冲突
java_package: 生成的java 类文件归属的包名.
java_outer_classname: 生成的java 类名称. 若不指定,以文件名进行命名,如: my_proto.proto 生成MyProto.java
接下来,您有了消息定义。消息只是包含一组类型化字段的聚合。许多标准的简单数据类型可以作为字段类型使用,包括bool、int32、float、double和string。您还可以通过使用其他消息类型作为字段类型来为消息添加进一步的结构——在上面的示例中,Person消息包含PhoneNumber消息,而AddressBook消息包含Person消息。您甚至可以定义嵌套在其他消息中的消息类型—如您所见,PhoneNumber类型是在Person中定义的。您还可以定义enum类型,如果您希望某个字段具有一个预定义的值列表—这里您希望指定一个电话号码可以是MOBILE、HOME或WORK中的一个。
每个元素上的“= 1”、“= 2”标记标识二进制编码中字段使用的惟一“标记”。标记号1-15编码的字节数比较高的数字少一个,因此,作为一种优化,您可以决定对常用或重复的元素使用这些标记,而对不常用的可选元素使用标记16或更高。重复字段中的每个元素都需要重新编码标记号,因此重复字段是此优化的特别好的候选项。
每个字段必须用下列修饰词之一进行注释:
required: 必须提供字段的值,否则消息将被视为“未初始化”。试图构建未初始化的消息将抛出RuntimeException。解析未初始化的消息将抛出IOException。除此之外,必填字段的行为与可选字段完全相同。
optional: 字段可以设置,也可以不设置。如果没有设置可选字段值,则使用默认值。对于简单类型,您可以指定自己的默认值,就像我们在示例中为电话号码类型所做的那样。否则,将使用系统默认值:数值类型为0,字符串为空字符串,bools 为false。对于嵌入的消息,默认值总是消息的“默认实例”或“原型”,它没有设置任何字段。调用访问器来获取未显式设置的可选(或必需)字段的值总是返回该字段的默认值。
repeated: 该字段可以重复任意次数(包括0次)。重复值的顺序将保留在协议缓冲区中。可以将重复字段看作是动态大小的数组。
2、编译你的 Protocol Buffers
1、安装protobuf 工具 (省略) 参考:protoc 2.5 安装
2、编译.proto 文件.
protoc -I=$SRC_DIR --java_out=$DST_DIR $SRC_DIR/addressbook.proto
个人是在.proto当前目录下执行的,命令如下:
protoc --java_out=d:/pro_test addressbook.proto
生成文件如下:
3、ProtoBuf API
让我们看看一些生成的代码,看看编译器为您创建了哪些类和方法。如果你看AddressBookProtos。在java中,你可以看到它定义了一个名为AddressBookProtos的类,在这个类中嵌套了你在addressbook.proto中指定的每个消息的类。每个类都有自己的Builder
类,您可以使用它来创建该类的实例。您可以在下面的生成器与消息一节中找到关于生成器的更多信息。
消息(message)和生成器(Builder)都为消息的每个字段自动生成访问器方法;消息只有getter,而构建器同时有getter和setter。下面是Person类的一些访问器(为简洁起见省略了实现):
// required string name = 1;
public boolean hasName();
public String getName();
// required int32 id = 2;
public boolean hasId();
public int getId();
// optional string email = 3;
public boolean hasEmail();
public String getEmail();
// repeated .tutorial.Person.PhoneNumber phones = 4;
public List<PhoneNumber> getPhonesList();
public int getPhonesCount();
public PhoneNumber getPhones(int index);
同时Person.Builder 有相同的getter和setter:
// required string name = 1;
public boolean hasName();
public java.lang.String getName();
public Builder setName(String value);
public Builder clearName();
// required int32 id = 2;
public boolean hasId();
public int getId();
public Builder setId(int value);
public Builder clearId();
// optional string email = 3;
public boolean hasEmail();
public String getEmail();
public Builder setEmail(String value);
public Builder clearEmail();
// repeated .tutorial.Person.PhoneNumber phones = 4;
public List<PhoneNumber> getPhonesList();
public int getPhonesCount();
public PhoneNumber getPhones(int index);
public Builder setPhones(int index, PhoneNumber value);
public Builder addPhones(PhoneNumber value);
public Builder addAllPhones(Iterable<PhoneNumber> value);
public Builder clearPhones();
可以看到,每个字段都有简单的javabean样式的getter和setter。每个奇异字段也有getter,如果该字段已设置,则返回true。最后,每个字段都有一个清除方法,将字段取消设置为空状态
重复字段有一些额外的方法——统计方法(也就是缩写列表的大小),getter和setter方法获取或设置一个特定的元素列表的索引,一个方法添加一个新元素添加到列表,以及一个addAll方法将整个容器的元素添加到列表中。
注意这些访问器方法如何使用大小写命名,即使.proto文件使用小写加下划线。这个转换是由协议缓冲区编译器自动完成的,以便生成的类与标准的Java风格约定相匹配。在.proto文件中,字段名应该始终使用带下划线的小写字母;这确保了在所有生成的语言中的良好命名实践。有关good .proto样式的更多信息,请参见样式指南。
枚举和嵌套类
生成的代码包括一个PhoneType Java 5 enum,嵌套在Person中:
public static enum PhoneType {
MOBILE(0, 0),
HOME(1, 1),
WORK(2, 2),
;
...
}
生成器和消息
协议缓冲区编译器生成的消息类都是不可变的。一旦构造了消息对象,就不能像修改Java字符串一样修改它。要构造消息,您必须首先构造一个生成器,将希望设置的任何字段设置为所选的值,然后调用生成器的build()方法。
您可能已经注意到,修改消息的构建器的每个方法都返回另一个构建器。返回的对象实际上是调用方法的相同构造器。返回它是为了方便,这样您就可以在一行代码中将几个setter连接在一起。
下面是一个如何创建Person实例的例子:
Person john =
Person.newBuilder()
.setId(1234)
.setName("John Doe")
.setEmail("jdoe@example.com")
.addPhones(
Person.PhoneNumber.newBuilder()
.setNumber("555-4321")
.setType(Person.PhoneType.HOME))
.build();