java序列化机制之protobuf(快速高效跨语言)

简介: 我们之前曾讲过java自带的一种序列化机制,但是这种机制效率太低,有很多缺点。因此也涌现出了很多优秀的系列化框架,比如说protobuf、protostuff、thrift、hession、kryo、avro、fst、msgpack等等。这篇文章我们就看一下第一个序列化框架protobuf,给出一个简单案例,看看其是如何实现的。注:若你对序列化概念和基本使用还有疑惑,可以翻看我之前的文章,或者百度一些基本概念和作用。

一、为什么要使用protobuf?


使用protobuf的原因肯定是为了解决开发中的一些问题,那使用其他的序列化机制会出现什么问题呢?


(1)java默认序列化机制:效率极低,而且还能不能跨语言之间共享数据。

(2)XML常用于与其他项目之间数据传输或者是共享数据,但是编码和解码会造成很大的性能损失。

(3)json格式也是常见的一种,但是在json在解析的时候非常耗时,而且json结构非常占内存。


但是我们protobuf是一种灵活的、高效的、自动化的序列化机制,可以有效的解决上面的问题。现在应该清楚了吧,正是由于目前的机制存在了很多问题,所以才有了这个序列化框架。


二、如何使用protobuf


protobuf这么优秀使用起来也是非常的简单,我们可以给出其主要的三个步骤,然后使用一个基本的案例去实现一下。


(1)定义.proto文件:我们在这个文件中描述我们序列化的信息,类似于bean类。

(2)根据.proto生成对应的类文件,上面这个proto就像是一个模板,现在我们要根据这个模板创建出一个java类。

(3)序列化。


有了这个基本的步骤我们我们就具体去实现一下:


1、下载安装protobuf


第一步:下载解压


我的电脑是windows10,因此这里给出在windows下的使用,linux下面使用我也将在后续文章推出。我们首先下载protobuf(github上搜索protobuf,有各种语言可供选择)。选择protoc-3.9.0-win64.zip。下载完成之后解压到D:/protobuf目录下面就好了。

v2-45781c981d35c05421306ef55a1924f1_1440w.jpg

第二步:配置环境变量

也就是将D:\protobuf\protoc-3.9.0-win64\bin配置到path环境变量里面。


第三步:验证是否安装成功


最后我们可以在cmd中输入protoc --version验证一下,是否成功。

v2-51aacc6a7f89b9d9fbb07aa828a0fdd4_1440w.jpg

OK,到这里我们就安装好了。


2、编写proto文件


之前我们说过proto 文件非常类似java的bean。在这里我们在bin目录下新建一个Person.proto文件(和proto.exe)。输入下列内容:

syntax="proto3"; 
option java_package = "com.fdd.protobuf";   
option java_outer_classname = "PersonProto";   
message Person  { 
  string name = 1;
  int32 age = 2;
}

我们解释一些我们都写了什么


(1)第一行有一个proto3,他表示的是protobuf的语法版本,就类似于jdk1.7和jdk1.8的概念。需要在第一行指定。默认使用的是proto2。

(2)java_package:表示的是java包,不指定就使用 package.生成的类会放到该package下。这里表示把生成的类存放在com.fdd.protobuf包下面。

(3)java_outer_classname:我们说过.proto文件,要生成对应的类,这个参数就指定输出什么类名。这里表示生成的类名是PersonProto。

(4)message 是用于数据格式定义.

  • 一个 .proto 文件中可以定义多个 message
  • message 中定义的字段支持 string、byte、bool、map、enum、数字类型和用户自定义的 message
  • 定义字段后面需要指定唯一的标识数字,这些数字用于识别二进制格式 message 中的字段,一旦开始使用这个 message,那么标识数字就不能改变
  • 如果需要定义 List,则在字段前加repeated即可.
  • 如果已经使用过该 message 生成的类后,想要增加字段直接新增即可.当新增字段的类


  • 解析老数据时,会将新字段置为默认值.当旧的类解析新数据时会忽视掉新增字段.

比如说这里我们就定义一个复杂的proto文件:

syntax = "proto3";
option java_package = "com.fdd.protobuf";
option java_outer_classname = "Persons";
message Staff {
    int32 id = 1;
    string name = 2;
    int32 age = 3;
    // 枚举示例
    enum PhoneType {
        MOBILE = 0;
        TELEPHONE = 1;
    }
    // 嵌套示例
    message PhoneNumber {
        string number = 1;
        PhoneType type = 2;
    } 
    // list示例
    repeated PhoneNumber phone = 4;      
    message Map {
        string key = 1;
        int32 value = 2;
    }
    // map示例
    Map map = 5;
}

由于这里只是展示一个基本案例,对于其他的数据类型可以根据自己的需要定义即可。


3、根据proto文件生成class类文件


编译起来很简单,在我们下载好的protobuf下面有一个bin目录,里面有一个proto.exe。我们就使用这个去编译person.proto文件就好。

v2-e0ffb6d9ce8f7afbe839de7463e6e257_1440w.jpg

也就是执行proto.exe --java_out = / Person.proto就会编译成功。

v2-75df6d1bc7624741b4caa1e58589a73d_1440w.jpg


