Protobuf是一个灵活的、高效的用于序列化数据的协议。相比较XML和JSON格式,protobuf更小、更快、更便捷。google protobuf是跨语言的,并且自带了一个编译器(protoc),只需要用它进行编译,可以编译成Java、python、C++、C#、Go等代码,然后就可以直接使用,不需要再写其他代码,自带有解析的代码。本篇文章将简要的介绍Java程序员如何使用Protobuf。
安装Protobuf编译器
工欲善其事,必先利其器。在使用Protobuf进行开发前,需要安装Protobuf的编译器。Protobuf编译器能够将.proto描述文件解析为特定特定语言中的类。我们可以通过两种途径获取代码,一种是通过google protobuf github,另一种比较简单的做法是从Protobuf的发行版页面中下载合适版本的预构建文件,这里我们下载protobuf-java-3.3.0.tar.gz
下载完成后,进行解压安装
tar -zxvf protobuf-java-3.3.0.tar.gz
cd protobuf-java-3.3.0
./configure #进行环境监测,并生成makefile文件
make # 编译
make install # 安装
这里有两点需要注意:
1. 如果是通过github下载源码编译安装,则需要先执行./autogen.sh,这一步主要用来联网获取GoogleMock数据,并生成对应的configure脚本。脚本生成完成后,编译安装步骤和下载release包时一样;
2. 在./configure进行环境检测时,可以通过–prefix=/user/local来指定安装路径
安装完成后,可以通过–version命令来检测是否安装成功
$ protoc --version
libprotoc 3.3.0
编写proto描述文件
我们以protobuf官网java入门教程中的例子为例,编写一个addressbook.proto
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;
}
需要进行说明的几点:
1. package声明是为了防止不同项目中的命名冲突。option java_package的意思是说,如果该.proto被编译为Java类型的文件,则包名使用java_package制定的路径,否则使用默认路径
2. option java_outer_classname用来声明包含.proto中所有类的类的名字,也即最外层类的名字。如果不指定的话,默认使用.proto文件名的驼峰形式。例如my_proto.proto编译生成的类文件名为MyProto
3. message用来代表一个类,required用来代表该字段是必须的,缺少该字段将会导致解析异常;option代表可选的,可有可无;repeated代表数组,意味着可以多次出现。
关于Protobuf语法的更多介绍,猛戳这里
编译Proto描述文件
使用前面安装好的protoc工具编译addressbook.proto,命令如下:
protoc -I=$SRC_DIR --java_out=$DST_DIR $SRC_DIR/addressbook.proto
SRC_DIR用来指定源码位置,如果不指定的话,默认为当前目录,DST_DIR用来制定输出目录,如果不指定的话,默认为当前位置,–java_out用来指定生成的为java类;如果要生成C++类,则通过–cpp_out来指定。
由于我的当前工作目录已经和addressbook.proto在同一级,因此我不需要指定SRC_DIR,运行命令:
protoc --java_out=./ ./addressbook.proto
如果运行一切顺利,会在当前目录中生成com.example.tutorial/AddressBookProtos.java,这个类中包含属性的getter和setter方法,用build模式进行构造。
使用生成的Java类
public static void main(String[] args) throws IOException {
Person john = Person.newBuilder()
.setId(1234)
.setName("Hone Doe")
.setEmail("jdoe@example.com")
.addPhones(Person.PhoneNumber.newBuilder()
.setNumber("555-4321")
.setType(Person.PhoneType.HOME)
).build();
System.out.println(john.toString());
byte[] bytes = john.toByteArray();
for(int i = 0;i < bytes.length;i++){
System.out.print(Integer.toBinaryString(bytes[i]));
}
System.out.println("");
/**===============================================*/
Person person = Person.parseFrom(bytes);
System.out.println(person.toString());
}
使用build模式构建对象以后,进行序列化和反序列化的工作就变的很简单了,我们可以通过调用toByteArray方法,将对象转换为字节数组;可以通过parseFrom方法,将字节数组转换为Java对象。
这里只是一个简单的介绍,更多Protobuf对象的方法,请戳这里