Protobuf是什么?
官网解释
Protobuf 是Google为序列化数据结构提供的语言无关,平台无关,可扩展的机制,它比XML更小更快更简单。你可以定义你想定义的数据表示成结构数据,然后你可以使用产生的代码去写或者从多种多样的数据流中读取你的结构数据,并且在多种多样的语言中使用。
Protocol buffers are Google's language-neutral, platform-neutral, extensible mechanism for serializing structured data – think XML, but smaller, faster, and simpler. You define how you want your data to be structured once, then you can use special generated source code to easily write and read your structured data to and from a variety of data streams and using a variety of languages.
下面我们编写一个例子,例子的目标是使用protobuf序列化和反序列化对象。在本例子中我们使用java编写,所以在这个过程用会引用到Maven构建项目。
例子
步骤一:编写proto文件
protobuf是一种跨语言的序列化/反序列化的工具,通过proto文件定义对象的数据结构,并依赖此proto文件进行序列化和反序列化操作。
新建PersonMsg.proto文件,详细定义如下:
//定义使用的protobuf版本
syntax = "proto3"; //(1)
//定义所在的protobuf包空间
package com.simple;//(2)
//生成的java类所在的包路径
option java_package = "com.simple"; //(3)
//生成的java类的类名
option java_outer_classname = "PersonMsg"; //(4)
//声明一个message类
message Person{ //(5)
string name = 1; //(6)
int32 age =2;//(7)
string email =3;//(8)
string phoneNumber =4;//(9)
}
简单解析一下:
(1)使用syntax关键字,定义要使用的protobuf版本,在这里我们使用proto3(注意:这个声明必须在第一行。)
(2)这个是用来防止不同protobuf message 类型的命名冲突,在不同语言中package对生成代码的影响也不一样,在java中,如果不显式提供option java_package参数,那么package所标明的内容就是生成java类所在的包路径。详细内容请查看原文下的package说明:protobuf guide
(3)~(4)标明java参数,其他语言不需要提供此参数。
如不提供java_package参数,会使用上面声明的package内容作为生成的java类的包路径。
如不提供java_outer_classname参数,会使用.proto文件的文件名作为生成java类的类名。
(5)这里声明了一个名字是Person的message(注意:这里message的名字不能跟上面 java_outer_classname参数定义的名字一样。)
(6)~(9)在这里声明了4个字段,声明字段的格式是 "类型 字段名=标号",在protobuf中,提供丰富的字段类型,在官网guide中可以查看proto类型对应产生不同语言的类型(详细查看Guide中的Scalar Value Types:protobuf guide)。message的标号都是从1开始递增,标号是不允许重复的。
步骤二:下载protobuf的编译工具
编写完proto文件,我们需要一个工具去编译它,所以我们需要下载protoc.exe。
下载方式:在官网中下载protoc-3.4.0-win32.zip(下载地址:protobuf 3.4下载链接),解压可以看到在bin目录下的protoc.exe,如下图:
protoc.exe是使用c++编写的proto文件的编译工具,文章后面我们会介绍使用protoc.exe编译proto文件产生对应的java类。
步骤三:新建Maven工程
在这个演示的案例中,我们使用maven构建项目。然后在src/main下新增protos目录存放.proto文件。
项目的目录结构如下:
因为我们这里使用的是java,所以要引入protobuf-java.jar来操作protobuf。我们使用的maven,所以直接在pom.xml中加入protobuf-java.jar的依赖。完整的pom.xml如下图:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.protobuf</groupId>
<artifactId>myprotobuflearn</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>myprotobuflearn</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>3.4.0</version>
</dependency>
</dependencies>
</project>
步骤四:编译proto文件
这一步我们需要使用下载的protoc.exe文件编译proto文件,产生对应的java类。
首先,把protoc.exe拷贝到工程src/main/protos目录下
然后,把步骤一所编写的proto文件也拷贝到src/main/protos目录下
进入cmd命令行,进入protoc.exe所在路径,输入如下命令:
protoc --java_out=../java PersonMsg.proto
因为我执行此命令时,protoc.exe和PersonMsg.proto在同一目录下,所以不用指定proto_path参数(默认会扫描protoc.exe所在目录的proto文件)。在这里指定生成java类所在目录为protos同级目录java文件夹下。
完整的命令格式:
protoc.exe --proto_path=$PROTO_DIR --java_out=$JAVA_OUT_DIR xxx.proto
说明:
1.proto_path是要编译的proto文件所在文件夹路径,protoc.exe会扫描这个路径,找到后面要求编译的proto文件
2.java_out是编译生成的java类存放的路径
3.最后就是要编译的proto文件的路径
执行编译命令后,我们去到src/main/java目录下查看是否有java类生成。
步骤四:编写测试类
上面我们已经成功编译protoc文件生成了对应Java类,现在我们就可以编写一个测试类验证一下protobuf的序列化和反序列化。
回到我们maven工程,在src/main/test中新增测试类PersonTest.java,代码如下:
package com.simple;
import static org.junit.Assert.*;
import java.io.IOException;
import org.junit.Test;
import com.simple.PersonMsg.Person;
public class PersonMsgTest {
@Test
public void testSerilize() throws IOException{
Person.Builder personBuilder = Person.newBuilder();//(1)
personBuilder.setName("changzhang");
personBuilder.setAge(18);
personBuilder.setEmail("123456789@qq.com");
personBuilder.setPhoneNumber("98765432");
Person orginPerson = personBuilder.build();//(2)
//序列化originPerson对象,转换成byte数组
byte[] byteArray = orginPerson.toByteArray();//(3)
//反序列化
Person newPerson = Person.parseFrom(byteArray);//(4)
assertEquals(newPerson.getName(),orginPerson.getName());
assertEquals(newPerson.getAge(), orginPerson.getAge());
assertEquals(newPerson.getEmail(), orginPerson.getEmail());
assertEquals(newPerson.getPhoneNumber(), orginPerson.getPhoneNumber());
}
}
说明:
(1)在protobuf中使用builder模式构造序列化对象。
(2)调用build方法构造对象,对象已经构造就不能改变。
(3) 使用toByteArray方法,把originPerson序列化成byte数组
(4)使用parseFrom方法反序列化,传入刚刚的byte数组
序列化和反序列化说明,请参考官网guide->Tutorials->java里面的Parsing and Serialization内容(连接)
运行下junit,ok!