Java开发框架选型对比:ruoyi与yudao框架

Posted 小黑上街

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java开发框架选型对比:ruoyi与yudao框架相关的知识,希望对你有一定的参考价值。

1、基础开发框架

        1.1 什么是基础开发框架?

基础框架可以理解为建立一个项目所需的基础框架,这个基础框架为凝聚了之前开发项目的通用、共性的方法、工具、技术等组成的代码包。

现有我们公司有两类基础框架:
(1)基于web应用的基础框架

(2)基于数据迁移场景的基础框架

大家可根据具体的实际场景制定试用自己的基础框架。

        1.2 为什么需要基础框架?

做开发的同事可能都知道,每次研究一项新技术含定时任务、权限、文件上传等这类都需要花时间研究与调试。但是只要第一次研究通了,后面开发效率就快了。

若对以往的项目不进行总结:

A写了定时任务相关代码 B 写了文件上传相关代码 C写了权限相关代码

现有D项目需要同时有定时任务、文件上传、权限管理相关功能,如何处理?

传统方式:逐一搜寻A、B、C复制相关代码,此效率极为低下,因此基础框架就有了必要性。

基础框架方式:封装相关代码,形成common层,并搭建模板项目作为基础框架供其他项目调用

        1.3 现有基础框架有哪些?

力软、JEECGBOOT、若依、芋道。其中力软为付费产品,JEECGBOOT的码云star为4.1k,若依的码云star为30.9k,芋道的码云star为13.8K。综合可见芋道和若依框架分值相对较高。

2 若依框架

官方网站:RuoYi 若依官方网站 |后台管理系统|权限管理系统|快速开发框架|企业管理系统|开源框架|微服务框架|前后端分离框架|开源后台系统|RuoYi|RuoYi-Vue|RuoYi-Cloud|RuoYi框架|RuoYi开源|RuoYi视频|若依视频|RuoYi开发文档|若依开发文档|Java开源框架|Java|SpringBoot|SrpingBoot2.0|SrpingCloud|Alibaba|MyBatis|Shiro|OAuth2.0|Thymeleaf|BootStrap|Vue|Element-UI||www.ruoyi.viphttp://www.ruoyi.vip/

主要包含:RuoYi  RuoYi-Vue  RuoYi-Cloud,分别为快速开发版,前后端分离版,微服务版。

生态齐全,并拥有拓展生态含uniapp版本、pgsql版本、mybaitplus版本、国产数据库版本、react、ant desing版本等

        2.1前端页面

  • 用户管理:用户是系统操作者,该功能主要完成系统用户配置。
  • 部门管理:配置系统组织机构(公司、部门、小组),树结构展现支持数据权限。
  • 岗位管理:配置系统用户所属担任职务。
  • 菜单管理:配置系统菜单,操作权限,按钮权限标识等。
  • 角色管理:角色菜单权限分配、设置角色按机构进行数据范围权限划分。
  • 字典管理:对系统中经常使用的一些较为固定的数据进行维护。
  • 参数管理:对系统动态配置常用参数。
  • 通知公告:系统通知公告信息发布维护。
  • 操作日志:系统正常操作日志记录和查询;系统异常信息日志记录和查询。
  • 登录日志:系统登录日志记录查询包含登录异常。
  • 在线用户:当前系统中活跃用户状态监控。
  • 定时任务:在线(添加、修改、删除)任务调度包含执行结果日志。
  • 代码生成:前后端代码的生成(java、html、xml、sql)支持CRUD下载 。
  • 系统接口:根据业务代码自动生成相关的api接口文档。
  • 服务监控:监视当前系统CPU、内存、磁盘、堆栈等相关信息。
  • 缓存监控:对系统的缓存信息查询,命令统计等。
  • 在线构建器:拖动表单元素生成相应的Vue代码。
  • 连接池监视:监视当期系统数据库连接池状态,可进行分析SQL找出系统性能瓶颈。

 

        2.2系统架构

主要层及架构

1、系统环境

  • Java EE 8
  • Servlet 3.0
  • Apache Maven 3

2、主框架

  • Spring Boot 2.2.x
  • Spring Framework 5.2.x
  • Spring Security 5.2.x

3、持久层

  • Apache MyBatis 3.5.x
  • Hibernate Validation 6.0.x
  • Alibaba Druid 1.2.x

4、视图层

  • Vue 2.6.x
  • Axios 0.21.x
  • Element 2.15.x

主要的工具如下

 3、芋道框架

项目地址

