Spring-Boot -- Google Protocol Buffer的应用


上一篇: 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 [email protected]
 * @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格式】包含的字节数要少,随之而来的就是传输效率要高了。




八、项目GitHub地址





猜你喜欢

转载自blog.csdn.net/appleyk/article/details/80682611