QLExpress 入门教程
引言
QLExpress 是一款由阿里巴巴开发的针对Java平台的动态语言,旨在提升开发者在各类业务场景下的效率与灵活性。它源自阿里巴巴内部复杂的电商需求,包括业务规则管理、布尔表达式计算、高精度数学运算等,并于2012年对外开源。QLExpress以其轻量、高效、安全的特性,在阿里集团内外产生了广泛影响。 地址:github.com/alibaba/QLE…
依赖与安装
Maven依赖
在你的项目pom.xml
文件中加入以下依赖:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>QLExpress</artifactId>
<version>3.3.3</version>
</dependency>
快速开始
创建 ExpressRunner
实例并执行简单脚本:
import com.alibaba.qlExpress.ExpressRunner;
import com.alibaba.qlExpress.DefaultContext;
public class QLExpressDemo {
public static void main(String[] args) {
ExpressRunner runner = new ExpressRunner();
DefaultContext<String, Object> context = new DefaultContext<>();
context.put("a", 1);
context.put("b", 2);
context.put("c", 3);
String express = "a + b * c";
try {
Object r = runner.execute(express, context, null, true, false);
System.out.println(r); // 输出结果:7
} catch (Exception e) {
e.printStackTrace();
}
}
}
语法与特性
基础语法
QLExpress 支持大部分Java的运算符,如 +
, -
, *
, /
, 比较运算符,逻辑运算符,以及流程控制语句 for
, if-then-else
等。
- 变量定义:var x = 10;
- 循环:for (var i = 0; i < 10; i++) { ... }
- 条件语句:if (x > 10) { ... } else { ... }
- 函数定义:function add(a, b) { return a + b; }
弱类型特性
QLExpress 是弱类型语言,类似于 Groovy 和 JavaScript,这使得编写业务逻辑更为灵活,但也意味着牺牲了部分编译时的类型安全检查。
安全控制
QLExpress提供了多级别安全控制,以防止潜在的安全风险,如防止死循环和不安全的系统API调用。你可以通过设置maxStack和maxCPUTime参数来限制递归深度和执行时间。
runner.setMaxStack(100);
runner.setMaxCPUTime(1000);
黑名单机制
默认禁用了一些危险的API调用,例如 System.exit
,并提供了接口供用户扩展黑名单。
白名单机制
分为编译期白名单和运行时白名单,可以精确控制脚本中允许使用的类和方法。
语法树与静态分析
QLExpress 提供API来静态分析脚本,例如获取脚本中使用的变量和函数名称,这有助于构建上下文或进行系统业务处理。
优化与缓存
引擎使用本地缓存加速重复脚本的执行,同时提供了API来管理缓存,例如清除缓存或从缓存中获取指令集。
高级特性
- 防止死循环:通过设置脚本执行超时时间来防止无限循环。
- 函数与操作符扩展:允许自定义函数、操作符和宏,扩展QLExpress的能力。
- 与Spring集成:通过自定义
IExpressContext
实现与Spring框架的无缝对接,便于在脚本中访问Spring Bean。
示例
自定义函数
runner.addFunction("max", new OperatorBase() {
@Override
public Object executeInner(Object[] list) throws Exception {
double max = Double.NEGATIVE_INFINITY;
for (Object obj : list) {
max = Math.max(max, ((Number)obj).doubleValue());
}
return max;
}
});
执行带自定义函数的脚本
context.put("numbers", new int[]{1, 2, 3, 4, 5});
String script = "max(numbers)";
Object result = runner.execute(script, context, null, true, false);
System.out.println(result); // 输出最大值:5
扩展操作符
QLExpress允许用户自定义操作符,从而扩展语言的功能。例如,你可以定义一个名为join
的操作符,用于合并多个元素到一个列表中。以下是如何实现这个自定义操作符的步骤:
import java.util.ArrayList;
import java.util.List;
public class JoinOperator extends Operator {
@Override
public Object executeInner(Object[] list) throws Exception {
List<Object> result = new ArrayList<>();
for (Object obj : list) {
if (obj instanceof List) {
result.addAll((List<?>) obj);
} else {
result.add(obj);
}
}
return result;
}
}
在QLExpress运行环境中注册此操作符:
ExpressRunner runner = new ExpressRunner();
runner.addOperator("join", new JoinOperator());
// 使用自定义的join操作符
Object r = runner.execute("1 join 2 join 3", context, null, false, false);
System.out.println(r); // 输出: [1, 2, 3]
绑定Java类或对象的Method
QLExpress支持将Java类的方法直接绑定到脚本中使用,这包括静态方法和实例方法。这样做的好处是可以直接在脚本里调用Java代码,无需手动实现操作符。
绑定静态方法
runner.addFunctionOfClassMethod("toUpperCase", String.class.getName(), "toUpperCase", new String[]{"String"}, null);
在脚本中调用:
String express = "toUpperCase(\"hello\")";
Object result = runner.execute(express, context, null, false, false);
System.out.println(result); // 输出: HELLO
绑定实例方法
假设有一个BeanExample
类,我们想在脚本中使用它的anyContains
方法。
public class BeanExample {
public boolean anyContains(String str, String searchStr) {
// 方法实现略
}
}
BeanExample bean = new BeanExample();
runner.addFunctionOfServiceMethod("containsChar", bean, "anyContains", new Class[]{String.class, String.class}, null);
String express = "containsChar('hello', 'e')";
Object r = runner.execute(express, context, null, false, false);
System.out.println(r); // 输出结果根据方法实现
Macro 宏定义
宏定义是QLExpress中一种简化重复表达式的机制,它允许你为一段常用表达式定义一个名字,之后在脚本中可以直接使用这个名字。这类似于编程语言中的函数,但更加简单,不涉及参数和返回值的概念。
runner.addMacro("calculateTotalPrice", "price * quantity + tax");
IExpressContext<String, Object> context = new DefaultContext<>();
context.put("price", 100);
context.put("quantity", 5);
context.put("tax", 5);
Object result = runner.execute("calculateTotalPrice", context, null, false, false);
System.out.println(result); // 输出: 505
在上述示例中,calculateTotalPrice
宏定义了一个计算总价格的公式,当在脚本中引用该宏时,QLExpress会自动替换为对应的表达式并计算结果。
结语
QLExpress凭借其灵活性和高性能,成为了处理复杂业务逻辑的理想选择。掌握QLExpress的使用,将极大地提升你在Java项目中的业务规则处理能力和效率。随着深入学习和实践,你会发现更多高级特性和技巧,助力你解决更复杂的问题。
转载自:https://juejin.cn/post/7385935890932482067