ruoyi-vue-pro: 🔥 官方推荐 🔥 RuoYi-Vue 全新 Pro 版本,优化重构所有功能。基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 微信小程序,支持 RBAC 动态权限、数据权限、SaaS 多租户、Flowable 工作流、三方登录、支付、短信、商城等功能。你的 ⭐️ Star ⭐️,是作者生发的动力!https://gitee.com/zhijiantianya/ruoyi-vue-pro?_from=gitee_search

        3.1 前端页面

 

        3.2 功能清单

  • 后端采用 Spring Boot、mysql + MyBatis Plus、Redis + Redisson。
  • 数据库可使用 MySQL、Oracle、PostgreSQL、SQL Server、MariaDB、国产达梦 DM、TiDB 等
  • 权限认证使用 Spring Security & Token & Redis,支持多终端、多种用户的认证系统。
  • 支持加载动态权限菜单,按钮级别权限控制,本地缓存提升性能。
  • 支持 SaaS 多租户系统,可自定义每个租户的权限,提供透明化的多租户底层封装。
  • 工作流使用 Flowable,支持动态表单、在线设计流程、会签 / 或签、多种任务分配方式。
  • 高效率开发,使用代码生成器可以一键生成前后端代码 + 单元测试 + Swagger 接口文档 + Validator 参数校验。
  • 集成微信小程序、微信公众号、企业微信、钉钉等三方登陆,集成支付宝、微信等支付与退款。
  • 集成阿里云、腾讯云、云片等短信渠道,集成 MinIO、阿里云、腾讯云、七牛云等云存储服务。

4 框架对比

        4.1 若依框架优势

1、功能简洁包含项目需要的最小框架:定时任务、系统参数、字典、通知公告、代码生成,启动项目速度快

2、具备完整的生态,查看其他语言代码方便

        4.2 芋道框架优势

1、功能模块多,集成mapstruct、mybaitplus、Oauth2 、短信、错误码,启动速度慢

2、集成特定场景应用:支付、工作流

3、框架复杂,一些场景排查问题较难,有严格的VO 与APP\\ADMIN接口区分,以及业务端和内部接口区分。代码框架接口清晰便捷明显,系统之间相互调用较多。

4、自动编写单元测试,每次打包时会利用的sqlite数据库进行单元测试。

5 框架选型

建议小团队使用若依框架,以下内容为亲自使用总结

(1)开发速度快,问题定位快

(2)可寻找任何语言开源替代(例如vue3的框架可能在IE9下不支持,需要换VUE2或者其他框架),或者项目团队成员只会net或python语言,这些都可以找到替代产品;国产化软件也可以找到相关替代。

建议大团队使用芋道框架

(1)APP端和pc端严格区分;业务API与前端接口严格区分

(2)有较多开源的拓展功能,集成了Hutool工具包,支持包含文件上传、小程序登录三方登录、工作流、短信、多租户、支付等功能。

(3)初始内置多种数据库脚本,可天然支持多种数据库含国产数据库。

6、若依基础框架项目分享

目前用若依的mybaitplus做了两个示例,分别包含单表查询、多表查询的案例。中间使用了代码生成方法。

        6.1单表查询

(1)首先新建数据库表

(2)点开代码生成页面,配置列表、查询、搜索字段,对于字典或者时间范围类型等条件可对应进行配置

(3)生成代码,前端拷贝、后端拷贝。

        6.2多表联合查询

(1)后端首先以单表模式生成增删改查

(2)在对应controller增加新的查询接口

(3)如果是表格页面,则前端复制页面的vue和api,直接进行修改及编写;表单可借助form-generator进行搭建。

具体若依案例项目含(单表及多表案例)的项目代码见链接.

若依mybaitplus基础框架

几种Java常用序列化框架的选型与对比


一  背景介绍

序列化与反序列化是我们日常数据持久化和网络传输中经常使用的技术,但是目前各种序列化框架让人眼花缭乱,不清楚什么场景到底采用哪种序列化框架。本文会将业界开源的序列化框架进行对比测试,分别从通用性、易用性、可扩展性、性能和数据类型与Java语法支持五方面给出对比测试。

  • 通用性:通用性是指序列化框架是否支持跨语言、跨平台。


  • 易用性:易用性是指序列化框架是否便于使用、调试,会影响开发效率。


  • 可扩展性:随着业务的发展,传输实体可能会发生变化,但是旧实体有可能还会被使用。这时候就需要考虑所选择的序列化框架是否具有良好的扩展性。


  • 性能:序列化性能主要包括时间开销和空间开销。序列化的数据通常用于持久化或网络传输,所以其大小是一个重要的指标。而编解码时间同样是影响序列化协议选择的重要指标,因为如今的系统都在追求高性能。


  • Java数据类型和语法支持:不同序列化框架所能够支持的数据类型以及语法结构是不同的。这里我们要对Java的数据类型和语法特性进行测试,来看看不同序列化框架对Java数据类型和语法结构的支持度。


