ProtoBuf的介绍以及在Java中使用protobuf将对象进行序列化与反序列化

场景

ProtoBuf简介

protocol buffers 是一种语言无关、平台无关、可扩展的序列化结构数据的方法,它可用于(数据)通信协议、数据存储等。

Protocol Buffers 是一种灵活,高效,自动化机制的结构数据序列化方法-可类比 XML,但是比 XML 更小(3 ~ 10倍)、更快(20 ~ 100倍)、更为简单。

你可以定义数据的结构,然后使用特殊生成的源代码轻松的在各种数据流中使用各种语言进行编写和读取结构数据。你甚至可以更新数据结构,而不破坏由旧数据结构编译的已部署程序。

ProtocolBuffer拥有多项比XML更高级的串行化结构数据的特性,ProtocolBuffer:

·        更简单

·        小3-10倍

·        快20-100倍

·        更少的歧义

·        可以方便的生成数据存取类

怎样使用ProtoBuf

定义proto描述文件

首先需要在一个 .proto 文件中定义你需要做串行化的数据结构信息。每个ProtocolBuffer信息是一小段逻辑记录,包含一系列的键值对。这里有个非常简单的 .proto 文件定义了个人信息:

message Person {
    required string name=1;
    required int32 id=2;
    optional string email=3;

    enum PhoneType {
        MOBILE=0;
        HOME=1;
        WORK=2;
    }

    message PhoneNumber {
        required string number=1;
        optional PhoneType type=2 [default=HOME];
    }

    repeated PhoneNumber phone=4;
}

当然这里的protobuf的秒数文件.proto也是有版本语法限制的,比如protoBuf3中没有required和optional。

每个消息类型拥有一个或多个特定的数字字段,每个字段拥有一个名字和一个值类型。值类型可以是数字(整数或浮点)、布 尔型、字符串、原始字节或者其他ProtocolBuffer类型,还允许数据结构的分级。

上面的message就类似于类,下面是属性。

required在protobuf2代表是必填的,optional在protobuf2中代表是可选的。repeated代表重复字段,类似于list。

下面的enum代表是枚举。

下面的message是在上面Person的里面,代表内部类。default代表默认值。

需要注意的是这里的=1,=2等都不是对其进行赋值,而是对属性做一个唯一标志。

编译描述文件

一旦你定义了自己的报文格式(message),你就可以运行ProtocolBuffer编译器,将你的 .proto 文件编译成特定语言的类。这些类提供了简单的方法访问每个字段(像是 query() 和set_query()),像是访问类的方法一样将结构串行化或反串行化。

编译器下载地址:

https://github.com/protocolbuffers/protobuf/releases

选择指定的操作系统的指定protobuf版本的protoc编译器进行下载

这里操作系统是Windows64位并且在下面要使用的protobuf的版本是3.13

所以这里下载对应的版本

将其下载到电脑上某路径下解压

后续就可以使用protoc.exe对proto文件进行编译。

为了使用方便,这里将protoc.exe添加进环境变量

然后打开cmd,输入protoc后回车,会输出编译的提示命令则是成功

Java中使用ProtoBuf序列化和反序列化示例

新建项目并引入依赖

打开IDEA-新建一个gradle项目,当然你也可以新建一个Maven项目。

这里以使用gradle作为依赖管理进行示例。

新建gradle项目后选择Java语言,然后添加protobuf相关的依赖。

来到Maven的中央仓库

https://mvnrepository.com/

搜索protobuf

这两个就是需要引入的依赖。

分别进入两个依赖中选择跟上面的编译的版本一致的版本

这里是Gradle的依赖,你也可以使用Maven并复制Maven的依赖。

找到IDEA中新建的gradle项目的build.gradle将两个依赖添加进去。

dependencies {
    compile (
            [group:'com.google.protobuf', name: 'protobuf-java', version: '3.13.0'],
            [group:'com.google.protobuf', name: 'protobuf-java-util', version: '3.13.0']
    )
}

因为设置了自动导入,所以会在项目中引入Google的相关protobuf的依赖的jar包

编写proto描述文件

然后在src下新建protobuf目录,在此目录下新建文件Student.proto

此时IDEA会提示,选择Text即可。

syntax = "proto3";

package com.badao.protobuf;

option optimize_for =SPEED;
option java_package = "com.badao.protobuf";
option java_outer_classname = "DataInfo";

message Student {
    string name = 1;
    int32 age = 2;
    string address = 3;
}

然后将其内容修改如下

这里的syntax代表使用的语法规则是protobuf3

然后下面的package是缺省时的包名,如果下面没有设置java_package则会使用此设置,如果设置了则不会使用。

下面的是一些配置

optimize_for是文件级别的选项,Protocol Buffer定义三种优化级别SPEED/CODE_SIZE/LITE_RUNTIME。缺省情况下是SPEED。

SPEED: 表示生成的代码运行效率高,但是由此生成的代码编译后会占用更多的空间。

然后java_package就是配置的生成代码的包名,

java_outer_classname选项表明想要生成Java类的名称。如果在.proto文件中没有明确的java_outer_classname定义,生成的class名称将会根据.proto文件的名称采用驼峰式的命名方式进行生成。如(foo_bar.proto生成的java类名为FooBar.java)。

这里配置的类名就叫DataInfo。

编译描述文件生成代码

在IDEA中下面的Terminal新建一个终端

因为已经将protoc编译命令的exe添加进环境变量,所以这里直接使用protoc命令即可。

protoc --java_out=src/main/java src/protobuf/Student.proto

这里--java_out=src/main/java就是设置的要生成代码后存放的位置

后面跟着个空格然后跟的是 proto描述文件的位置

回车后如果没有报错,并且已经在src/main/java/com/badao/protobuf下生成DataInfo类

则编译成功。

对象数据的序列化与反序列化

在此包下新建一个ProtobuTest类,并编写main方法

package com.badao.protobuf;

import com.google.protobuf.InvalidProtocolBufferException;

public class ProtobufTest {
    public static void main(String[] args) throws InvalidProtocolBufferException {
        DataInfo.Student student = DataInfo.Student.newBuilder()
                .setName("公众号:霸道的程序猿").setAge(100).setAddress("中国").build();
        byte[] bytes = student.toByteArray();

        DataInfo.Student student1 = DataInfo.Student.parseFrom(bytes);
        System.out.println(student1.getName());
        System.out.println(student1.getAge());
        System.out.println(student1.getAddress());
    }
}

注意这里的新建对象并对属性进行赋值时必须采用如上这种方式

        DataInfo.Student student = DataInfo.Student.newBuilder()
                .setName("公众号:霸道的程序猿").setAge(100).setAddress("中国").build();

然后将对象进行序列化为字节数组就可以通过

byte[] bytes = student.toByteArray();

将字节数据反序列化为对象就可以通过

DataInfo.Student student1 = DataInfo.Student.parseFrom(bytes);

然后运行该main方法。

示例代码下载

https://download.csdn.net/download/BADAO_LIUMANG_QIZHI/12858952

博客园: https://www.cnblogs.com/badaoliumangqizhi/ 关注公众号 霸道的程序猿 获取编程相关电子书、教程推送与免费下载。
原文地址:https://www.cnblogs.com/badaoliumangqizhi/p/13691898.html