上一篇: Google Protocol Buffer -- Windows下Python的应用
在上一篇博文中我罗略了一些关于Protobuf的资料,以及其在Python下的应用,因为protobuf协议是跨语言平台的,所以本篇基于Java语言下的应用和Python的如出一辙,就不再讲如何安装protobuf和编写proto文件了,本篇从另一个角度解答为什么使用protobuf协议传输通信要比使用xml和json的效率高的多。
一、项目目录结构
二、Pom
<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.appleyk</groupId>
<artifactId>Spring-Boot-Protocol-Buffer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<description>Spring-Boot -- Google Protobuff的应用</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.12.RELEASE</version>
</parent>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 添加热部署 devtools:监听文件变动 -->
<!-- 当Java文件改动时,Spring-boo会快速重新启动 -->
<!-- 最简单的测试,就是随便找一个文件Ctrl+S一下,就可以看到效果 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<!-- optional=true,依赖不会传递 -->
<!-- 本项目依赖devtools;若依赖本项目的其他项目想要使用devtools,需要重新引入 -->
<optional>true</optional>
</dependency>
<!-- Spring 单元测试 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- JUnit单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>3.5.0</version>
</dependency>
</dependencies>
</project>
三、Student实体类
package com.appleyk.protocol.entity;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 学生类
* @author yukun24@126.com
* @blob http://blog.csdn.net/appleyk
* @date 2018年6月13日-下午3:00:33
*/
public class Student {
/**
* ID == 唯一标识
*/
private Long id;
/**
* 姓名
*/
private String name;
/**
* 兴趣爱好
*/
private List<String> hobbies;
/**
* 各科成绩
*/
private Map<String, Double> grades;
public Student(){
hobbies = new ArrayList<>();
grades = new HashMap<>();
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<String> getHobbies() {
return hobbies;
}
public void setHobbies(List<String> hobbies) {
this.hobbies = hobbies;
}
public Map<String, Double> getGrades() {
return grades;
}
public void setGrades(Map<String, Double> grades) {
this.grades = grades;
}
public void addHobby(String hobby){
hobbies.add(hobby);
}
}
四、Proto协议文件
Student.proto
syntax = "proto3";//这个版本的protoc的protobuf编译器已经可以支持proto2语法和proto3的语法
package com.appleyk.protocol.model;
option java_outer_classname = "SObjectModel"; //输出的类名
message Students{
repeated Student students = 1;
message Student{
int64 id = 1 ;
string name = 2 ;
repeated string hobbies = 3 ;
map<string,double> grades = 4 ;
}
}
五、基于demo实现CMD命令行的protoc的编译指令
package com.appleyk.protocol.utils;
import java.io.IOException;
/**
* protoc.exe -I=proto的输入目录 --java_out=java类输出目录 proto的输入目录包括包括proto文件
*
* @author Administrator
*
*/
public class GenerateClass {
public static void main(String[] args) throws IOException {
String protoFile = "Student.proto";
String path = "E:/Spring-boot/Spring-Boot-Protocol-Buffer/src/main/java/com/appleyk/protocol/proto";
String out = "E:/Spring-boot/Spring-Boot-Protocol-Buffer/src/main/java";
String strCmd = "D:/protobuf/src/protoc.exe -I=" + path + " --java_out=" + out + " " + path + "/" + protoFile;
System.out.println(strCmd);
Runtime.getRuntime().exec(strCmd);
System.out.println("完成");
}
}
六、编译Student.proto
编译后,刷新项目,会在model包下多出一个类文件
此文件就是基于proto文件协议编译后的Java类
七、测试SObjectModel类
package com.appleyk.protocol.test;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import com.appleyk.protocol.model.SObjectModel.Students;
import com.appleyk.protocol.model.SObjectModel.Students.Student;
import com.fasterxml.jackson.databind.ObjectMapper;
public class StudentModelTest {
public static void main(String[] args) throws Exception{
/**
* 消息体的构建器,消息体的build方法可以产生对象
*/
Students.Builder sBuilders = Students.newBuilder();
Student.Builder sBuilder = Student.newBuilder();
sBuilder.setId(1001L);
sBuilder.setName("张三");
//添加兴趣爱好
sBuilder.addHobbies("游戏");
sBuilder.addHobbies("睡觉");
sBuilder.addHobbies("篮球");
//添加成绩键值对
sBuilder.putGrades("数学", 95.5);
sBuilder.putGrades("语文", 88.5);
sBuilder.putGrades("英语", 99.0);
Student.Builder sBuilder2 = Student.newBuilder();
sBuilder2.setId(1002L);
sBuilder2.setName("李玟");
//添加兴趣爱好
sBuilder2.addHobbies("逛街");
sBuilder2.addHobbies("看书");
//添加成绩键值对
sBuilder2.putGrades("数学", 85.5);
sBuilder2.putGrades("语文", 92.0);
sBuilder2.putGrades("英语", 95.5);
//学生列表构建 == 添加两个学生builder【消息体】
sBuilders.addStudents(sBuilder);
sBuilders.addStudents(sBuilder2);
//builder 转 对象
Students students = sBuilders.build();
System.out.println("输出对象信息:"+students);
byte[] data = students.toByteArray();
System.out.println("protobuff字节数:"+data.length);
//对象 再次转化成Java实体类
pbfModelToJavaEntity(students.getStudentsList());
}
public static void pbfModelToJavaEntity(List<Student> students) throws Exception{
List<com.appleyk.protocol.entity.Student> stuList = new ArrayList<>();
com.appleyk.protocol.entity.Student stu;
for (Student student : students) {
stu = new com.appleyk.protocol.entity.Student();
stu.setName(student.getName());
stu.setId(student.getId());
//取兴趣爱好
for(int i =0;i<student.getHobbiesCount();i++){
stu.addHobby(student.getHobbies(i));
}
//取各科成绩
Map<String, Double> gradesMap = student.getGradesMap();
stu.setGrades(gradesMap);
stuList.add(stu);
}
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(stuList);
System.out.println("json字节数:"+json.getBytes().length);
}
}
运行结果:
输出对象信息:students {
id: 1001
name: "\345\274\240\344\270\211"
hobbies: "\346\270\270\346\210\217"
hobbies: "\347\235\241\350\247\211"
hobbies: "\347\257\256\347\220\203"
grades {
key: "\346\225\260\345\255\246"
value: 95.5
}
grades {
key: "\350\257\255\346\226\207"
value: 88.5
}
grades {
key: "\350\213\261\350\257\255"
value: 99.0
}
}
students {
id: 1002
name: "\346\235\216\347\216\237"
hobbies: "\351\200\233\350\241\227"
hobbies: "\347\234\213\344\271\246"
grades {
key: "\346\225\260\345\255\246"
value: 85.5
}
grades {
key: "\350\257\255\346\226\207"
value: 92.0
}
grades {
key: "\350\213\261\350\257\255"
value: 95.5
}
}
protobuff字节数:180
json字节数:232
我们可以很清楚的看到,使用proto协议文件构建的对象体要比同信息同内容的Java实体对象【json格式】包含的字节数要少,随之而来的就是传输效率要高了。