Protobuf是什么离赫?
官網(wǎng)解釋
Protobuf 是Google為序列化數(shù)據(jù)結(jié)構(gòu)提供的語言無關(guān),平臺無關(guān)旬盯,可擴(kuò)展的機(jī)制翎猛,它比XML更小更快更簡單。你可以定義你想定義的數(shù)據(jù)表示成結(jié)構(gòu)數(shù)據(jù)萨咳,然后你可以使用產(chǎn)生的代碼去寫或者從多種多樣的數(shù)據(jù)流中讀取你的結(jié)構(gòu)數(shù)據(jù)疫稿,并且在多種多樣的語言中使用。
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.
下面我們編寫一個例子舀凛,例子的目標(biāo)是使用protobuf序列化和反序列化對象腾降。在本例子中我們使用java編寫碎绎,所以在這個過程用會引用到Maven構(gòu)建項(xiàng)目。
例子
步驟一:編寫proto文件
protobuf是一種跨語言的序列化/反序列化的工具奸晴,通過proto文件定義對象的數(shù)據(jù)結(jié)構(gòu)日麸,并依賴此proto文件進(jìn)行序列化和反序列化操作逮光。
新建PersonMsg.proto文件涕刚,詳細(xì)定義如下:
//定義使用的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關(guān)鍵字乙帮,定義要使用的protobuf版本,在這里我們使用proto3(注意:這個聲明必須在第一行驾茴。)
(2)這個是用來防止不同protobuf message 類型的命名沖突氢卡,在不同語言中package對生成代碼的影響也不一樣译秦,在java中,如果不顯式提供option java_package參數(shù)棋返,那么package所標(biāo)明的內(nèi)容就是生成java類所在的包路徑雷猪。詳細(xì)內(nèi)容請查看原文下的package說明:protobuf guide
(3)~(4)標(biāo)明java參數(shù)晰房,其他語言不需要提供此參數(shù)殊者。
如不提供java_package參數(shù),會使用上面聲明的package內(nèi)容作為生成的java類的包路徑猖吴。
如不提供java_outer_classname參數(shù)海蔽,會使用.proto文件的文件名作為生成java類的類名。
(5)這里聲明了一個名字是Person的message(注意:這里message的名字不能跟上面 java_outer_classname參數(shù)定義的名字一樣拗引。)
(6)~(9)在這里聲明了4個字段幌衣,聲明字段的格式是 "類型 字段名=標(biāo)號",在protobuf中哼凯,提供豐富的字段類型,在官網(wǎng)guide中可以查看proto類型對應(yīng)產(chǎn)生不同語言的類型(詳細(xì)查看Guide中的Scalar Value Types:protobuf guide)猎贴。message的標(biāo)號都是從1開始遞增家坎,標(biāo)號是不允許重復(fù)的虱疏。
步驟二:下載protobuf的編譯工具
編寫完proto文件,我們需要一個工具去編譯它对粪,所以我們需要下載protoc.exe装蓬。
下載方式:在官網(wǎng)中下載protoc-3.4.0-win32.zip(下載地址:protobuf 3.4下載鏈接),解壓可以看到在bin目錄下的protoc.exe儡遮,如下圖:
protoc.exe是使用c++編寫的proto文件的編譯工具鄙币,文章后面我們會介紹使用protoc.exe編譯proto文件產(chǎn)生對應(yīng)的java類蹂随。
步驟三:新建Maven工程
在這個演示的案例中,我們使用maven構(gòu)建項(xiàng)目绩衷。然后在src/main下新增protos目錄存放.proto文件激率。
項(xiàng)目的目錄結(jié)構(gòu)如下:
因?yàn)槲覀冞@里使用的是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文件,產(chǎn)生對應(yīng)的java類壤短。
首先慨仿,把protoc.exe拷貝到工程src/main/protos目錄下
然后,把步驟一所編寫的proto文件也拷貝到src/main/protos目錄下
進(jìn)入cmd命令行帘撰,進(jìn)入protoc.exe所在路徑万皿,輸入如下命令:
protoc --java_out=../java PersonMsg.proto
因?yàn)槲覉?zhí)行此命令時牢硅,protoc.exe和PersonMsg.proto在同一目錄下,所以不用指定proto_path參數(shù)(默認(rèn)會掃描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文件的路徑
執(zhí)行編譯命令后瞧剖,我們?nèi)サ絪rc/main/java目錄下查看是否有java類生成可免。
步驟四:編寫測試類
上面我們已經(jīng)成功編譯protoc文件生成了對應(yīng)Java類浇借,現(xiàn)在我們就可以編寫一個測試類驗(yàn)證一下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對象吼和,轉(zhuǎn)換成byte數(shù)組
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模式構(gòu)造序列化對象炫乓。
(2)調(diào)用build方法構(gòu)造對象献丑,對象已經(jīng)構(gòu)造就不能改變。
(3) 使用toByteArray方法箩做,把originPerson序列化成byte數(shù)組
(4)使用parseFrom方法反序列化筐摘,傳入剛剛的byte數(shù)組
序列化和反序列化說明咖熟,請參考官網(wǎng)guide->Tutorials->java里面的Parsing and Serialization內(nèi)容(連接)
運(yùn)行下junit,ok!