4、使用class类文件


上面意味着我们已经做好了序列化的准备工作,接下来我们就可以直接使用这个类了。

(1)第一步:将生成的PersonProto类引入到我们的IDEA或者是eclipse中。

(2)第二步:在idea或者是eclipse添加protobuf的依赖。

<dependency>
    <groupId>com.google.protobuf</groupId>
    <artifactId>protobuf-java</artifactId>
    <version>3.9.0</version>
</dependency>

(3)第三步:使用

首先看一些如何序列化:

//1、 创建Builder
PersonProto.Person.Builder builder = PersonProto.Person.newBuilder();
//2、 设置Person的属性
builder.setAge(20);
builder.setName("java的架构师技术栈");
//3、 创建Person
PersonProto.Person person = builder.build();
//4、序列化
byte[] data = person.toByteArray();
//5、将data保存在本地或者是传到网络

然后反序列化

try {
       //一行代码实现反序列化,data可以是本地数据或者是网络数据
       PersonProto.Person person = PersonProto.Person.parseFrom(data);
       System.out.println(person.getAge());
       System.out.println(person.getName());
} catch (InvalidProtocolBufferException e) {
       e.printStackTrace();
}

简单吧,基本上就是我们定义好proto文件,然后使用proto.exe编译成类文件,最后导入这个类和依赖就可以直接使用了。当然这只是一个最简单不过的代码,可以把这篇教程当成一个简单的使用教程。一般情况是是结合SpringBoot来使用的。具体使用会陆续推出。

相关文章
|
10月前
|
JSON 网络协议 安全
【Java】(10)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
439 1
|
10月前
|
JSON 网络协议 安全
【Java基础】(1)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
406 1
|
11月前
|
存储 NoSQL Java
配置RedisTemplate序列化机制
通过上述步骤,你可以灵活配置RedisTemplate的序列化机制,根据应用需求选择合适的序列化器,从而确保数据在Redis中的存储和读取效率最优化。配置合适的序列化机制对于性能和存储效率至关重要,而且这样可以确保数据在存储和传输过程中的结构清晰和一致性。
605 11
|
11月前
|
XML JSON 编解码
从JSON到Protobuf,深入序列化方案的选型与原理
序列化是数据跨边界传输的“翻译官”,将结构化数据转为二进制流。JSON可读性强但冗余大,Protobuf高效紧凑、性能优越,成主流选择。不同场景需权衡标准化与定制优化,选最合适方案。
627 3
|
存储 安全 Java
深入理解Java序列化接口及其实现机制
记住,序列化不仅仅是把对象状态保存下来那么简单,它涉及到类的版本控制、安全性和性能等多个重要方面。正确理解和实现Java序列化机制对于构建高效、安全和可维护的Java应用至关重要。
358 0
|
人工智能 前端开发 安全
Java开发不可不知的秘密:类加载器实现机制
类加载器是Java中负责动态加载类到JVM的组件,理解其工作原理对开发复杂应用至关重要。本文详解类加载过程、双亲委派模型及常见类加载器,并介绍自定义类加载器的实现与应用场景。
414 4
|
JSON Java 数据库连接
|
人工智能 JavaScript Java
Java反射机制及原理
本文介绍了Java反射机制的基本概念、使用方法及其原理。反射在实际项目中比代理更常用,掌握它可以提升编程能力并理解框架设计原理。文章详细讲解了获取Class对象的四种方式:对象.getClass()、类.class、Class.forName()和类加载器.loadClass(),并分析了Class.forName()与ClassLoader的区别。此外,还探讨了通过Class对象进行实例化、获取方法和字段等操作的具体实现。最后从JVM类加载机制角度解析了Class对象的本质及其与类和实例的关系,帮助读者深入理解Java反射的工作原理。
342 0
|
人工智能 Java 关系型数据库
Java——SPI机制详解
SPI(Service Provider Interface)是JDK内置的服务提供发现机制,主要用于框架扩展和组件替换。通过在`META-INF/services/`目录下定义接口实现类文件,Java程序可利用`ServiceLoader`动态加载服务实现。SPI核心思想是解耦,允许不同厂商为同一接口提供多种实现,如`java.sql.Driver`的MySQL与PostgreSQL实现。然而,SPI存在缺陷:需遍历所有实现并实例化,可能造成资源浪费;获取实现类方式不够灵活;多线程使用时存在安全问题。尽管如此,SPI仍是Java生态系统中实现插件化和模块化设计的重要工具。
769 0
|
设计模式 人工智能 安全
AQS:Java 中悲观锁的底层实现机制
AQS(AbstractQueuedSynchronizer)是Java并发包中实现同步组件的基础工具,支持锁(如ReentrantLock、ReadWriteLock)和线程同步工具类(如CountDownLatch、Semaphore)等。Doug Lea设计AQS旨在抽象基础同步操作,简化同步组件构建。 使用AQS需实现`tryAcquire(int arg)`和`tryRelease(int arg)`方法以获取和释放资源,共享模式还需实现`tryAcquireShared(int arg)`和`tryReleaseShared(int arg)`。
596 32
AQS:Java 中悲观锁的底层实现机制