这一系列文章主要是对protocol buffer这种编码格式的使用方式、特点、使用技巧进行说明,并在原生protobuf的基础上进行扩展和优化,使得它能更好地为我们服务。
在上一篇文章中
google protocol buffer——protobuf的基本使用和模型分析
我们展示了protobuf在java中的基本使用方式。而本文将继续深入探究protobuf的编码原理。
主要分为两个部分
第一部分是结合上一篇文章留下的几个伏笔展示protobuf的使用特性
第二部分是分析protobuf的编码原理,解释特性背后的原因
第一部分,Protobuf使用特性
1.不同类型对象的转换
我们先定义如下一个.proto文件
syntax = "proto3";option java_package = "cn.tera.protobuf.model";option java_outer_classname = "DifferentModels";message Person {
string name = 1; int32 id = 2; string email = 3;}message Article {
string title = 1; int32 wordsCount = 2; string author = 3;}
其中我们定义了2个模型,一个Person,一个Article,虽然他们的字段名字不相同,但是类型和编号都是一致的
接着我们生成.java文件,最终文件结构如下图
此时我们尝试做如下的一个转换
/** * 测试不同模型间的转换 * @throws Exception */@Testpublic void parseDifferentModelsTest() throws Exception {
//创建一个Person对象 DifferentModels.Person person = DifferentModels.Person.newBuilder() .setName("person name") .setId(1) .setEmail("tera@google.com") .build(); //对person编码 byte[] personBytes = person.toByteArray(); //将编码后的数据直接merge成Article对象 DifferentModels.Article article = DifferentModels.Article.parseFrom(personBytes); System.out.println("article's title:" + article.getTitle()); System.out.println("article's wordsCount:" + article.getWordsCount()); System.out.println("article's author:" + article.getAuthor());}
输出结果如下
article's title:person namearticle's wordsCount:1article's author:tera@google.com
可以看到,虽然jsonBytes是由person对象编码得到的,但是可以用于article对象的解码,不但不会报错,所有的数据内容都是完整保留的
这种兼容性的前提是模型中所定义的字段类型和序号都是一一对应相同的
在平时的编码中,我们经常会遇到从数据库中读取数据模型,然后将其转换成业务模型,而很多时候,这2种模型的内容其实是完全一致的,此时我们也许就可以使用protobuf的这种特性,就可以省去很多低效的赋值代码
2.protobuf序号的重要性
在上一篇文章中,我们看到在定义.proto文件时,字段后面会跟着一个"= X",这里并不是指这个字段的值,而是表示这个字段的“序号”,和正确地编码与解码息息相关,在我看来是protocol buffer的灵魂
我们定义如下的.proto文件,这里注意,Model1和Model2的name和id的序号有不同
syntax = "proto3";option java_package = "cn.tera.protobuf.model";option java_outer_classname = "TagImportance";message Model1 {
string name = 1; int32 id = 2; string email = 3;}message Model2 {
string name = 2; int32 id = 1; string email = 3;}
定义如下的测试方法
/** * 序号的重要性测试 * * @throws Exception */@Testpublic void tagImportanceTest() throws Exception {
TagImportance.Model1 model1 = TagImportance.Model1.newBuilder() .setEmail("model1@google.com") .setId(1) .setName("model1") .build