最近工作中使用到protobuffer,查了很多资料,这篇有用,转发收藏,留着以后查阅
本文转自:https://my.oschina.net/pierrecai/blog/873359
编码/解码在Java中又称序列化/反序列化,Java本身的序列化反序列化技术生成的二进制码流太大,且转化效率低下,一般不适用于远程跨节点调用的编码框架。
Protobuf全称Protocol Buffers,由谷歌开源而来,特点如下:
- 码流小、效率高
- 语言平台无关,不只Java可以用,C++、python亦可
- 使用数据描述文件,可自动生成代码
- 和Facebook Thirft对比,Protobuf并不需要完全在开始就完全定义好全部结构,而可以在后期extends之前的结构,并且仍然可以读取之前结构编码的信息
使用步骤:
- 编写.proto类定义文件
- 通过提供的编译器产生对应的Java类
- 使用提供的API读写信息
1、编写.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;
}
<h3>文件说明:</h3>
<ol>
<li>package用于区分同名文件,在java_package缺省时,就是产生的bean的包</li>
- java_outer_classname指定包含文件中定义的所有类的类,如果缺省,会自动将.proto文件名设为类名
- message定义一个类,message中可以包含message
- 每个属性后面的 =1、=2等数字是在二进制化数据时,属性的“唯一标识”,在每个message中独立编号
-
支持的修饰符:
- required:标识必须被提供的字段,如果required字段的值没有被提供,编码解码时都会跑出异常;
- optional:标识可以提供或不提供的字段,如果不提供,则返回默认值。默认值和java中相似,但是“子message”的字段会全部为空。默认值也可以用[default = value]进行设置;
- repeated:标识ArrayList
- 每个属性后面的 =1、=2等数字,注意0-15比之后的数字再序列化时少一个字节,固0-15一般用在required和repeated字段上
支持的数据类型:
bool
,int32
,float
,double,
string
,enum
.proto Type Notes C++ Type Java Type Python Type Go Type double double double float *float64 float float float float *float32 int32 虽然可以有负数,但是效率不高 int32 int int *int32 int64 虽然可以有负数,但是效率不高 int64 long int/long[3] *int64 uint32 无符号整数,只能是正数 uint32 int int/long[3] *uint32 uint64 无符号整数,只能是正数 uint64 long int/long[3] *uint64 sint32 有符号整数,表示负数的效率更高 int32 int int *int32 sint64 有符号整数,表示负数的效率更高 int64 long int/long[3] *int64 fixed32 永远占据4个字节,如果超过256的话,效率更高 uint32 int int *uint32 fixed64 永远占据8个字节,如果超过256的话,效率更高 uint64 long int/long[3] *uint64 sfixed32 永远占据4个字节 int32 int int *int32 sfixed64 永远占据8个字节 int64 long int/long[3] *int64 bool bool boolean bool *bool string 字符串必须是UTF-8或者7-bit ASCII。 string String str/unicode *string bytes string ByteString str []byte 备注:
[3] Python3中不再有int和long之分,而只有不限长度的整型int。
2、通过提供的编译器产生对应的Java类
2.1、将protoc.exe和对应的.proto文件放到项目所在的java文件夹下
protoc.exe --java_out=.\ .\Request.proto
就可以在上面的proto文件中的java_package描述的位置产生指定的文件
3、使用提供的API读写信息
public class TestSubscribeReqProto { /** * 1、构建对象 * @return */ public static SubscribeReqProto.SubscribeReq createSubscribeReq(){ SubscribeReqProto.SubscribeReq.Builder builder = SubscribeReqProto.SubscribeReq.newBuilder(); builder.setSubReqID(1); builder.setUserName("caizhijie"); builder.setProductName("Yellow Book"); List<String> address = new ArrayList<>(); address.add("NanJing YuHuaTai"); address.add("BeiJing ZhiJinCheng"); address.add("ShenZhen HongShuLin"); builder.addAllAddress(address); return builder.build(); } /** * 2、编码 * @param req * @return */ public static byte[] encode(SubscribeReqProto.SubscribeReq req){ return req.toByteArray(); } /** * 3、解码 * @param body * @return * @throws InvalidProtocolBufferException */ public static SubscribeReqProto.SubscribeReq decode(byte[] body) throws InvalidProtocolBufferException { return SubscribeReqProto.SubscribeReq.parseFrom(body); } public static void main(String[] args) throws InvalidProtocolBufferException { SubscribeReqProto.SubscribeReq req = createSubscribeReq(); System.out.println("Before encode : "+req.toString()); SubscribeReqProto.SubscribeReq req2 = decode(encode(req)); System.out.println("After encode : "+req2.toString()); System.out.println("Assert equal : -->" + req2.equals(req)); } }