告别 if else,推荐 5 款 Java 表达式引擎
前言
我们在设计一些表单或者流程引擎时,可能我们会设计各种各样的表达式或者规则,我们通过各种表达式或者规则来实现我们的业务流转。今天就来盘点一下我们经常会使用到的表达式引擎。
这些表达式引擎都是比较常用的,很多人估计都很熟悉。我们再一起回顾一下。
spring el
官方文档
https://docs.spring.io/spring-framework/reference/core/expressions.html
。
官方示例
https://github.com/spring-projects/spring-framework/tree/master/spring-expression
。
Spring Expression Language (SpEL) 是 Spring 框架中的一个强大的表达式语言,用于在运行时查询和操作对象图。以下是关于 Spring EL 的几个关键点:
动态查询和操作: SpEL 允许你在运行时执行复杂的查询和操作数据,比如读取 bean 的属性值、调用方法、进行算术运算、逻辑判断等。
集成于 Spring 框架: SpEL 广泛应用于 Spring 的各种模块中,如 Spring Security 的访问控制表达式、Spring Data 的查询条件定义、Spring Integration 的消息路由等。
基本语法: SpEL 表达式通常被包含在 #{...}中,例如 #{property}用来获取一个 bean 的属性值。它支持字符串、布尔、算术、关系、逻辑运算符,以及方法调用、数组和列表索引访问等。
上下文感知: SpEL 能够访问 Spring 应用上下文中的 Bean,这意味着你可以直接在表达式中引用配置的 bean,实现高度灵活的配置和运行时行为调整。
类型转换: SpEL 提供了内置的类型转换服务,可以自动或显式地将一种类型的值转换为另一种类型。
安全考量: 使用 SpEL 时需要注意安全性,避免注入攻击。Spring 提供了 ExpressionParser 的配置来限制表达式的执行能力,如禁用方法调用或属性访问等。
例子:
访问 Bean 属性:
#{myBean.propertyName}
方法调用:
#{myBean.myMethod(args)}
三元运算符:
#{condition ? trueValue : falseValue}
列表和数组访问:
#{myList[0]}
算术运算:
#{2+3}
spel 工具类
ognl
官方文档
https://ognl.orphan.software/language-guide
。
官方示例
https://github.com/orphan-oss/ognl
。
OGNL (Object-Graph Navigation Language) 是一个强大的表达式语言,用于获取和设置 Java 对象的属性。它在许多 Java 框架中被用作数据绑定和操作对象图的工具,最著名的应用是在 Apache Struts2 框架中。
以下是关于 OGNL 的一些关键特性:
简单表达式: OGNL 允许你以简单的字符串形式编写表达式来访问对象属性,如 person.name 就可以获取 person 对象的 name 属性。
链式导航: 支持链式调用来深入对象图,例如 customer.address.street 会依次导航到 customer 的 address 属性,再从 address 获取 street。
集合操作: OGNL 可以直接在表达式中处理集合和数组,包括遍历、筛选、投影等操作,如 customers.{name}可以获取所有 customers 集合中每个元素的 name 属性。
上下文敏感: OGNL 表达式解析时会考虑一个上下文环境,这个环境包含了变量、对象和其他表达式可能需要的信息。
方法调用与构造器: 除了属性访问,OGNL 还支持调用对象的方法和构造新对象,如 @myUtil.trim(name)调用工具类方法,或 new java.util.Date()创建新对象。
条件与逻辑运算: 支持 if、else 逻辑,以及 &&、||等逻辑运算符,使得表达式可以处理更复杂的逻辑判断。
变量赋值: OGNL 不仅能够读取数据,还能设置对象属性的值,如 person.name = "Alice"。
安全问题: 和 SpEL 一样,使用 OGNL 时也需注意表达式注入的安全风险,确保用户输入不会被直接用于构造表达式,以防止恶意操作。
OGNL 以其简洁的语法和强大的功能,在处理对象关系和数据绑定方面非常实用,尤其是在需要动态操作对象和集合的场景下。
ognl 工具类
Aviator
官方文档
http://fnil.net/aviator/
。
官方示例
https://github.com/killme2008/aviatorscript
。
Aviator 是一个轻量级的 Java 表达式执行引擎,它设计用于高性能的动态计算场景,特别是那些需要在运行时解析和执行复杂表达式的应用场景。
以下是 Aviator 的一些核心特点和功能:
高性能: Aviator 优化了表达式的编译和执行过程,特别适合于对性能有严格要求的系统,如金融风控、实时计算等领域。
易于集成: 提供简单的 API 接口,使得在 Java 项目中嵌入 Aviator 变得非常容易,只需引入依赖,即可开始编写和执行表达式。
丰富的表达式支持: 支持数学运算、逻辑运算、比较运算、位运算、字符串操作、三元运算、变量定义与引用、函数调用等,几乎覆盖了所有常见的运算需求。
安全沙箱模式: Aviator 提供了沙箱机制,可以限制表达式的执行权限,比如禁止访问某些方法或字段,从而提高应用的安全性。动态脚本执行: 允许在运行时动态加载和执行脚本,非常适合用于规则引擎、配置驱动的系统逻辑等场景。
JIT 编译: Aviator 采用即时编译技术,将表达式编译成 Java 字节码执行,进一步提升执行效率。数据绑定: 可以方便地将 Java 对象、Map、List 等数据结构绑定到表达式上下文中,实现表达式与 Java 数据的无缝对接。
扩展性: 支持自定义函数,用户可以根据需要扩展 Aviator 的功能,增加特定业务逻辑的处理能力。
Aviator 因其高性能和灵活性,在需要动态脚本处理的场景中,特别是在那些对性能敏感且需要频繁执行复杂计算逻辑的应用中,是一个非常有吸引力的选择。
Aviator 工具类
Mvel2
官方文档
https://juejin.cn/post/mvel.documentnode.com/
。
官方示例
https://github.com/mvel/mvel
。
MVEL2(MVFLEX Expression Language 2)是一种强大且灵活的 Java 库,用于解析和执行表达式语言。它是 MVEL 项目的第二代版本,旨在提供高效、简洁的方式来操作对象和执行逻辑。
下面是关于 MVEL2 的一些关键特性和使用指南:
动态类型与静态类型混合: MVEL 支持动态类型,同时也允许静态类型检查,这意味着你可以选择是否在编译时检查类型错误,增加了灵活性和安全性。
简洁的语法: MVEL 语法基于 Java 但更加简洁,便于编写和阅读,适用于快速构建表达式和小型脚本。
属性访问与方法调用: 类似于其他表达式语言,MVEL 允许直接访问对象属性和调用其方法,如 person.name 或 list.size()。
控制流语句: 支持 if、else、switch、循环(for、while)等控制流结构,使得在表达式中实现复杂逻辑成为可能。
模板引擎: MVEL2 提供了一个强大的模板引擎,可以用来生成文本输出,类似于 Velocity 或 Freemarker,但与 MVEL 表达式无缝集成。
变量赋值与函数定义: 直接在表达式中定义变量和函数,支持局部变量和闭包(匿名函数)。数据绑定与转换: 自动或手动进行类型转换,简化了不同数据类型间的操作。
集成与扩展: MVEL 设计为易于集成到现有 Java 项目中,同时提供了扩展点,允许用户定义自定义函数和操作符。
性能优化: MVEL 关注执行效率,通过优化的编译器和执行引擎来减少运行时开销。
Hutool 表达式引擎门面
官方文档
https://doc.hutool.cn/pages/ExpressionUtil/#%E4%BB%8B%E7%BB%8D
。
hutool 工具包在 5.5.0 版本之后,提供了表达式计算引擎封装为门面模式,提供统一的 API,去除差异。目前支持如下表达式引擎
Aviator
Apache Jexl3
MVEL
JfireEL
Rhino
Spring Expression Language (SpEL)
如上所述的表达式引擎不能满足要求,hutool 还支持通过 SPI 进行自定义扩展
基于 hutool 封装的工具类
总结
本文介绍了市面比较常用的表达式引擎组件,而这些引擎基本上都可以用 hutool 提供的表达式门面实现,hutool 确实在工具类这方面做得很好,基本上我们日常会用到的工具,它大部分都涵盖到。最后文末 demo 链接,也提供了跟 spring 整合的表达引擎聚合实现,大家感兴趣也可以看看。
评论