Protobuf 入门级指南(谷歌的序列化框架,类似json,但更快)

     最近公司有用到Protobuf这个序列化框架,以前从来没用过,所以今天学习了下,入个门。

    Protobuf 是一种与平台和语言无关的序列化框架,常用于通信协议,数据存储等。

    优点:它的速度比xml和json更快,同时他能将数据压缩的更小,对于结构化数据优势更明显。什么是结构化数据呢?就是带有一定结构的数据。比如电话簿上有很多记录数据,每条记录包含姓名、ID、邮件、电话等,这种结构重复出现。

    缺点:需要你定义proto文件,然后根据proto文件生成对应的java类,然后我们对这个java类进行序列化和反序列化。并且proto文件和生成的java类都比较反人类,难以读懂。 


    安装:


    首先需要安装proto到本地

下载地址:https://github.com/protocolbuffers/protobuf/releases/tag/v3.7.1

   然后将 bin 路径添加到 path 环境变量里,使用命令protoc --version查看是否安装成功。

    使用方法:

    1. 首先定义一个你proto文件ProtobufTest.proto

    

syntax = "proto3"; // PB协议版本


import "google/protobuf/any.proto"; // 引用外部的message,可以是本地的,也可以是此处比较特殊的 Any


option java_package = "com.didispace.web"; // 生成类的包名,注意:会在指定路径下按照该包名的定义来生成文件夹

option java_outer_classname="PersonTestProtos"; // 生成类的类名,注意:下划线的命名会在编译的时候被自动改为驼峰命名


message PersonTest {

    int32 id = 1; // int 类型

    string name = 2; // string 类型

    string email = 3;

    Sex sex = 4; // 枚举类型

    repeated PhoneNumber phone = 5; // 引用下面定义的 PhoneNumber 类型的 message repeated的含义是这里可以放多个PhoneNumber,相当于list

    map tags = 6; // map 类型

    repeated google.protobuf.Any details = 7; // 使用 google 的 any 类型


    // 定义一个枚举

    enum Sex {

        DEFAULT = 0;

        MALE = 1;

        Female = 2;

    }


    // 定义一个 message

    message PhoneNumber {

        string number = 1;

        PhoneType type = 2;


        enum PhoneType {

            MOBILE = 0;

            HOME = 1;

            WORK = 2;

        }


    }


}



    2. 在proto文件所在目录用命令行执行命令,生成java实体类PersonTestProtos

protoc -I=./ --java_out=./ ./ProtobufTest.proto

     然后就能看到对应的目录下生成了实体类。

        


     

        3. 序列化和反序列对应的PersonTestProtos类代码例子如下,同时下方也有protobuf对象和json互转的例子,因为有时候我们与第三方公司交互时,有可能使用到json。


package com.didispace.web;


import com.google.protobuf.ByteString;

import com.google.protobuf.InvalidProtocolBufferException;

import com.googlecode.protobuf.format.JsonFormat;


import java.io.ByteArrayInputStream;

import java.io.ByteArrayOutputStream;

import java.io.IOException;


/**

 * @ClassName: ProtobufTest

 * @Description: ProtoBuf 测试

 **/

public class ProtobufTest {


    public static void main(String[] args) {

        try {

            /** Step1:生成 personTest 对象 */

            // personTest 构造器

            PersonTestProtos.PersonTest.Builder personBuilder = PersonTestProtos.PersonTest.newBuilder();

            // personTest 赋值

            personBuilder.setName("yizhiyuan");

            personBuilder.setEmail("[email protected]");

            personBuilder.setSex(PersonTestProtos.PersonTest.Sex.MALE);


            // 内部的 PhoneNumber 构造器

            PersonTestProtos.PersonTest.PhoneNumber.Builder phoneNumberBuilder = PersonTestProtos.PersonTest.PhoneNumber.newBuilder();

            // PhoneNumber 赋值

            phoneNumberBuilder.setType(PersonTestProtos.PersonTest.PhoneNumber.PhoneType.MOBILE);

            phoneNumberBuilder.setNumber("17717037257");


            // personTest 设置 PhoneNumber

            personBuilder.addPhone(phoneNumberBuilder);


            // 生成 personTest 对象

            PersonTestProtos.PersonTest personTest = personBuilder.build();


            //查看生成的personTest对象

            System.out.println("生成的personTest对象" + personTest.toString());


            // 粘包,将一个或者多个protobuf 对象字节写入 stream

            // 序列化

            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

            personTest.writeDelimitedTo(byteArrayOutputStream);


            // Step2:反序列化,从 steam 中读取一个或者多个 protobuf 字节对象

            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());

            PersonTestProtos.PersonTest personTestResult = PersonTestProtos.PersonTest.parseDelimitedFrom(byteArrayInputStream);

            System.out.println(String.format("反序列化得到的信息,姓名:%s,性别:%d,手机号:%s,邮箱:%s",

                    personTestResult.getName(), personTest.getSexValue(), personTest.getPhone(0).getNumber(),

                    personTest.getEmail()));


            // Step3:protobuf 对象与json的相互转换,有时候我们与第三方系统可能要使用json

            // 将personTest 对象转换为json

            String json =  JsonFormat.printToString(personTest);

            System.out.println("对象转换为string后:"+JsonFormat.printToString(personTest));


            //将json反序列化为protobuf对象

            PersonTestProtos.PersonTest.Builder personBuilderForJson = PersonTestProtos.PersonTest.newBuilder();

            JsonFormat.merge(json,personBuilderForJson);

            System.out.println("json 反序列化为对象:"+ personBuilderForJson.build().toString());

        } catch (InvalidProtocolBufferException e) {

            e.printStackTrace();

        } catch (IOException e) {

            e.printStackTrace();

        }


    }


}


            

            大家如果有任何问题,欢迎关注"Java自学之路"这个公众号来提问,里面也有自学全套视频(含项目)和Idea激活码等信息。

        最后,用命令生成对应的java文件其实挺麻烦的,protobuf也提供了与maven和gradle集成的插件,通过相关配置,在maven的compile或者在gradle的build的时候,可以生成对应的java文件。大家有兴趣可以看看protobuf-maven-plugin 和 protobuf-gradle-plugin 这两个插件。


你可能感兴趣的:(Protobuf 入门级指南(谷歌的序列化框架,类似json,但更快))