Protocol Buffer是google2008年推出的一种数据交换协议,可以使用proto文件将数据序列化或反序列化,google提供了多种语言的实现,每一种实现都包含了相应语言的编译器及库文件,由于Protocol Buffer协议使用二进制格式进行数据传输,所以它比xml和json等协议进行数据交换快。
一、安装protobuf
Mac下通过Homebrew安装protobuf
brew install protobuf
检查protobuf是否安装成功
protoc --version
---
libprotoc 3.6.1
二、Protobuf-Java工具使用
- 2.1 定义.proto文件
根据protobuf的语法定义需要序列化或反序列化的模型,保存为.proto文件,如本文定义的:TestProtobuf.proto文件:
syntax = "proto3"; // set proto syntax
option java_package = "com.xiaofan.test.protobuf";
option java_outer_classname = "TestCat";
message Cat{
int64 id = 1;
string name = 2;
int32 type = 3;
repeated string nick = 4;
}
第一行指定使用的是proto3语法,如果不指定,则默认使用proto2语法;
第二行指定生成Java工具所在的包,如果不指定,则采用默认包名;
第三行指定生成Java工具类的名称,如果不指定,则根据.proto文件的名称采用驼峰式命名方式生成;
第四行开始定义protobuf消息类型,即我们需要的数据模型,模型字段的类型是protbuf文件的类型,可参考如下protobuf类型与Java类型的对照表,每个字段都有一个唯一的数字标识符,用于在消息的二进制格式中识别各字段([1,15]的标识符占用一字节,[16,2047]的标识符占2字节)
一个.proto文件可以定义多个消息类型,.proto文件的类型与生成的Java访问类的类型对照如下:
protobuf类型 | Java类型 | 说明 |
---|---|---|
double | double | |
float | float | |
int32 | int | 使用变长编码,编码负数不够高效,如果字段可能有负值,可使用sint32替代 |
int64 | long | 使用变长编码,编码负数不够高效,如果字段可能有负值,可使用sint64替代 |
uint32 | int | 使用变长编码 |
uint64 | long | 使用变长编码 |
sint32 | int | 使用变长编码,有符号的整型,该编码对负值的处理比int32效率高 |
sint64 | long | 使用变长编码,有符号的整型,该编码对负值的处理比int64效率高 |
fixed32 | int | 总是4字节 |
fixed64 | long | 总是8字节 |
sfixed32 | int | 总是4字节 |
sfixed64 | long | 总是8字节 |
bool | boolean | |
string | String | 字符串必须是UTF-8编码或者7-bit ASCII编码的文本 |
bytes | ByteString | 可包含任意顺序的字节数据 |
可以对字段指定修饰符:
修饰符 | 含义 |
---|---|
required | 表示该字段必须要设置。proto3语法中去除了该修饰符 |
optional | 表示该字段有0个或1个值。proto3语法去除了该修饰符,如果一个字段不指定修饰符,默认是optional |
repeated | 表示该字段可以重复0次或任意多次,相当于Java中的List |
- 2.2 生成Java工具
通过定义好的.proto文件生成Java工具类,需要机遇.proto文件运行protocal buffer编译器protoc,命令如下:
protoc --proto_path=IMPORT_PATH --java_out=DST_DIR path/to/file.proto
其中: --proto_path可以简写为 -I,实际执行:
protoc --proto_path=/Users/jerry/Desktop/test/protobuf --java_out=/Users/jerry/Desktop/test/protobuf TestProtobuf.proto
or
protoc --I=/Users/jerry/Desktop/test/protobuf --java_out=/Users/jerry/Desktop/test/protobuf TestProtobuf.proto
在proto3语法下执行required修饰符会报如下错误,去除修饰符即可:
TestProtobuf.proto: Required fields are not allowed in proto3.
在proto3语法下执行optional修饰符会报如下错误,去除修饰符即可:
Explicit 'optional' labels are disallowed in the Proto3 syntax. To define 'optional' fields in Proto3, simply remove the 'optional' label, as fields are 'optional' by default.
执行成功后,Java工具会生成在–java_out指定目录下生成根据java_package
指定包名相同的文件夹下。
- 2.3 Java工具使用
生成的Java工具类如下:
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: TestProtobuf.proto
package com.xiaofan.test.protobuf;
public final class TestCat {
private TestCat() {}
public static void registerAllExtensions(
com.google.protobuf.ExtensionRegistryLite registry) {
}
public static void registerAllExtensions(
com.google.protobuf.ExtensionRegistry registry) {
registerAllExtensions(
(com.google.protobuf.ExtensionRegistryLite) registry);
}
public interface CatOrBuilder extends
...
}
...
}
调用示例:
public class TestProtobuf {
public static void main(String[] args) {
TestCat.Cat.Builder builder = TestCat.Cat.newBuilder();
builder.setId(12L);
builder.setName("Jerry");
builder.setType(1);
builder.addNick("brave");
builder.addNick("handsome");
builder.addNick("smart");
TestCat.Cat cat = builder.build();
System.out.println(cat);
try {
TestCat.Cat cat1 = TestCat.Cat.parseFrom(cat.toByteArray());
System.out.println(cat1);
} catch (InvalidProtocolBufferException e) {
e.printStackTrace();
}
}
}
---
id: 12
name: "Jerry"
type: 1
nick: "brave"
nick: "handsome"
nick: "smart"
id: 12
name: "Jerry"
type: 1
nick: "brave"
nick: "handsome"
nick: "smart"