spring 项目中集成 Protocol Buffers 示例

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 plain copy
  1. package tutorial;  
  2.   
  3. //这里声明输出的java的包名  
  4. option java_package = "com.example.tutorial";  
  5. //这里声明输出的java的类名  
  6. option java_outer_classname = "AddressBookProtos";  
  7.   
  8. message Person {  
  9.   required string name = 1;  
  10.   required int32 id = 2;  
  11.   optional string email = 3;  
  12.   
  13.   enum PhoneType {  
  14.     MOBILE = 0;  
  15.     HOME = 1;  
  16.     WORK = 2;  
  17.   }  
  18.   
  19.   message PhoneNumber {  
  20.     required string number = 1;  
  21.     optional PhoneType type = 2 [default = HOME];  
  22.   }  
  23.   
  24.   repeated PhoneNumber phone = 4;  
  25. }  
  26.   
  27. message AddressBook {  
  28.   repeated Person person = 1;  
  29. }  
说明:

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 plain copy
  1. import java.io.IOException;  
  2. import java.io.InputStream;  
  3. import java.io.OutputStream;  
  4. import javax.servlet.http.HttpServletRequest;  
  5. import javax.servlet.http.HttpServletResponse;  
  6. import org.springframework.stereotype.Controller;  
  7. import org.springframework.web.bind.annotation.RequestMapping;  
  8. import com.example.tutorial.AddressBookProtos.AddressBook;  
  9. import com.example.tutorial.AddressBookProtos.Person;  
  10.   
  11. /** 
  12.  * @author shannon 
  13.  * 
  14.  */  
  15. @Controller  
  16. @RequestMapping("/pbtest")  
  17. public class TestController {  
  18.     @RequestMapping("upload")  
  19.     public void upload(HttpServletRequest request, HttpServletResponse response) throws IOException {  
  20.         InputStream inputStream = request.getInputStream();  
  21.         AddressBook addressBook = AddressBook.parseFrom(inputStream);  
  22.         inputStream.close();  
  23.         System.out.println(addressBook);  
  24.     }  
  25.       
  26.     @RequestMapping("download")  
  27.     public void download(HttpServletResponse response) throws IOException{  
  28.         Person john = Person.newBuilder()  
  29.                 .setId(1234)  
  30.                 .setName("John Doe")  
  31.                 .setEmail("jdoe@example.com")  
  32.                 .addPhone(  
  33.                   Person.PhoneNumber.newBuilder()  
  34.                     .setNumber("555-4321")  
  35.                     .setType(Person.PhoneType.HOME))  
  36.                 .build();  
  37.         AddressBook addressBook = AddressBook.newBuilder().addPerson(john).build();  
  38.         response.setContentType("application/x-protobuf");  
  39.         OutputStream outputStream = response.getOutputStream();  
  40.         addressBook.writeTo(outputStream);  
  41.         outputStream.flush();  
  42.         outputStream.close();  
  43.     }  
  44. }  
说明:upload接口和download接口分别是上传和下载电话薄。


五、client端使用

在客户端工程中加入PB的jar包,拷入代码文件AddressBookProtos.java。
下面的例子模拟客户端向服务器发送和接收电话薄。

如下:

[java]  view plain copy
  1. package test;  
  2.   
  3. import java.io.IOException;  
  4. import java.io.InputStream;  
  5. import java.io.OutputStream;  
  6. import java.net.HttpURLConnection;  
  7. import java.net.URL;  
  8. import com.example.tutorial.AddressBookProtos.AddressBook;  
  9. import com.example.tutorial.AddressBookProtos.Person;  
  10.   
  11. /** 
  12.  *  
  13.  * @author shannon 
  14.  */  
  15. public class PBClientTest {  
  16.       
  17.     public static void main(String[] args) throws IOException {  
  18.         String url = "http://www.example.com/pbtest/upload.pb";  
  19.         upload(url);  
  20.         String url2 = "http://www.example.com/pbtest/download.pb";  
  21.         download(url2);  
  22.     }  
  23.       
  24.     public static void upload(String url) throws IOException {  
  25.         Person john = Person.newBuilder()  
  26.         .setId(1234)  
  27.         .setName("John Doe")  
  28.         .setEmail("jdoe@example.com")  
  29.         .addPhone(  
  30.           Person.PhoneNumber.newBuilder()  
  31.             .setNumber("555-4321")  
  32.             .setType(Person.PhoneType.HOME))  
  33.         .build();  
  34.         AddressBook addressBook = AddressBook.newBuilder().addPerson(john).build();  
  35.         byte[] content = addressBook.toByteArray();  
  36.           
  37.         URL targetUrl = new URL(url);  
  38.         HttpURLConnection connection = (HttpURLConnection) targetUrl.openConnection();  
  39.         connection.setDoOutput(true);  
  40.         connection.setDoInput(true);  
  41.         connection.setRequestProperty("Content-Type""application/x-protobuf");  
  42.         connection.setRequestProperty("Accept""application/x-protobuf");  
  43.         connection.setRequestMethod("POST");  
  44.         connection.setRequestProperty("Connect-Length", Integer.toString(content.length));  
  45.         connection.setFixedLengthStreamingMode(content.length);  
  46.         OutputStream outputStream = connection.getOutputStream();  
  47.         outputStream.write(content);  
  48.         outputStream.flush();  
  49.         outputStream.close();  
  50.     }  
  51.       
  52.     public static void download(String url) throws IOException {          
  53.         URL target = new URL(url);  
  54.         HttpURLConnection conn = (HttpURLConnection) target.openConnection();  
  55.         conn.setDoOutput(true);  
  56.         conn.setDoInput(true);  
  57.         conn.setRequestMethod("GET");  
  58.         conn.setRequestProperty("Content-Type""application/x-protobuf");  
  59.         conn.setRequestProperty("Accept""application/x-protobuf");  
  60.         conn.connect();  
  61.         // check response code  
  62.         int code = conn.getResponseCode();  
  63.         System.out.println("code:" + code);  
  64.         System.out.println(conn.getContent());  
  65.         boolean success = (code >= 200) && (code < 300);  
  66.           
  67.         InputStream in = success ? conn.getInputStream() : conn.getErrorStream();  
  68.         AddressBook addressBook = AddressBook.parseFrom(in);  
  69.         in.close();  
  70.         System.out.println(addressBook);  
  71.     }  
  72. }  
说明:

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


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值