下面分别对JDK Serializable、FST、Kryo、Protobuf、Thrift、Hession和Avro进行对比测试。

二  序列化框架

1  JDK Serializable

JDK Serializable是Java自带的序列化框架,我们只需要实现 java.io.Serializable java.io.Externalizable 接口,就可以使用Java自带的序列化机制。实现序列化接口只是表示该类能够被序列化/反序列化,我们还需要借助I/O操作的ObjectInputStream和ObjectOutputStream对对象进行序列化和反序列化。

下面是使用JDK 序列化框架进行编解码的Demo:

/** * 编码 */public static byte[] encoder(Object ob) throws Exception{    //用于缓冲字节数字    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();    //序列化对象    ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);    objectOutputStream.writeObject(ob);    byte[] result = byteArrayOutputStream.toByteArray();    //关闭流    objectOutputStream.close();    byteArrayOutputStream.close();    return result;}/** * 解码 */public static <T> decoder(byte[] bytes) throws Exception {    ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);    ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);    T object = (T) objectInputStream.readObject();    objectInputStream.close();    byteArrayInputStream.close();    return object;}

通用性

由于是Java内置序列化框架,所以本身是不支持跨语言序列化与反序列化。

易用性

作为Java内置序列化框架,无序引用任何外部依赖即可完成序列化任务。但是JDK Serializable在使用上相比开源框架难用许多,可以看到上面的编解码使用非常生硬,需要借助ByteArrayOutputStream和ByteArrayInputStream才可以完整字节的转换。

可扩展性

JDK Serializable中通过serialVersionUID控制序列化类的版本,如果序列化与反序列化版本不一致,则会抛出 java.io.InvalidClassException 异常信息,提示序列化与反序列化SUID不一致。

java.io.InvalidClassException: com.yjz.serialization.java.UserInfo; local class incompatible: stream classdesc serialVersionUID = -5548195544707231683, local class serialVersionUID = -5194320341014913710

上面这种情况,是由于我们没有定义serialVersionUID,而是由JDK自动hash生成的,所以序列化与反序列化前后结果不一致。

但是我们可以通过自定义serialVersionUID方式来规避掉这种情况(序列化前后都是使用定义的serialVersionUID),这样JDK Serializable就可以支持字段扩展了。

private static final long serialVersionUID = 1L;
 
性能

JDK Serializable是Java自带的序列化框架,但是在性能上其实一点不像亲生的。下面测试用例是我们贯穿全文的一个测试实体。

public class MessageInfo implements Serializable {
private String username; private String password; private int age; private HashMap<String,Object> params; ... public static MessageInfo buildMessage() { MessageInfo messageInfo = new MessageInfo(); messageInfo.setUsername("abcdefg"); messageInfo.setPassword("123456789"); messageInfo.setAge(27); Map<String,Object> map = new HashMap<>(); for(int i = 0; i< 20; i++) { map.put(String.valueOf(i),"a"); } return messageInfo; }}

使用JDK序列化后字节大小为:432。光看这组数字也许不会感觉到什么,之后我们会拿这个数据和其它序列化框架进行对比。

我们对该测试用例进行1000万次序列化,然后计算时间总和:

1000万序列化耗时(ms) 1000万反序列化耗时(ms)
38952 96508

同样我们之后会同其它序列化框架进行对比。

数据类型和语法结构支持性

由于JDK Serializable是Java语法原生序列化框架,所以基本都能够支持Java数据类型和语法。


JDK
8种基础类型 支持
List集合类 支持
Set集合类 支持
Queue集合类 支持
Map映射 大部分支持(WeakHashMap不支持)
自定义类类型 支持
枚举类型 支持

WeakHashMap没有实现Serializable接口。


JDK
对象为null 支持
没有无参构造函 支持
static内部类 支持(static内部类需要实现序列化接口)
非static内部类 支持,但是外部类也需要实现序列化接口
局部内部类 支持
匿名内部类 支持
Lambda表达式 修改代码可以支持,看注1
闭包 支持
异常类 支持

注1:但我们要序列化下面代码:

Runnable runnable = () -> System.out.println("Hello");

直接序列化会得到以下异常:

com.yjz.serialization.SerializerFunctionTest$$Lambda$1/189568618

原因就是我们Runnable的Lambda并没有实现Serializable接口。我们可以做如下修改,即可支持Lambda表达式序列化。

Runnable runnable = (Runnable & Serializable) () -> System.out.println("Hello");

2  FST序列化框架

FST(fast-serialization)是完全兼容JDK序列化协议的Java序列化框架,它在序列化速度上能达到JDK的10倍,序列化结果只有JDK的1/3。目前FST的版本为2.56,在2.17版本之后提供了对Android的支持。

下面是使用FST序列化的Demo,FSTConfiguration是线程安全的,但是为了防止频繁调用时其成为性能瓶颈,一般会使用TreadLocal为每个线程分配一个FSTConfiguration。

private final ThreadLocal<FSTConfiguration> conf = ThreadLocal.withInitial(() -> {      FSTConfiguration conf = FSTConfiguration.createDefaultConfiguration();      return conf;  });
public byte[] encoder(Object object) {    return conf.get().asByteArray(object);}
public <T> decoder(byte[] bytes) {    Object ob = conf.get().asObject(bytes);    return (T)ob;}

通用性

FST同样是针对Java而开发的序列化框架,所以也不存在跨语言特性。

易用性

在易用性上,FST可以说能够甩JDK Serializable几条街,语法极其简洁,FSTConfiguration封装了大部分方法。

可扩展性

FST通过@Version注解能够支持新增字段与旧的数据流兼容。对于新增的字段都需要通过@Version注解标识,没有版本注释意味着版本为0。

private String origiField;@Version(1)private String addField;

注意:

  • 删除字段将破坏向后兼容性,但是如果我们在原始字段情况下删除字段是能够向后兼容的(没有新增任何字段)。但是如果新增字段后,再删除字段的话就会破坏其兼容性。


  • Version注解功能不能应用于自己实现的readObject/writeObject情况。


  • 如果自己实现了Serializer,需要自己控制Version。


综合来看,FST在扩展性上面虽然支持,但是用起来还是比较繁琐的。

性能

使用FST序列化上面的测试用例,序列化后大小为:172,相比JDK序列化的432 ,将近减少了1/3。下面我们再看序列化与反序列化的时间开销。

1000万序列化耗时(ms) 1000万反序列化耗时(ms)
13587 19031

我们可以优化一下FST,将循环引用判断关闭,并且对序列化类进行余注册。

private static final ThreadLocal<FSTConfiguration> conf = ThreadLocal.withInitial(() -> {      FSTConfiguration conf = FSTConfiguration.createDefaultConfiguration();      conf.registerClass(UserInfo.class);      conf.setShareReferences(false);      return conf;  });

通过上面的优化配置,得到的时间开销如下:

1000万序列化耗时(ms) 1000万反序列化耗时(ms)
7609 17792

可以看到序列化时间将近提升了2倍,但是通过优化后的序列化数据大小增长到了191 。

数据类型和语法结构支持性

FST是基于JDK序列化框架而进行开发的,所以在数据类型和语法上和Java支持性一致。


FST
8种基础类型 支持
List集合类 支持
Set集合类 支持
Queue集合类 支持
Map映射 大部门支持(WeakHashMap不支持)
自定义类类型 支持
枚举类型 支持


FST
对象为null 支持
没有无参构造函数 支持
static内部类 支持(static内部类需要实现序列化接口)
非static内部类 支持,但是外部类也需要实现序列化接口
局部内部类 支持
匿名内部类 支持
Lambda表达式 修改代码可以支持(同JDK)
闭包 支持
异常类 支持

3  Kryo序列化框架

Kryo一个快速有效的Java二进制序列化框架,它依赖底层ASM库用于字节码生成,因此有比较好的运行速度。Kryo的目标就是提供一个序列化速度快、结果体积小、API简单易用的序列化框架。Kryo支持自动深/浅拷贝,它是直接通过对象->对象的深度拷贝,而不是对象->字节->对象的过程。

下面是使用Kryo进行序列化的Demo:

private static final ThreadLocal<Kryo> kryoLocal = ThreadLocal.withInitial(() -> { Kryo kryo = new Kryo(); kryo.setRegistrationRequired(false);//不需要提前预注册类 return kryo; });
public static byte[] encoder(Object object) { Output output = new Output(); kryoLocal.get().writeObject(output,object); output.flush(); return output.toBytes(); }
public static <T> T decoder(byte[] bytes) { Input input = new Input(bytes); Object ob = kryoLocal.get().readClassAndObject(input); return (T) ob; }

需要注意的是使用Output.writeXxx时候一定要用对应的Input.readxxx,比如Output.writeClassAndObject()要与Input.readClassAndObject()。


通用性

首先Kryo官网说自己是一款Java二进制序列化框架,其次在网上搜了一遍没有看到Kryo的跨语言使用,只是一些文章提及了跨语言使用非常复杂,但是没有找到其它语言的相关实现。

易用性

在使用方式上Kryo提供的API也是非常简洁易用,Input和Output封装了你几乎能够想到的所有流操作。Kryo提供了丰富的灵活配置,比如自定义序列化器、设置默认序列化器等等,这些配置使用起来还是比较费劲的。

可扩展性

Kryo默认序列化器FiledSerializer是不支持字段扩展的,如果想要使用扩展序列化器则需要配置其它默认序列化器。

比如:

private static final ThreadLocal<Kryo> kryoLocal = ThreadLocal.withInitial(() -> { Kryo kryo = new Kryo(); kryo.setRegistrationRequired(false); kryo.setDefaultSerializer(TaggedFieldSerializer.class); return kryo; });

性能

使用Kryo测试上面的测试用例,Kryo序列化后的字节大小为172 ,和FST未经优化的大小一致。时间开销如下:

1000万序列化时间开销(ms) 1000万反序列化时间开销(ms)
13550 14315

我们同样关闭循环引用配置和预注册序列化类,序列化后的字节大小为120,因为这时候类序列化的标识是使用的数字,而不是类全名。使用的是时间开销如下:

1000万序列化时间开销(ms) 1000万反序列化时间开销(ms)
11799 11584

数据类型和语法结构支持性

Kryo对于序列化类的基本要求就是需要含有无参构造函数,因为反序列化过程中需要使用无参构造函数创建对象。


Kryo
8种基础类型 支持
List集合类 支持
Set集合类 支持
Queue集合类 部分支持(ArrayBlockingQueue不支持)
Map映射
自定义类类型
枚举类型


Kryo
对象为null 支持
没有无参构造函数 不支持
static内部类 支持
非static内部类 不支持
局部内部类 支持
匿名内部类 支持
Lambda表达式 不支持
闭包 支持
异常类 不支持(StackOverflowError)

4  Protocol buffer

Protocol buffer是一种语言中立、平台无关、可扩展的序列化框架。Protocol buffer相较于前面几种序列化框架而言,它是需要预先定义Schema的。

下面是使用Protobuf的Demo:

(1)编写proto描述文件:

syntax = "proto3";
option java_package = "com.yjz.serialization.protobuf3";
message MessageInfo{ string username = 1; string password = 2; int32 age = 3; map<string,string> params = 4;}

(2)生成Java代码:

protoc --java_out=./src/main/java message.proto

(3)生成的Java代码,已经自带了编解码方法:

//编码byte[] bytes = MessageInfo.toByteArray()//解码MessageInfo messageInfo = Message.MessageInfo.parseFrom(bytes);

通用性

protobuf设计之初的目标就是能够设计一款与语言无关的序列化框架,它目前支持了Java、Python、C++、Go、C#等,并且很多其它语言都提供了第三方包。所以在通用性上,protobuf是非常给力的。

易用性

protobuf需要使用IDL来定义Schema描述文件,定义完描述文件后,我们可以直接使用protoc来直接生成序列化与反序列化代码。所以,在使用上只需要简单编写描述文件,就可以使用protobuf了。

可扩展性

可扩展性同样是protobuf设计之初的目标之一,我们可以非常轻松的在.proto文件进行修改。

新增字段:对于新增字段,我们一定要保证新增字段要有对应的默认值,这样才能够与旧代码交互。相应的新协议生成的消息,可以被旧协议解析。

删除字段:删除字段需要注意的是,对应的字段、标签不能够在后续更新中使用。为了避免错误,我们可以通过reserved规避带哦。

message userinfo{ reserved 3,7; //在保留标签中,添加删除的字段标签 reserved "age","sex" //在保留字段中,添加删除的字段}

protobuf在数据兼容性上也非常友好,int32、unit32、int64、unit64、bool是完全兼容的,所以我们可以根据需要修改其类型。

通过上面来看,protobuf在扩展性上做了很多,能够很友好的支持协议扩展。

性能

我们同样使用上面的实例来进行性能测试,使用protobuf序列化后的字节大小为 192,下面是对应的时间开销。

1000万数据序列化耗时(ms) 1000万数据反序列化耗时(ms)
14235 30694

可以看出protobuf的反序列化性能要比FST、Kryo差一些。

数据类型和语法结构支持

Protobuf使用IDL定义Schema所以不支持定义Java方法,下面序列化变量的测试:


Protobuf
8种基础类型 基本支持(无byte、shot、char)
List集合类 支持
Set集合类 支持
Queue集合类 支持
Map映射 支持
自定义类类型 支持
枚举类型 支持

注:List、Set、Queue通过protobuf repeated定义测试的。只要实现Iterable接口的类都可以使用repeated列表。

5  Thrift序列化框架

Thrift是由Facebook实现的一种高效的、支持多种语言的远程服务调用框架,即RPC(Remote Procedure Call)。后来Facebook将Thrift开源到Apache。可以看到Thrift是一个RPC框架,但是由于Thrift提供了多语言之间的RPC服务,所以很多时候被用于序列化中。

使用Thrift实现序列化主要分为三步,创建thrift IDL文件、编译生成Java代码、使用TSerializer和TDeserializer进行序列化和反序列化。

(1)使用Thrift IDL定义thrift文件:

namespace java com.yjz.serialization.thrift
struct MessageInfo{ 1: string username; 2: string password; 3: i32 age; 4: map<string,string> params;}

(2)使用thrift编译器生成Java代码:

thrift --gen java message.thrift

(3)使用TSerializer和TDeserializer进行编解码:

 public static byte[] encoder(MessageInfo messageInfo) throws Exception{ TSerializer serializer = new TSerializer(); return serializer.serialize(messageInfo); } public static MessageInfo decoder(byte[] bytes) throws Exception{ TDeserializer deserializer = new TDeserializer(); MessageInfo messageInfo = new MessageInfo(); deserializer.deserialize(messageInfo,bytes); return messageInfo; }
 
通用性

Thrift和protobuf类似,都需要使用IDL定义描述文件,这是目前实现跨语言序列化/RPC的一种有效方式。Thrift目前支持 C++、Java、Python、PHP、Ruby、 Erlang、Perl、Haskell、C#、Cocoa、JavaScript、Node.js、Smalltalk、OCaml、Delphi等语言,所以可以看到Thrift具有很强的通用性。

易用性

Thrift在易用性上和protobuf类似,都需要经过三步:使用IDL编写thrift文件、编译生成Java代码和调用序列化与反序列化方法。protobuf在生成类中已经内置了序列化与反序列化方法,而Thrift需要单独调用内置序列化器来进行编解码。

可扩展性

Thrift支持字段扩展,在扩展字段过程中需要注意以下问题:

  • 修改字段名称:修改字段名称不影响序列化与反序列化,反序列化数据赋值到更新过的字段上。因为编解码过程利用的是编号对应。


  • 修改字段类型:修改字段类型,如果修改的字段为optional类型字段,则返回数据为null或0(数据类型默认值)。如果修改是required类型字段,则会直接抛出异常,提示字段没有找到。


  • 新增字段:如果新增字段是required类型,则需要为其设置默认值,负责在反序列化过程抛出异常。如果为optional类型字段,反序列化过程不会存在该字段(因为optional字段没有赋值的情况,不会参与序列化与反序列化)。如果为缺省类型,则反序列化值为null或0(和数据类型有关)。


  • 删除字段:无论required类型字段还是optional类型字段,都可以删除,不会影响反序列化。


  • 删除后的字段整数标签不要复用,负责会影响反序列化。


性能

上面的测试用例,使用Thrift序列化后的字节大小为:257,下面是对应的序列化时间与反序列化时间开销:

1000万序列化时间开销(ms) 1000万反序列化时间开销(ms)
28634 20722

Thrift在序列化和反序列化的时间开销总和上和protobuf差不多,protobuf在序列化时间上更占优势,而Thrift在反序列化上有自己的优势。

数据类型和语法结构支持

数据类型支持:由于Thrift使用IDL来定义序列化类,所以能够支持的数据类型就是Thrift数据类型。Thrift所能够支持的Java数据类型:

  1. 8中基础数据类型,没有short、char,只能使用double和String代替。

  2. 集合类型,支持List、Set、Map,不支持Queue。

  3. 自定义类类型(struct类型)。

  4. 枚举类型。

  5. 字节数组。


Thrift同样不支持定义Java方法。

6  Hessian序列化框架

Hessian是caucho公司开发的轻量级RPC(Remote Procedure Call)框架,它使用HTTP协议传输,使用Hessian二进制序列化。

Hessian由于其支持跨语言、高效的二进制序列化协议,被经常用于序列化框架使用。Hessian序列化协议分为Hessian1.0和Hessian2.0,Hessian2.0协议对序列化过程进行了优化(优化内容待看),在性能上相较Hessian1.0有明显提升。

使用Hessian序列化非常简单,只需要通过HessianInput和HessianOutput即可完成对象的序列化,下面是Hessian序列化的Demo:
public static <T> byte[] encoder2(T obj) throws Exception{ ByteArrayOutputStream bos = new ByteArrayOutputStream(); Hessian2Output hessian2Output = new Hessian2Output(bos); hessian2Output.writeObject(obj); return bos.toByteArray(); }
public static <T> T decoder2(byte[] bytes) throws Exception { ByteArrayInputStream bis = new ByteArrayInputStream(bytes); Hessian2Input hessian2Input = new Hessian2Input(bis); Object obj = hessian2Input.readObject(); return (T) obj; }
 
通用性

Hessian与Protobuf、Thrift一样,支持跨语言RPC通信。Hessian相比其它跨语言PRC框架的一个主要优势在于,它不是采用IDL来定义数据和服务,而是通过自描述来完成服务的定义。目前Hessian已经实现了语言包括:Java、Flash/Flex、Python、C++、.Net/C#、D、Erlang、PHP、Ruby、Object-C。

易用性

相较于Protobuf和Thrift,由于Hessian不需要通过IDL来定义数据和服务,对于序列化的数据只需要实现Serializable接口即可,所以使用上相比Protobuf和Thrift更加容易。

可扩展性

Hession序列化类虽然需要实现Serializable接口,但是它并不受serialVersionUID影响,能够轻松支持字段扩展。

  1. 修改字段名称:反序列化后新字段名称为null或0(受类型影响)。

  2. 新增字段:反序列化后新增字段为null或0(受类型影响)。

  3. 删除字段:能够正常反序列化。

  4. 修改字段类型:如果字段类型兼容能够正常反序列化,如果不兼容则直接抛出异常。


性能

使用Hessian1.0协议序列化上面的测试用例,序列化结果大小为277。使用Hessian2.0序列化协议,序列化结果大小为178。

序列化化与反序列化的时间开销如下:


1000万序列化时间开销(ms) 1000万反序列化时间开销(ms)
Hessian1.0 57648 55261
Hessian2.0 38823 17682

可以看到Hessian1.0的无论在序列化后体积大小,还是在序列化、反序列化时间上都比Hessian2.0相差很远。

数据类型和语法结构支持

由于Hession使用Java自描述序列化类,所以Java原生数据类型、集合类、自定义类、枚举等基本都能够支持(SynchronousQueue不支持),Java语法结构也能够很好的支持。

7  Avro序列化框架

Avro是一个数据序列化框架。它是Apache Hadoop下的一个子项目,由Doug Cutting主导Hadoop过程中开发的数据序列化框架。Avro在设计之初就用于支持数据密集型应用,很适合远程或本地大规模数据交换和存储。

使用Avro序列化分为三步:

(1)定义avsc文件:

{ "namespace": "com.yjz.serialization.avro", "type": "record", "name": "MessageInfo", "fields": [ {"name": "username","type": "string"}, {"name": "password","type": "string"}, {"name": "age","type": "int"}, {"name": "params","type": {"type": "map","values": "string"} } ]}

(2)使用avro-tools.jar编译生成Java代码(或maven编译生成):

java -jar avro-tools-1.8.2.jar compile schema src/main/resources/avro/Message.avsc ./src/main/java

(3)借助BinaryEncoder和BinaryDecoder进行编解码:
public static byte[] encoder(MessageInfo obj) throws Exception{ DatumWriter<MessageInfo> datumWriter = new SpecificDatumWriter<>(MessageInfo.class); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); BinaryEncoder binaryEncoder = EncoderFactory.get().directBinaryEncoder(outputStream,null); datumWriter.write(obj,binaryEncoder); return outputStream.toByteArray(); }
public static MessageInfo decoder(byte[] bytes) throws Exception{ DatumReader<MessageInfo> datumReader = new SpecificDatumReader<>(MessageInfo.class); BinaryDecoder binaryDecoder = DecoderFactory.get().directBinaryDecoder(new ByteArrayInputStream(bytes),null); return datumReader.read(new MessageInfo(),binaryDecoder); }

通用性

Avro通过Schema定义数据结构,目前支持Java、C、C++、C#、Python、PHP和Ruby语言,所以在这些语言之间Avro具有很好的通用性。

易用性

Avro对于动态语言无需生成代码,但对于Java这类静态语言,还是需要使用avro-tools.jar来编译生成Java代码。在Schema编写上,个人感觉相比Thrift、Protobuf更加复杂。

可扩展性

  1. 给所有field定义default值。如果某field没有default值,以后将不能删除该field。

  2. 如果要新增field,必须定义default值。

  3. 不能修改field type。

  4. 不能修改field name,不过可以通过增加alias解决。


性能

使用Avro生成代码序列化之后的结果为:111。下面是使用Avro序列化的时间开销:


1000万序列化时间开销(ms) 1000万序反列化时间开销(ms)
生成Java代码 26565 45383

数据类型和语法结构支持

Avro需要使用Avro所支持的数据类型来编写Schema信息,所以能够支持的Java数据类型即为Avro所支持的数据类型。Avro支持数据类型有:基础类型(null、boolean、int、long、float、double、bytes、string),复杂数据类型(Record、Enum、Array、Map、Union、Fixed)。

Avro自动生成代码,或者直接使用Schema,不能支持在序列化类中定义java方法。

三  总结

1  通用性

下面是从通用性上对比各个序列化框架,可以看出Protobuf在通用上是最佳的,能够支持多种主流变成语言。

序列化框架 通用性
JDK Serializer 只适用于Java
FST 只适用于Java
Kryo 主要适用于Java(可复杂支持跨语言)
Protocol buffer 支持多种语言
Thrift 支持多种语言
Hessian 支持多种语言
Avro 支持多种语言

2  易用性

下面是从API使用的易用性上面来对比各个序列化框架,可以说除了JDK Serializer外的序列化框架都提供了不错API使用方式。

序列化框架 易用性
JDK Serializer 使用语法过于生硬
FST 使用简洁,FSTConfiguration提供了序列化与反序列化的方法
Kryo 使用简洁,Input/Output封装了几乎所有能有需要的流方法
Protocol buffer 稍微复杂。需要编写所需序列化类的proto文件,然后编译生成Java代码。但是自动生成Java类,包含了序列化与反序列化方法
Thrift 稍微复杂。需要编写所需的序列化类的thrift文件,然后编译生成Java代码。然后通过TSerializer和TDserializer进行序列化与反序列化
Hessian 使用简单,在跨语言的基础上不需要使用IDL
Avro 使用较复杂。相较于Protobuf和Thrift来说,对于一些静态语言无序生成代码。但是对于Java来一般还需要生成代码,并且Avro提供的API不是很友好

3  可扩展性

下面是各个序列化框架的可扩展性对比,可以看到Protobuf的可扩展性是最方便、自然的。其它序列化框架都需要一些配置、注解等操作。

序列化框架 可扩展性
JDK Serializer 自定义serialVersionUID,保证序列化前后VUID一致即可
FST 通过@Version控制版本,新增字段需要修改Version版本
Kryo 默认序列化器不支持字段扩展,需要修改默认序列化器或自己实现序列化器
Protocol buffer 支持字段扩展,只要保证新增id标识没有使用过即可
Thrift 支持字段扩展。新增字段为required类型时,需要设置默认值
Hessian 支持字段扩展
Avro 支持字段扩展。注意需要为字段设置默认值

4  性能

序列化大小对比

对比各个序列化框架序列化后的数据大小如下,可以看出kryo preregister(预先注册序列化类)和Avro序列化结果都很不错。所以,如果在序列化大小上有需求,可以选择Kryo或Avro。

几种Java常用序列化框架的选型与对比

序列化时间开销对比

下面是序列化与反序列化的时间开销,kryo preregister和fst preregister都能提供优异的性能,其中fst pre序列化时间就最佳,而kryo pre在序列化和反序列化时间开销上基本一致。所以,如果序列化时间是主要的考虑指标,可以选择Kryo或FST,都能提供不错的性能体验。

几种Java常用序列化框架的选型与对比

5  数据类型和语法结构支持

各序列化框架对Java数据类型支持的对比:


注:集合类型测试基本覆盖了所有对应的实现类。

  1. List测试内容:ArrayList、LinkedList、Stack、CopyOnWriteArrayList、Vector。

  2. Set测试内容:HashSet、LinkedHashSet、TreeSet、CopyOnWriteArraySet。

  3. Map测试内容:HashMap、LinkedHashMap、TreeMap、WeakHashMap、ConcurrentHashMap、Hashtable。

  4. Queue测试内容:PriorityQueue、ArrayBlockingQueue、LinkedBlockingQueue、ConcurrentLinkedQueue、SynchronousQueue、ArrayDeque、LinkedBlockingDeque和ConcurrentLinkedDeque。


下面根据测试总结了以上序列化框架所能支持的数据类型、语法。


  • 注1:static内部类需要实现序列化接口。

  • 注2:外部类需要实现序列化接口。

  • 注3:需要在Lambda表达式前添加(IXxx & Serializable)。


由于Protobuf、Thrift是IDL定义类文件,然后使用各自的编译器生成Java代码。IDL没有提供定义staic内部类、非static内部类等语法,所以这些功能无法测试。



阿里云开发者社区

世界读书日,来读书吧


4月23日是第26个世界读书日,阿里云开发者社区推出“记录阅读之路,影响同行之人”活动,6位阿里技术人为同学们分享他们看过的好书,开发者藏经阁也推出了最受大家欢迎的电子书


点击“阅读原文”,推荐曾经影响你的书,来一起读书吧~

以上是关于Java开发框架选型对比:ruoyi与yudao框架的主要内容,如果未能解决你的问题,请参考以下文章

几种Java常用序列化框架的选型与对比

几种Java常用序列化框架的选型与对比

几种Java常用序列化框架的选型与对比

Web开发技术选型之Java与PHP

Web3区块链合约开发语言框架工具深度对比选型万字长文

关于若依框架套餐的区别?