http://blog.csdn.net/fangzhangsc2006/article/details/8687388
本文適用于了解spring框架,同時想在spring項目中使用Protocol Buffers(以下簡稱PB)的讀者。
本文標題為《spring 項目中集成 Protocol Buffers 示例》,意思當然是教讀者如何將PB配置到spring項目中去,但事實上在spring項目中使用PB無需任何配置,命該題目的用意也是讓正在苦苦尋找配置方式的朋友在此止步,因為當初我也是這樣。
什么是PB?以下為PB官網的描述。
譯為:PB是一種高效的且可擴展的結構化數據編碼方案。Google將其用在內部的幾乎所有的RPC協議和文件格式。
通俗的講PB是一個不錯的序列化和反序列化方案,其采用二進制編碼方案,效率比xml、json高。將PB用在文件存儲、網絡傳輸都是不錯的選擇。為什么這么說?
比如我們需要在服務器與客戶端之間傳輸一些結構化的數據(對象、結構體),我們怎么去序列化和反序列化呢?你可能想到了以下方案:
1.使用Java Serialization。但這是依賴于java語言的,客戶端和服務器采用不同的語言實現時就不好辦了,而且Java Serialization公認的有一些問題。
2.定義自己的格式,比如約定“第幾個字節表示內容長度”、“第幾個字節表示數據類型”、“某某符號表示分隔符”等等,很明顯這只能適用于一些數據格式很簡單的場合。
3.使用xml或者json。首先xml的臃腫是大家都詬病的。另外xml和json的樹形結構使用起來也是比較繁瑣的,肯定是不及類使用起來簡單。不過他們還是有一個PB不具備的優點,那就是數據的自描述性。如果這一點是你看重的,那么xml和json還是可以成為被選擇的理由。
說了這么多,到底PB怎么用呢?下面我將以在springMVC項目中使用PB為示例介紹其基本用法。其實PB不依賴于任何平臺或框架,原則上只要語言支持就可以,目前支持Java、C++、Python。
一、下載
去【PB官網】下載編譯器。
去【maven中央庫】搜索并下載PB的jar包。
二、定義.proto文件
約定數據格式,然后用PB的語法將其定義為.proto文件。這里使用官網的例子。
[java]?view plaincopy
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?phone?=?4;?? }?? ?? message?AddressBook?{?? ??repeated?Person?person?=?1;?? }??
說明:
1.上面的例子是定義一個電話薄,電話薄AddressBook包含多個Person,一個Person有name等屬性,還有多個PhoneNumber,一個PhoneNumber有PhoneType枚舉……
2.可以看出.proto文件中除了可以定義基本的數據類型,比如int32、string,還可以嵌套使用自定義的類型,比如AddressBook中使用Person類型。
3.屬性聲明前的required、optional、repeated分別表示必須賦值、可為空、集合。
三、編譯
使用之前下載的編譯器編譯.proto文件,命令格式為:
protoc -I=$SRC_DIR --java_out=$DST_DIR $SRC_DIR/addressbook.proto
如果將編譯器protoc.exe放到和.proto文件一起,輸出目錄為當前路徑的話編譯命令可以簡化為:
protoc.exe --java_out=. addressbook.proto
輸出路徑參數“.”表示當前目錄。
編譯完成后當前目錄下生成了com\example\tutorial\AddressBookProtos.java文件。然后我們就可以直接使用這個代碼文件了。
四、server端使用
在springMVC項目中加入PB的jar包,除此之外沒有任何的配置。
將生產的代碼文件AddressBookProtos.java拷到項目中,包名要一致。
在controller層中通過request拿到inputstream對象,然后通過PB對象的靜態方法parseFrom()就可從輸入流中反序列化出PB實例。
如果向客戶端返回PB對象,則通過PB實例的writeTo()方法,參數為OutputStream。
例子代碼:
[java]?view plaincopy
import?java.io.IOException;?? import?java.io.InputStream;?? import?java.io.OutputStream;?? import?javax.servlet.http.HttpServletRequest;?? import?javax.servlet.http.HttpServletResponse;?? import?org.springframework.stereotype.Controller;?? import?org.springframework.web.bind.annotation.RequestMapping;?? import?com.example.tutorial.AddressBookProtos.AddressBook;?? import?com.example.tutorial.AddressBookProtos.Person;?? ?? ? ? ? ?? @Controller?? @RequestMapping("/pbtest")?? public?class?TestController?{?? ????@RequestMapping("upload")?? ????public?void?upload(HttpServletRequest?request,?HttpServletResponse?response)?throws?IOException?{?? ????????InputStream?inputStream?=?request.getInputStream();?? ????????AddressBook?addressBook?=?AddressBook.parseFrom(inputStream);?? ????????inputStream.close();?? ????????System.out.println(addressBook);?? ????}?? ?????? ????@RequestMapping("download")?? ????public?void?download(HttpServletResponse?response)?throws?IOException{?? ????????Person?john?=?Person.newBuilder()?? ????????????????.setId(1234)?? ????????????????.setName("John?Doe")?? ????????????????.setEmail("jdoe@example.com")?? ????????????????.addPhone(?? ??????????????????Person.PhoneNumber.newBuilder()?? ????????????????????.setNumber("555-4321")?? ????????????????????.setType(Person.PhoneType.HOME))?? ????????????????.build();?? ????????AddressBook?addressBook?=?AddressBook.newBuilder().addPerson(john).build();?? ????????response.setContentType("application/x-protobuf");?? ????????OutputStream?outputStream?=?response.getOutputStream();?? ????????addressBook.writeTo(outputStream);?? ????????outputStream.flush();?? ????????outputStream.close();?? ????}?? }??
說明:upload接口和download接口分別是上傳和下載電話薄。
五、client端使用
在客戶端工程中加入PB的jar包,拷入代碼文件AddressBookProtos.java。
下面的例子模擬客戶端向服務器發送和接收電話薄。
如下:
[java]?view plaincopy
package?test;?? ?? import?java.io.IOException;?? import?java.io.InputStream;?? import?java.io.OutputStream;?? import?java.net.HttpURLConnection;?? import?java.net.URL;?? import?com.example.tutorial.AddressBookProtos.AddressBook;?? import?com.example.tutorial.AddressBookProtos.Person;?? ?? ? ? ? ?? public?class?PBClientTest?{?? ?????? ????public?static?void?main(String[]?args)?throws?IOException?{?? ????????String?url?=?"http://www.example.com/pbtest/upload.pb";?? ????????upload(url);?? ????????String?url2?=?"http://www.example.com/pbtest/download.pb";?? ????????download(url2);?? ????}?? ?????? ????public?static?void?upload(String?url)?throws?IOException?{?? ????????Person?john?=?Person.newBuilder()?? ????????.setId(1234)?? ????????.setName("John?Doe")?? ????????.setEmail("jdoe@example.com")?? ????????.addPhone(?? ??????????Person.PhoneNumber.newBuilder()?? ????????????.setNumber("555-4321")?? ????????????.setType(Person.PhoneType.HOME))?? ????????.build();?? ????????AddressBook?addressBook?=?AddressBook.newBuilder().addPerson(john).build();?? ????????byte[]?content?=?addressBook.toByteArray();?? ?????????? ????????URL?targetUrl?=?new?URL(url);?? ????????HttpURLConnection?connection?=?(HttpURLConnection)?targetUrl.openConnection();?? ????????connection.setDoOutput(true);?? ????????connection.setDoInput(true);?? ????????connection.setRequestProperty("Content-Type",?"application/x-protobuf");?? ????????connection.setRequestProperty("Accept",?"application/x-protobuf");?? ????????connection.setRequestMethod("POST");?? ????????connection.setRequestProperty("Connect-Length",?Integer.toString(content.length));?? ????????connection.setFixedLengthStreamingMode(content.length);?? ????????OutputStream?outputStream?=?connection.getOutputStream();?? ????????outputStream.write(content);?? ????????outputStream.flush();?? ????????outputStream.close();?? ????}?? ?????? ????public?static?void?download(String?url)?throws?IOException?{?????????? ????????URL?target?=?new?URL(url);?? ????????HttpURLConnection?conn?=?(HttpURLConnection)?target.openConnection();?? ????????conn.setDoOutput(true);?? ????????conn.setDoInput(true);?? ????????conn.setRequestMethod("GET");?? ????????conn.setRequestProperty("Content-Type",?"application/x-protobuf");?? ????????conn.setRequestProperty("Accept",?"application/x-protobuf");?? ????????conn.connect();?? ?????????? ????????int?code?=?conn.getResponseCode();?? ????????System.out.println("code:"?+?code);?? ????????System.out.println(conn.getContent());?? ????????boolean?success?=?(code?>=?200)?&&?(code?<?300);?? ?????????? ????????InputStream?in?=?success???conn.getInputStream()?:?conn.getErrorStream();?? ????????AddressBook?addressBook?=?AddressBook.parseFrom(in);?? ????????in.close();?? ????????System.out.println(addressBook);?? ????}?? }??
說明:
1.客戶端為普通的java project就可以了。
2.我這里的url的后綴為.pb,這要依照server端的web.xml配置,可以配置為其他的,比如.html
六、總結
上面的例子演示了如何序列化和反序列PB對象,其實就是調用PB對象自身提供的方法,比如parseFrom、writeTo。可以看出PB的使用的確是很方便的。通過一個方法就可以將二進制數據轉為對象。
什么?你連轉換對象的方法都不想調?那就參考【我的另一篇博客】客吧!
參考:
【PB官網】:http://code.google.com/p/protobuf/
【maven中央庫】:http://search.maven.org/
【我的另一篇博客】:http://blog.csdn.net/fangzhangsc2006/article/details/8687415
總結
以上是生活随笔為你收集整理的spring 项目中集成 Protocol Buffers 示例的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。