最近公司有用到Protobuf这个序列化框架,以前从来没用过,所以今天学习了下,入个门。
Protobuf 是一种与平台和语言无关的序列化框架,常用于通信协议,数据存储等。
优点:它的速度比xml和json更快,同时他能将数据压缩的更小,对于结构化数据优势更明显。什么是结构化数据呢?就是带有一定结构的数据。比如电话簿上有很多记录数据,每条记录包含姓名、ID、邮件、电话等,这种结构重复出现。
缺点:需要你定义proto文件,然后根据proto文件生成对应的java类,然后我们对这个java类进行序列化和反序列化。并且proto文件和生成的java类都比较反人类,难以读懂。
安装:
首先需要安装proto到本地
下载地址:https://github.com/protocolbuffers/protobuf/releases/tag/v3.7.1
然后将 bin 路径添加到 path 环境变量里,使用命令protoc --version查看是否安装成功。
使用方法:
1. 首先定义一个你proto文件ProtobufTest.proto
syntax = "proto3"; // PB协议版本
import "google/protobuf/any.proto"; // 引用外部的message,可以是本地的,也可以是此处比较特殊的 Any
option java_package = "com.didispace.web"; // 生成类的包名,注意:会在指定路径下按照该包名的定义来生成文件夹
option java_outer_classname="PersonTestProtos"; // 生成类的类名,注意:下划线的命名会在编译的时候被自动改为驼峰命名
message PersonTest {
int32 id = 1; // int 类型
string name = 2; // string 类型
string email = 3;
Sex sex = 4; // 枚举类型
repeated PhoneNumber phone = 5; // 引用下面定义的 PhoneNumber 类型的 message repeated的含义是这里可以放多个PhoneNumber,相当于list
map
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. 在proto文件所在目录用命令行执行命令,生成java实体类PersonTestProtos
protoc -I=./ --java_out=./ ./ProtobufTest.proto
然后就能看到对应的目录下生成了实体类。
3. 序列化和反序列对应的PersonTestProtos类代码例子如下,同时下方也有protobuf对象和json互转的例子,因为有时候我们与第三方公司交互时,有可能使用到json。
package com.didispace.web;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import com.googlecode.protobuf.format.JsonFormat;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
/**
* @ClassName: ProtobufTest
* @Description: ProtoBuf 测试
**/
public class ProtobufTest {
public static void main(String[] args) {
try {
/** Step1:生成 personTest 对象 */
// personTest 构造器
PersonTestProtos.PersonTest.Builder personBuilder = PersonTestProtos.PersonTest.newBuilder();
// personTest 赋值
personBuilder.setName("yizhiyuan");
personBuilder.setEmail("[email protected]");
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();
//查看生成的personTest对象
System.out.println("生成的personTest对象" + personTest.toString());
// 粘包,将一个或者多个protobuf 对象字节写入 stream
// 序列化
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
personTest.writeDelimitedTo(byteArrayOutputStream);
// Step2:反序列化,从 steam 中读取一个或者多个 protobuf 字节对象
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
PersonTestProtos.PersonTest personTestResult = PersonTestProtos.PersonTest.parseDelimitedFrom(byteArrayInputStream);
System.out.println(String.format("反序列化得到的信息,姓名:%s,性别:%d,手机号:%s,邮箱:%s",
personTestResult.getName(), personTest.getSexValue(), personTest.getPhone(0).getNumber(),
personTest.getEmail()));
// Step3:protobuf 对象与json的相互转换,有时候我们与第三方系统可能要使用json
// 将personTest 对象转换为json
String json = JsonFormat.printToString(personTest);
System.out.println("对象转换为string后:"+JsonFormat.printToString(personTest));
//将json反序列化为protobuf对象
PersonTestProtos.PersonTest.Builder personBuilderForJson = PersonTestProtos.PersonTest.newBuilder();
JsonFormat.merge(json,personBuilderForJson);
System.out.println("json 反序列化为对象:"+ personBuilderForJson.build().toString());
} catch (InvalidProtocolBufferException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
大家如果有任何问题,欢迎关注"Java自学之路"这个公众号来提问,里面也有自学全套视频(含项目)和Idea激活码等信息。
最后,用命令生成对应的java文件其实挺麻烦的,protobuf也提供了与maven和gradle集成的插件,通过相关配置,在maven的compile或者在gradle的build的时候,可以生成对应的java文件。大家有兴趣可以看看protobuf-maven-plugin 和 protobuf-gradle-plugin 这两个插件。