Google Protobuf Java API详解
阅读数:479 评论数:0
跳转到新版页面分类
python/Java
正文
0. 入门介绍
Protobuf有两个大版本,proto2和proto3,proto3相对proto2而言,简言之就是支持更多的语言(Ruby、C#等 ),删除了一些复杂的语法和特性,引入了更多的约定等 。
凡事皆有双面性,XML、JSON更注重数据结构化,关注人类可读性和语义表达能力,Protobuf更注重数据序列化,关注效率、空间、速度,人类可读性差,语义表达能力不足,所以我们需要借助.proto定义数据结构的文件来更友好的使用Protobuf。
在idea中可以安装Protobuf相关插件来实现.proto文件转java类。
-----------------下面是proto3---------------------------------------------------------------------------
1、编写proto文件
定义一个JetProtos.proto文件
syntax = "proto3"; // PB协议版本
import "google/protobuf/any.proto"; // 引用外部的message,可以是本地的,也可以是此处比较特殊的 Any
package jet.protobuf; // 包名,其他 proto 在引用此 proto 的时候使用
// 注意:和下面的 java_package 是两种易混淆概念,同时定义的时候,java_package 具有较高的优先级
option java_package = "com.jet.protobuf"; // 生成类的包名,注意:会在指定路径下按照该包名的定义来生成文件夹
option java_outer_classname="PersonTestProtos"; // 生成类的类名,注意:下划线的命名会在编译的时候被自动改为驼峰命名
message PersonTest {
int32 id = 1; // int 类型 ,编号是1,编号不能重复
string name = 2; // string 类型
string email = 3;
Sex sex = 4; // 枚举类型
repeated PhoneNumber phone = 5; // 引用下面定义的 PhoneNumber 类型的 message
map<string, string> tags = 6; // map 类型
repeated google.protobuf.Any details = 7; // 使用 google 的 any 类型
// 定义一个枚举
enum Sex {
DEFAULT = 0;
MALE = 1;
Female = 2;
}
// 定义一个 message
message PhoneNumber {
string number = 1;
PhoneType type = 2;
enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}
}
}
2、编译成java文件
可以从github上下载安装,也可以使用idea插件操作。
3、maven依赖
<!-- protobuf -->
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>3.7.1</version>
</dependency>
4、序列化和反序列化
package com.jet.mini.protobuf;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
/**
* @ClassName: ProtoTest
* @Description: ProtoBuf 测试
* @Author: Jet.Chen
* @Date: 2019/5/8 9:55
* @Version: 1.0
**/
public class ProtoTest {
public static void main(String[] args) {
try {
/** Step1:生成 personTest 对象 */
// personTest 构造器
PersonTestProtos.PersonTest.Builder personBuilder = PersonTestProtos.PersonTest.newBuilder();
// personTest 赋值
personBuilder.setName("Jet Chen");
personBuilder.setEmail("ckk505214992@gmail.com");
personBuilder.setSex(PersonTestProtos.PersonTest.Sex.MALE);
// 内部的 PhoneNumber 构造器
PersonTestProtos.PersonTest.PhoneNumber.Builder phoneNumberBuilder = PersonTestProtos.PersonTest.PhoneNumber.newBuilder();
// PhoneNumber 赋值
phoneNumberBuilder.setType(PersonTestProtos.PersonTest.PhoneNumber.PhoneType.MOBILE);
phoneNumberBuilder.setNumber("17717037257");
// personTest 设置 PhoneNumber
personBuilder.addPhone(phoneNumberBuilder);
// 生成 personTest 对象
PersonTestProtos.PersonTest personTest = personBuilder.build();
/** Step2:序列化和反序列化 */
// 方式一 byte[]:
// 序列化
// byte[] bytes = personTest.toByteArray();
// 反序列化
// PersonTestProtos.PersonTest personTestResult = PersonTestProtos.PersonTest.parseFrom(bytes);
// System.out.println(String.format("反序列化得到的信息,姓名:%s,性别:%d,手机号:%s", personTestResult.getName(), personTest.getSexValue(), personTest.getPhone(0).getNumber()));
// 方式二 ByteString:
// 序列化
// ByteString byteString = personTest.toByteString();
// System.out.println(byteString.toString());
// 反序列化
// PersonTestProtos.PersonTest personTestResult = PersonTestProtos.PersonTest.parseFrom(byteString);
// System.out.println(String.format("反序列化得到的信息,姓名:%s,性别:%d,手机号:%s", personTestResult.getName(), personTest.getSexValue(), personTest.getPhone(0).getNumber()));
// 方式三 InputStream
// 粘包,将一个或者多个protobuf 对象字节写入 stream
// 序列化
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
personTest.writeDelimitedTo(byteArrayOutputStream);
// 反序列化,从 steam 中读取一个或者多个 protobuf 字节对象
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
PersonTestProtos.PersonTest personTestResult = PersonTestProtos.PersonTest.parseDelimitedFrom(byteArrayInputStream);
System.out.println(String.format("反序列化得到的信息,姓名:%s,性别:%d,手机号:%s", personTestResult.getName(), personTest.getSexValue(), personTest.getPhone(0).getNumber()));
} catch (InvalidProtocolBufferException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
-----------------下面是proto2---------------------------------------------------------------------------
1.依赖
想要正常的使用生成的Java类,需要导入protobuf的依赖:protobuf-java.jar
2.protobuf Java API
以GPS信号为例,Gps.proto文件如下:
syntax = "proto2";
option java_package = "com.test.bean";
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)Builders
使用Protobuf生成的每一个java类中,都会包含两种内部类:Msg和Msg包含的Builder。
以上面的GPS信号为例,
AddressBookProtos.AddressBook、AddressBookProtos.Person、AddressBookProtos.Person.PhoneNumber
AddressBookProtos.AddressBook.Builder、AddressBookProtos.Person.Builder、
AddressBookProtos.Person.PhoneNumber.Builder
这两个类提供不同的API,具体来说:
Buider提供了构建类,查询类的API(set、get、has、clear等方法)
Msg提供了查询、序列化的API。
public class Main {
public static void main(String[] args) {
//使用builder构建一个Person对象
AddressBookProtos.Person john =
AddressBookProtos.Person.newBuilder()
.setId(1234)
.setName("John Doe")
.setEmail("jdoe@example.com")
.addPhones(
//内部类PhoneNumber同样使用其Builder构建,但是注意,不需要调用build()方法
AddressBookProtos.Person.PhoneNumber.newBuilder()
.setNumber("555-4321")
.setType(AddressBookProtos.Person.PhoneType.HOME))
//build()结束整个程序流,返回Person对象
.build();
System.out.println(john);
}
}
//Msg只提供了get和has方法
String email = john.getEmail();
boolean hasEmail = john.hasEmail();
//而builder提供了add/set、get、has和clear方法
AddressBookProtos.Person.Builder builder = AddressBookProtos.Person.newBuilder();
builder.setEmail("jdoe@example.com");
boolean hasEmail1 = builder.hasEmail();
String email1 = builder.getEmail();
builder.clearEmail();
序列化:
- byte[] toBytesArray():生成字节数组
- void writeTo(OutputStresam output) :序列化并写入到指定的输出流中
反序列化:
- static Person parseFrom(byte[] data):解析二进制数组,反序列化指定对象
- static Person parseFrom(InputStream input):解析输入流,反序列化指出指定对象
public class Main {
public static void main(String[] args) {
//使用builder()Msg类
AddressBookProtos.Person john =
AddressBookProtos.Person.newBuilder()
.setId(1234)
.setName("John Doe")
.setEmail("jdoe@example.com")
.addPhones(
AddressBookProtos.Person.PhoneNumber.newBuilder()
.setNumber("555-4321")
.setType(AddressBookProtos.Person.PhoneType.HOME))
.build();
try {
//序列化
byte[] bytes = john.toByteArray();
//反序列化
System.out.println(AddressBookProtos.Person.parseFrom(bytes));
} catch (InvalidProtocolBufferException e) {
e.printStackTrace();
}
}
}
最后欢迎大家访问我的个人网站:1024s