Protobuf 简介

protobuf (protocol buffer) 是谷歌内部的混合语言数据标准。通过将结构化的数据进行序列化(串行化),用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。通常说的protobuf包括以下三点:

  • 是一种二进制数据交换格式。支持不同与语言例如C++、java中定义的存储类的内容与二进制序列串相互转换,主要用于数据传输或保存。
  • 定义了一种源文件,扩展名为.proto.使用此文件,可以定义存储类的内容。
  • 可以使用编译器将.proto编译成.cc 或.java,使之成为一个可以在C++或java等工程中直接使用的类。

定义Proto文件

messageprotobuf中定义一个消息类型是通过关键字message字段指定的,这个关键字类似于C++/Java中的class关键字。使用protobuf编译器将proto编译成C++代码之后,每个message都会生成一个名字与之对应的C++类,该类公开继承自google::protobuf::Message

创建turorial.person.proto 文件,文件内容如下:

// FileName: tutorial.person.proto

// 通常文件名建议命名格式为 包名.消息名.proto

// 表示正在使用proto2命令

syntax = "proto2";

//包声明,tutorial 也可以声明为二级类型。

//例如a.b,表示a类别下b子类别

package tutorial;

//编译器将生成一个名为person的类

//类的字段信息包括姓名name,编号id,邮箱email,

//以及电话号码phones

message Person {

  required string name = 1;  // (位置1)

  required int32 id = 2; 

  optional string email = 3;  // (位置2)

  enum PhoneType {  //电话类型枚举值

    MOBILE = 0;  //手机号 

    HOME = 1;    //家庭联系电话

    WORK = 2;    //工作联系电话

  }

 

  //电话号码phone消息体

  //组成包括号码number、电话类型 type

  message PhoneNumber {

    required string number = 1;   

    optional PhoneType type =

          2 [default = HOME]; // (位置3)

  } 

 

  repeated PhoneNumber phones = 4; // (位置4)

}

// 通讯录消息体,包括一个Person类的people

message AddressBook {

  repeated Person people = 1;

}

字段解释:

包声明

proto 文件以package声明开头,这有助于防止不同项目之间命名冲突。在C++中,以package声明的文件内容生成的类将放在与包名匹配的namespace中,上面的.proto文件中所有的声明都属于tutorial

标识符:

protobuf协议的标识符为message或enum,如示例中的Person和PhoneType。message标识一条消息,enum标识一个枚举类型。使用protobuf编译器将协议文件编译后,message和enum都会生成一个类

字段规则

·  repeated: 消息体中可重复字段,重复的值的顺序会被保留(例如位置3)。其中,proto3默认使用packed方式存储,这样编码方式比较节省内存。

标识号

标识号:在消息体的定义中,每个字段都必须要有一个唯一的标识号,标识号是[0,2^29-1]范围内的一个整数。以Person为例,name=1,id=2, email=3, phones=4 中的1-4就是标识号。

数据定义

许多标准的简单数据类型都可以用作message字段类型,包括bool,int32,float,doublestring。还可以使用其他message类型作为字段类型在消息体中添加更多结构。在上面的示例中,Person包含PhoneNumber message, 而AddressBook包含Person message。甚至可以定义嵌套在其他message中的message类型。例如,上面的PhoneNumber定义在Person

 

在java中使用protobuf的简单示例

以下是示例中用到的proto文件teacher.proto:

syntax = "proto3";

package space;

message course{

   // 课程名称

   string name = 1;

   // 课程分数

   int32 score = 2;

}

message student{

// 学生名称

  string name = 1;

   // 学生年龄

   int32 age = 2;

   // 学生所修课程,可能是多门,是一组数组

   repeated course course = 3;

  }

从Release Protocol Buffers v3.5.1 · protocolbuffers/protobuf · GitHub 下载protoc工具,此工具可以根据proto文件生成java类。

下载完成后,解压缩该文件。

打开cmd命令窗口,跳转至解压缩路径:

执行以下命令:

protoc.exe --java_out=src/main/java/ teacher.proto

执行此命令前,需要在新建文件夹目录src/main/java

执行成功后,可以看到在src/main/java 下生成的java类文件Teacher.java。

Protobuf 简介_第1张图片

新建一个maven工程,并导入以下pom依赖

   

        com.google.protobuf

        protobuf-java

        3.21.9

   

   

   

        com.google.protobuf

        protobuf-java-util

        3.21.9

   

    

   

        io.grpc

        grpc-all

        1.11.0

   

   

        

            kr.motd.maven

            os-maven-plugin

            1.5.0.Final

       

   

   

       

            org.xolstice.maven.plugins

            protobuf-maven-plugin

            0.5.0

           

               

                    com.google.protobuf:protoc:3.1.0:exe:${os.detected.classifier}

               

                grpc-java

               

                    io.grpc:protoc-gen-grpc-java:1.11.0:exe:${os.detected.classifier}

               

           

           

               

                   

                        compile

                        compile-custom

                   

               

           

       

   

将Teacher.java 添加至该工程。新建一个测试类TestMyProtobuf.java 进行测试。代码如下:

import com.google.protobuf.InvalidProtocolBufferException;

import com.google.protobuf.util.JsonFormat;

import space.Teacher;

import java.util.Arrays;

public class TestMyProtobuf {

    public static void main(String[] args) {

        // 生成课程1对象

        Teacher.course.Builder courseBuilder1 = Teacher.course.newBuilder();

        courseBuilder1.setName("Java");

        courseBuilder1.setScore(99);

        Teacher.course course1 = courseBuilder1.build();

        // 生成课程2对象

        Teacher.course.Builder courseBuilder2 = Teacher.course.newBuilder();

        courseBuilder2.setName("Python");

        courseBuilder2.setScore(98);

        Teacher.course course2 = courseBuilder2.build();

        // 生成学生对象

        Teacher.student.Builder studentBuilder = Teacher.student.newBuilder();

        studentBuilder.setName("Lucy");

        studentBuilder.setAge(23);

        studentBuilder.addCourse(0,course1);

        studentBuilder.addCourse(1,course2);

        Teacher.student student = studentBuilder.build();

        // proto对象

        System.out.println("The student object is: \n" + student);

        // 序列化

        byte[] studentByte = student.toByteArray();

        System.out.println("The student after encode is:\n" + Arrays.toString(studentByte));

        try {

            // 反序列化

            Teacher.student newStudent = Teacher.student.parseFrom(studentByte);

            System.out.println("The student after decode is:\n" + newStudent);

            // 转换json

            System.out.println("The student json format is:\n" + JsonFormat.printer().print(student));

        } catch (InvalidProtocolBufferException e) {

            e.printStackTrace();

        }

    }

}

测试结果如下:

The student object is:

name: "Lucy"

age: 23

course {

  name: "Java"

  score: 99

}

course {

  name: "Python"

  score: 98

}

The student after encode is:

[10, 4, 76, 117, 99, 121, 16, 23, 26, 8, 10, 4, 74, 97, 118, 97, 16, 99, 26, 10, 10, 6, 80, 121, 116, 104, 111, 110, 16, 98]

The student after decode is:

name: "Lucy"

age: 23

course {

  name: "Java"

  score: 99

}

course {

  name: "Python"

  score: 98

}

The student json format is:

{

  "name": "Lucy",

  "age": 23,

  "course": [{

    "name": "Java",

    "score": 99

  }, {

    "name": "Python",

    "score": 98

  }]

}

 

你可能感兴趣的:(java)