Protobuf在java项目中的使用
protobuf的介绍和安装这里就不多说了,不太清楚的可以参考我的上一篇博文()。这里我们就基于java的项目,引入并使用protobuf来处理数据。
资源以及环境介绍:
1JDK 1.7.0_67
2Eclipse jee-indigo
3protobuf: proto.exe 和protobuf-2.5.0.jar
其中jdk和eclipse为下面demo的一个开发环境,这里版本号为该开发环境的一个版本,供参考。而protobuf使用的2.5版本,博文后面将提供protobuf相关资源的个人云盘下载地址,有需要的可自行下载。
具体步骤:
一、创建项目,添加protobuf支持
在eclipse中创建一个web项目(不限定是web项目,其他java项目均可,这里创建web项目只为后续中导入jar包方便),然后将protobuf基于java类库protobuf-2.5.0.jar导入项目中,即将protobuf-2.5.0.jar文件复制到lib文件夹下就行。
二、编写.proto文件,确定消息体
在项目项目的src下创建一个包用于存放.proto文件,再在该包下创建一个普通文件,以.proto为扩展名。这里包名和.proto文件名可以根据需要自行命名。打开.proto文件,根据需要编写消息体,这里贴出笔者demo的.proto文件代码
package demo;
option java_package = "sample.demo";
option java_outer_classname = "ProtoDemo";
message Student {
required int32 id = 1;
required string name = 2;
optional string email = 3;
enum Sex {
MAN = 0;
WOMAN = 1;
}
optional Sex sex = 4 [default = MAN];
enum PhoneType{
MOBILE = 0;
HOME = 1;
WORK = 2;
}
message PhoneNumber {
required string number = 1;
optional PhoneType type = 2 [default = HOME];
}
repeated PhoneNumber phone = 5;
}
补充:就该消息体的定义的做简单说明
1. option java_package指定该proto文件生成的java类的包名
2. optionjava_outer_classname指定proto文件生成的java类的类名
3. message是消息定义的关键字,对应着java中的class,在该proto文件的生成的java类文件中,将会定义了一个内部类Student以及Student类的内部类PhoneNumber
4. required 、optional、repeated均为限制附,修饰消息体中的字段,说明如下:
required:该字段为必要字段,既在序列化和反序列化之前该字段必须已经被赋值, 每个消息中可以包含0个或多个optional类型的字段
optional:该字段为非必要字段
repeated:表示的字段可以包含0个或多个数据,实际在生成的java类中,被repeated修饰的字段将被声明成一个List<字段类型>类型
5.int32和string 定义字段类型,类似于java中的int和String,关于类型的对照表如下:
.proto Type | C++ Type | Java Type | Notes |
double | double | double |
|
float | float | float |
|
int32 | int32 | int | 使用可变长编码方式。编码负数时不够高效——如果你的字段可能含有负数,那么请使用sint32。 |
int64 | int64 | long | 使用可变长编码方式。编码负数时不够高效——如果你的字段可能含有负数,那么请使用sint64。 |
uint32 | uint32 | int | Uses variable-length encoding. |
uint64 | uint64 | long | Uses variable-length encoding. |
sint32 | int32 | int | 使用可变长编码方式。有符号的整型值。编码时比通常的int32高效。 |
sint64 | int64 | long | 使用可变长编码方式。有符号的整型值。编码时比通常的int64高效。 |
fixed32 | uint32 | int | 总是4个字节。如果数值总是比总是比228大的话,这个类型会比uint32高效。 |
fixed64 | uint64 | long | 总是8个字节。如果数值总是比总是比256大的话,这个类型会比uint64高效。. |
sfixed32 | int32 | int | 总是4个字节。 |
sfixed64 | int64 | long | 总是8个字节。 |
bool | bool | boolean |
|
string | string | String | 一个字符串必须是UTF-8编码或者7-bit ASCII编码的文本。 |
bytes | string | ByteString | 可能包含任意顺序的字节数据。 |
6 其中id =1 和 name = 2 ,id和name均消息体中的字段名,在生产的java类中将成为消息类Student的属性的属性名,而标签数字1和2则表示不同的字段在序列化后的二进制数据中的布局位置,需要注意的是该值在同一message中不能重复。
7其中enum Sex {},定义一个名为Sex的枚举类型,在后面可以用Sex来声明消息体中字段的属性
以上说明均基于java,由于官方网址一直访问不进去,关于proto定义的详细说明参考了博文:http://www.cnblogs.com/stephen-liu74/archive/2013/01/02/2841485.html
http://ju.outofmemory.cn/entry/109294三、编译.proto文件,生产对应的java类文件
1打开cmd窗口,将cmd工作空间定位到.proto文件的所在位置,即执行命令:
cd C:\server
这里笔者是先将.proto文件复制到了server目录下进行编译的
2再在该目录下执行命令:
protoc--java_out=. protodemo.proto
这里protodemo.proto为你编写的.proto文件的文件名,protoc--java_out表示以java的形式编译该.proto文件,将会在目录下得到一个sample\demo\ProtoDemo.java文件
3将生成的文件及目录整个复制到项目的src下(这里就将sample文件夹复制到src下),将会在src下看到一个包为sample.demo,在包下有一个ProtoDemo的类
以上方式为手动编译,而Eclipse是可以添加protobuf插件的,可以实现自动编译。笔者想在eclipse上在线安装protobuf插件来着,但是一直访问不进去插件地址,可能是网速不行,读者可以试试,安装后配置protobuf的编译器就可以实现自动编译.proto文件。笔者由于eclipse没有安装上插件这里就不再详细介绍了,读者可以参考博文:
http://www.360doc.com/content/14/0718/15/16044571_395291178.shtml
四、将java实例转换成字节数据,再将字节数据转换成java实例
现在在我们的java项目就有了生成的ProtoDemo类,现在编写代码来使用该来转换相关信息数据。创建了一个Test类,具体代码如下
package proto.demo.test;
import java.util.List;
import com.google.protobuf.InvalidProtocolBufferException;
import sample.demo.ProtoDemo;
import sample.demo.ProtoDemo.Student;
import sample.demo.ProtoDemo.Student.PhoneNumber;
public class Test {
/**
* protobuf是一种数据交换的格式,以二进制的格式进行数据交换,主要用于网络传输、配置文件、数据存储等诸多领域
*
* 下面我们就将指定格式的信息转换成字节形式数据,然后将字节形式数据恢复成指定格式的信息
* 读者可以简单的看下.proto文件生成的ProtoDemo类的结构
*/
public static void main(String[] args) {
//获得Student对象
//这里的Student对象构造器被私有化,我们通过通过Student的内部类Builder来构建builder
ProtoDemo.Student.Builder builder = ProtoDemo.Student.newBuilder();
//Student类未提供相关属性的set方法,而Student的内部类builder提供了构建Student相关属性的set方法
builder.setId(1);
builder.setName("凌晨1点21分");
builder.setEmail("2474600377@qq.com");
builder.setSex(ProtoDemo.Student.Sex.MAN);
//相同方法获取PhoneNumber对象
ProtoDemo.Student.PhoneNumber.Builder phb = ProtoDemo.Student.PhoneNumber.newBuilder();
phb.setNumber("123456789");
phb.setType(ProtoDemo.Student.PhoneType.HOME);
PhoneNumber pn = phb.build();
builder.addPhone(pn);
//这里再获取了一个PhoneNumber对象,原理一样
PhoneNumber pn2 = ProtoDemo.Student.PhoneNumber.newBuilder()
.setNumber("789456123").setType(ProtoDemo.Student.PhoneType.MOBILE).build();
builder.addPhone(pn2);
Student stu = builder.build();
//这里你我们将封装有数据的对象实例,转换为字节数组,用于数据传输、存储等
byte[] stuByte = stu.toByteArray();
//这里得到了stuBte字节数组后,我们可以将该数据进行数据传输或存储,这里至于用什么技术传输就根据具体情况而定
//假如这里stuByt通过传输,下面的代码接到了该数据
//接收方 ,这里为了方便我们就写在一个类里面
try {
//将字节数据转换为对应的对象实例
Student stu2 = ProtoDemo.Student.parseFrom(stuByte);
//这里得到了Student实例了,就可以根据需要来操作里面的数据了
System.out.println("学生ID:"+stu2.getId());
System.out.println("姓名:"+stu2.getName());
System.out.println("性别:"+(stu2.getSex().getNumber()==0?"男":"女"));
System.out.println("邮箱:"+stu2.getEmail());
//这里phoneNumber字段
List<PhoneNumber> phList = stu2.getPhoneList();
for (PhoneNumber p : phList) {
System.out.println(p.getType().toString()+"电话:"+p.getNumber());
}
} catch (InvalidProtocolBufferException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
程序运行结果截图如下:
相关资源:
一、本文参考资源
1Protobuf 语法指南:
http://ju.outofmemory.cn/entry/109294
http://www.cnblogs.com/stephen-liu74/archive/2013/01/02/2841485.html
2.Eclipse中添加Protobuf支持
http://www.360doc.com/content/14/0718/15/16044571_395291178.shtml
二、本文demo相关资源(个人云盘)
1.protobuf相关资源下载:
http://pan.baidu.com/s/1c0I4cU0
2.本文demo资源:
http://pan.baidu.com/s/1i330Kgl