动态脚本引擎之QLExpress讲解
1 QLExpress
1.1 简介
1.1.1 介绍
QLExpress(Quick Language Express) 是阿里巴巴开源的一门动态脚本引擎解析工具,是一个轻量级、高性能的 Java 规则引擎 和 动态脚本执行框架,起源于阿里巴巴的电商业务,旨在解决业务规则、表达式、数学计算等动态脚本的解析问题。它类似于 Groovy 或 JavaScript 引擎,但更专注于规则解析和执行效率
其具有以下基本特点:
线程安全:QLExpress被设计为线程安全的动态脚本引擎,它使用threadlocal类型的临时变量,确保在引擎运算过程中的并发场景下的线程安全性。高效执行: 为了提高执行效率,QLExpress在编译过程中可以将比较耗时的脚本编译结果缓存到本地机器。此外,运行时的临时变量创建采用了缓冲池技术,以确保高效的运行时性能,使其与一些性能优秀的脚本引擎(如Groovy)相当。弱类型脚本语言:QLExpress采用弱类型脚本语言,语法类似于Groovy和JavaScript。这使得业务规则的表达更加灵活,虽然相对于强类型脚本语言可能略慢,但在业务的灵活性方面提供了很大的优势。安全控制:QLExpress提供了一些运行时参数的设置,以进行安全控制。通过这些参数,可以预防一些潜在的安全问题,如死循环或对高危系统API的调用。代码精简、依赖最小:QLExpress的设计追求代码的精简和最小依赖,其jar包大小为250k,适用于所有Java的运行环境。这使得它在各种环境中都能轻松部署和运行,包括在Android系统的低端POS机上广泛应用。
1.1.2 QLExpress与常用规则引擎对比
| 特性 / 规则引擎 | Drools | Aviator | EasyRule | QLExpress |
|---|---|---|---|---|
| 语言 | Drools规则语言 (DRL) | Aviator表达式语言 | Java | 弱类型脚本语言 |
| 性能 | 适用于复杂规则,可能较慢 | 高性能表达式求值引擎 | 相对较高性能,适用于简单规则 | 高效执行,适用于业务规则和表达式计算 |
| 灵活性 | 非常灵活,支持动态修改规则 | 灵活,支持丰富的运算符和函数 | 简单易用,适合非专业开发人员 | 灵活,支持业务规则、表达式和数学计算 |
| 语法 | 专门的规则语言 | 表达式语言 | Java编写规则 | 弱类型脚本语言,类似于Groovy和JavaScript |
| 应用场景 | 复杂的业务规则 | 简单的表达式计算和规则 | 简单规则场景,面向非专业开发人员 | 业务规则、表达式、数学计算,适用于电商业务 |
| 开发者社区 | 大型开发者社区 | 相对较小的社区规模 | 相对较小的社区规模 | 相对较小的社区规模,阿里巴巴内部有影响力 |
| 文档 | 详尽的文档 | 文档相对较少 | 文档相对较少 | 文档相对较少,可能需要深入源代码理解 |
| 开源 | 是 | 是 | 是 | 是 |
Drools 适用于复杂的业务规则,而 Aviator 和QLExpress适用于相对简单的表达式计算和规则。EasyRule更适合简单规则场景,特别是面向非专业开发人员的情况。最终选择取决于具体需求,包括业务规则的复杂性、性能要求、开发人员技能水平以及项目的特定场景。
1.2 简单操作和原理介绍
1.2.1 简单操作
在 Maven 项目中引入 QLExpress,需要在项目的 pom.xml 文件中添加相关的依赖:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>QLExpress</artifactId>
<version>3.3.0</version> <!-- 推荐最新稳定版 -->
</dependency>
以下展示简单演示如何使用 QLExpress 计算折扣后的金额。在实际项目中,可能需要更复杂的脚本和上下文,以适应业务需求。
import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;
publicclass QLExpressExample {
public static void main(String[] args) {
try {
// 创建 QLExpress 引擎
ExpressRunner runner = new ExpressRunner();
// 创建上下文并设置变量
DefaultContext<String, Object> context = new DefaultContext<>();
context.put("amount", 1000);
context.put("discount", 0.1);
// 执行脚本
String expression = "amount * (1 - discount)";
Object result = runner.execute(expression, context, null, true, false);
// 输出结果
System.out.println("Result: " + result);
} catch (Exception e) {
e.printStackTrace();
}
}
}
参数说明
| 参数 | 类型 | 是否必填 | 默认值 | 说明 |
|---|---|---|---|---|
| express | String | 是 | - | 要执行的动态脚本(如 "a + b * 2" 或 "if (x > 0) { return 'OK'; }") |
| context | DefaultContext<String, Object> | 是 | - | 变量上下文,用于向脚本传递参数(如 context.put("a", 10)) 如果无变量可传 null,但通常建议显式创建 new DefaultContext<>() |
| errorList | List |
否 | null | 用于收集执行过程中的错误信息(如语法错误) 传 null 表示不收集,错误直接抛异常。 |
| isCache | boolean | 否 | true | 是否缓存脚本的编译结果(提升重复执行性能)。建议开启(true),除非脚本每次变化。 |
| isTrace | boolean | 否 | false | 是否输出详细的执行日志(调试时使用)。生产环境建议关闭(false),避免日志膨胀。 |
1.2.2 原理介绍
QLExpress 的一般工作原理,包括语法树分析、上下文和执行过程。

语法树分析:QLExpress会先将输入的脚本进行词法分析和语法分析,生成一棵语法树。这个语法树表示了脚本的结构,将其组织成可以被执行的形式。上下文: 在QLExpress中,上下文是一个关键概念。它是脚本执行时的环境,包含变量、函数等信息。在执行脚本之前,可以向上下文中添加变量,定义函数,设置一些执行参数等。这样,脚本执行时可以引用上下文中的内容。执行过程:QLExpress的执行过程包括编译和运行两个主要阶段。在编译阶段,脚本被解析并生成可以执行的指令序列。在运行阶段,这些指令被执行,从而实现脚本的功能。
在这个框架下,二次定制的功能扩展可以包括:
自定义操作符和函数: 可以通过实现自定义的操作符和函数,使得脚本能够执行特定的业务逻辑。修改执行流程: 可以在执行阶段插入自定义的逻辑,改变脚本执行的流程。定制编译过程: 在编译阶段定制特定的优化或变换,以满足特殊需求。扩展上下文功能: 可以添加一些上下文的拦截器,使得在脚本执行前后可以执行额外的逻辑。
1.3 基本语法学习
1.3.1 操作符
QLExpress 支持一系列操作符,包括算术运算符、比较运算符、逻辑运算符等。
| 类别 | 操作符 | 示例 | 描述 |
|---|---|---|---|
| 算术运算符 | + | a + b | 加法,将两个数字相加 |
| - | a - b | 减法,从第一个数字中减去第二个数字 | |
| * | a * b | 乘法,将两个数字相乘 | |
| / | a / b | 除法,将第一个数字除以第二个数字 | |
| % | a % b | 取余,返回第一个数字除以第二个数字的余数 | |
| 比较运算符 | == | a == b | 等于,判断两个值是否相等 |
| != | a != b | 不等于,判断两个值是否不相等 | |
| > | a > b | 大于,判断第一个值是否大于第二个值 | |
| < | a < b | 小于,判断第一个值是否小于第二个值 | |
| >= | a >= b | 大于等于,判断第一个值是否大于或等于第二个值 | |
| <= | a <= b | 小于等于,判断第一个值是否小于或等于第二个值 | |
| 逻辑运算符 | && | condition1 && condition2 | 逻辑与,两个条件都为真时结果为真 |
| || | condition1 || condition2 | 逻辑或,两个条件中有一个为真时结果为真 | |
| ! | !condition | 逻辑非,将真变为假,假变为真 | |
| 三元运算符 | ? : | condition ? valueIfTrue : valueIfFalse | 用于根据条件选择两个值中的一个 |
定义一个 Calculator 类,其中包含一些数字和一个用户对象,然后使用 QLExpress 进行一些简单的运算和条件判断。
import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;
public class Calculator {
public static void main(String[] args) {
try {
ExpressRunner runner = new ExpressRunner();
DefaultContext<String, Object> context = new DefaultContext<>();
// 设置变量
context.put("a", 10);
context.put("b", 5);
// 算术运算示例
executeAndPrint(runner, context, "a + b", "Addition");
// 比较运算示例
executeAndPrint(runner, context, "a > b", "Greater than");
// 逻辑运算示例
executeAndPrint(runner, context, "a > 0 && b > 0", "Logical AND");
// 三元运算示例
executeAndPrint(runner, context, "a > b ? 'a is greater' : 'b is greater'", "Ternary Operator");
} catch (Exception e) {
e.printStackTrace();
}
}
private static void executeAndPrint(ExpressRunner runner, DefaultContext<String, Object> context, String expression, String operation) throws Exception {
// 执行脚本
Object result = runner.execute(expression, context, null, true, false);
// 输出结果
System.out.println(operation + ": " + result);
}
}
1.3.2 Java对象操作
基本的 Java 语法和对象操作在 QLExpress 中同样适用,演示在 QLExpress 中使用 Java 语法和进行对象操作:
import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;
public class QLExpressJavaSyntaxExample {
public static void main(String[] args) {
try {
ExpressRunner runner = new ExpressRunner();
DefaultContext<String, Object> context = new DefaultContext<>();
// 创建一个用户对象
User user = new User("John", 25);
context.put("user", user);
// 使用 Java 语法访问对象属性和调用方法
executeAndPrint(runner, context, "user.getName()", "Accessing Object Property");
executeAndPrint(runner, context, "user.getAge() + 5", "Performing Arithmetic with Object Property");
// 使用 Java 语法进行对象操作
executeAndPrint(runner, context, "user.age = 30", "Modifying Object Property");
} catch (Exception e) {
e.printStackTrace();
}
}
private static void executeAndPrint(ExpressRunner runner, DefaultContext<String, Object> context, String expression, String operation) throws Exception {
// 执行脚本
Object result = runner.execute(expression, context, null, true, false);
// 输出结果
System.out.println(operation + ": " + result);
}
@Data
@AllArgsConstructor
@NoArgsConstructor
// 用户对象类
static class User {
private String name;
private int age;
}
}
1.3.3 脚本中定义function
在QLExpress中,可以通过 function 关键字来定义函数。以下是一个简单的示例:
import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;
public class QLExpressFunctionExample {
public static void main(String[] args) {
try {
final String express = "function add(int a, int b){\n" +
" return a + b;\n" +
"};\n" +
"\n" +
"function sub(int a, int b){\n" +
" return a - b;\n" +
"};\n" +
"\n" +
"a = 10;\n" +
"result = add(a, 4) + sub(a, 9);\n" +
"return result;";
ExpressRunner runner = new ExpressRunner();
DefaultContext<String, Object> context = new DefaultContext<>();
// 执行脚本
Object result = runner.execute(express, context, null, true, false);
// 输出脚本执行结果
System.out.println("Result: " + result);
// 输出函数调用过程中的参数和返回值
System.out.println("add function call: a + 4 = " + context.get("a") + " + 4 = " + context.get("result"));
} catch (Exception e) {
e.printStackTrace();
}
}
}
1.3.4 扩展操作符
在 QLExpress 中,可以通过自定义操作符(Operator)来扩展语言的功能。
import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;
import java.util.ArrayList;
import java.util.List;
public class QLExpressOperatorExample {
public static void main(String[] args) {
try {
// 示例 1:替换 ifthenelse 关键字
ExpressRunner runner1 = new ExpressRunner();
DefaultContext<String, Object> context1 = new DefaultContext<>();
context1.put("语文",120);
context1.put("数学",23);
context1.put("英语",23);
runner1.addOperatorWithAlias("如果", "if", null);
runner1.addOperatorWithAlias("则", "then", null);
runner1.addOperatorWithAlias("否则", "else", null);
String express1 = "如果 (语文 + 数学 + 英语 > 270) 则 {return 1;} 否则 {return 0;}";
Object result1 = runner1.execute(express1, context1, null, false, false, 100L);
System.out.println("Result 1: " + result1); // 输出结果 1
// 示例 2:自定义 Operator
ExpressRunner runner2 = new ExpressRunner();
DefaultContext<String, Object> context2 = new DefaultContext<>();
// 自定义 Operator
runner2.addOperator("join", new JoinOperator());
// 示例 2.1:addOperator
Object result2_1 = runner2.execute("1 join 2 join 3", context2, null, false, false);
System.out.println("Result 2.1: " + result2_1); // 输出结果 [1, 2, 3]
// 示例 2.2:replaceOperator
ExpressRunner runner2_2 = new ExpressRunner();
runner2_2.replaceOperator("+", new JoinOperator());
Object result2_2 = runner2_2.execute("1 + 2 + 3", context2, null, false, false);
System.out.println("Result 2.2: " + result2_2); // 输出结果 [1, 2, 3]
// 示例 2.3:addFunction
ExpressRunner runner2_3 = new ExpressRunner();
runner2_3.addFunction("join", new JoinOperator());
Object result2_3 = runner2_3.execute("join(1, 2, 3)", context2, null, false, false);
System.out.println("Result 2.3: " + result2_3); // 输出结果 [1, 2, 3]
} catch (Exception e) {
e.printStackTrace();
}
}
// JoinOperator 类的定义
publicstaticclass JoinOperator extends com.ql.util.express.Operator {
public Object executeInner(Object[] list) throws Exception {
Object opdata1 = list[0];
Object opdata2 = list[1];
if (opdata1 instanceof List) {
((List) opdata1).add(opdata2);
return opdata1;
} else {
List result = new ArrayList();
for (Object opdata : list) {
result.add(opdata);
}
return result;
}
}
}
}
1.3.5 绑定Java类或对象的methon
在 QLExpress 中,可使用 addFunctionOfClassMethod 和 addFunctionOfServiceMethod 方法来绑定 Java 类或对象的方法。
import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;
public class QLExpressFunctionBindingExample {
public static void main(String[] args) {
try {
ExpressRunner runner = new ExpressRunner();
DefaultContext<String, Object> context = new DefaultContext<>();
// 绑定 Math 类的 abs 方法
runner.addFunctionOfClassMethod("取绝对值", Math.class.getName(), "abs", new String[]{"double"}, null);
// 绑定 BeanExample 类的 upper 方法
runner.addFunctionOfClassMethod("转换为大写", BeanExample.class.getName(), "upper", new String[]{"String"}, null);
// 绑定 System.out 的 println 方法
runner.addFunctionOfServiceMethod("打印", System.out, "println", new String[]{"String"}, null);
// 绑定 BeanExample 对象的 anyContains 方法
runner.addFunctionOfServiceMethod("contains", new BeanExample(), "anyContains", new Class[]{String.class, String.class}, null);
String express = "取绝对值(-100); 转换为大写(\"hello world\"); 打印(\"你好吗?\"); contains(\"helloworld\", \"aeiou\")";
Object result = runner.execute(express, context, null, false, false);
System.out.println("Result: " + result);
} catch (Exception e) {
e.printStackTrace();
}
}
public static class BeanExample {
public static double abs(double value) {
System.out.println("取绝对值结果: " + value);
return Math.abs(value);
}
public static String upper(String abc) {
System.out.println("转换为大写结果: " + abc.toUpperCase());
return abc.toUpperCase();
}
public boolean anyContains(String str, String searchStr) {
char[] s = str.toCharArray();
for (char c : s) {
if (searchStr.contains(c + "")) {
returntrue;
}
}
returnfalse;
}
}
}
1.3.6 宏定义(macro)
在QLExpress中,宏定义(macro)允许将一个表达式片段命名为宏,并在其他地方引用这个宏。当需要在多个地方使用相同的复杂表达式时非常有用,可以提高代码的可读性和维护性。
import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;
public class QLExpressMacroExample {
public static void main(String[] args) {
try {
ExpressRunner runner = new ExpressRunner();
DefaultContext<String, Object> context = new DefaultContext<>();
// 定义宏
runner.addMacro("计算平均成绩", "(语文+数学+英语)/3.0");
runner.addMacro("是否优秀", "计算平均成绩>90");
// 设置变量值
context.put("语文", 88);
context.put("数学", 99);
context.put("英语", 95);
// 执行表达式并打印结果
Object result = runner.execute("是否优秀", context, null, false, false);
System.out.println("Result: " + result);
} catch (Exception e) {
e.printStackTrace();
}
}
}
1.3.7 编译脚本查询外部需要定义的变量和函数
在 QLExpress 中,编译脚本并查询外部需要定义的变量和函数可以通过 ExpressRunner 提供的一些方法来实现。
import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;
public class QLExpressCompileExample {
public static void main(String[] args) {
try {
ExpressRunner runner = new ExpressRunner(true, true);
DefaultContext<String, Object> context = new DefaultContext<>();
context.put("语文",120);
context.put("数学",23);
context.put("英语",23);
context.put("综合考试",235);
// 定义脚本
String express = "double 平均分 = (语文 + 数学 + 英语 + 综合考试) / 4.0; return 平均分";
// 查询外部需要定义的变量
String[] variableNames = runner.getOutVarNames(express);
System.out.println("外部需要定义的变量:");
for (String variableName : variableNames) {
System.out.println("var : " + variableName);
}
// 查询外部需要定义的函数
String[] functionNames = runner.getOutFunctionNames(express);
System.out.println("\n外部需要定义的函数:");
for (String functionName : functionNames) {
System.out.println("function : " + functionName);
}
// 编译脚本并执行
Object result = runner.execute(express, context, null, false, false);
System.out.println("\n脚本执行结果: " + result);
} catch (Exception e) {
e.printStackTrace();
}
}
}
1.3.8 不定参数的使用
在QLExpress中,可以通过使用不定参数(动态参数)来处理方法的参数。
import com.ql.util.express.DefaultContext;
import com.ql.util.express.DynamicParamsUtil;
import com.ql.util.express.ExpressRunner;
public class QLExpressDynamicParamsExample {
public static void main(String[] args) {
try {
// 创建 ExpressRunner 实例
ExpressRunner runner = new ExpressRunner();
// 创建 DefaultContext 实例
DefaultContext<String, Object> expressContext = new DefaultContext<>();
// 在 runner 中添加一个函数,使用不定参数
runner.addFunctionOfServiceMethod("getTemplate", new QLExpressDynamicParamsExample(), "getTemplate",
new Class[]{Object[].class}, null);
// 调用 getTemplate 方法,传递数组作为参数
Object resultWithArray = runner.execute("getTemplate([11, '22', 33L, true])",
expressContext, null, false, false);
System.out.println("Result with Array: " + resultWithArray);
// 打开全局开关,启用动态参数调用
DynamicParamsUtil.supportDynamicParams = true;
// 调用 getTemplate 方法,传递多个参数
Object resultWithDynamicParams = runner.execute("getTemplate(11, '22', 33L, true)", expressContext,
null, false, false);
System.out.println("Result with Dynamic Params: " + resultWithDynamicParams);
} catch (Exception e) {
e.printStackTrace();
}
}
// 等价于 getTemplate(Object[] params)
public Object getTemplate(Object... params) {
StringBuilder result = new StringBuilder();
for (Object obj : params) {
result.append(obj).append(",");
}
return result.toString();
}
}
1.3.9 集合的快捷用法
在QLExpress中,你可以使用一些快捷的语法来操作集合。
import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class QLExpressCollectionOperationsExample {
public static void main(String[] args) {
try {
ExpressRunner runner = new ExpressRunner();
DefaultContext<String, Object> context = new DefaultContext<>();
// 使用NewMap创建Map
String expressMap = "abc = NewMap(1:1, 2:2); return abc.get(1) + abc.get(2);";
Object resultMap = runner.execute(expressMap, context, null, false, false);
System.out.println("NewMap Result: " + resultMap);
// 使用NewList创建List
String expressList = "abc = NewList(1, 2, 3); return abc.get(1) + abc.get(2);";
Object resultList = runner.execute(expressList, context, null, false, false);
System.out.println("NewList Result: " + resultList);
// 使用方括号[]创建List
String expressSquareBrackets = "abc = [1, 2, 3]; return abc[1] + abc[2];";
Object resultSquareBrackets = runner.execute(expressSquareBrackets, context, null, false, false);
System.out.println("Square Brackets Result: " + resultSquareBrackets);
} catch (Exception e) {
e.printStackTrace();
}
}
}
1.3.10 集合的遍历
类似java的语法,只是ql不支持for(obj:list){}的语法,只能通过下标访问。
import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;
import java.util.HashMap;
import java.util.Map;
public class QLExpressCollectionTraversalExample {
public static void main(String[] args) {
try {
ExpressRunner runner = new ExpressRunner();
DefaultContext<String, Object> context = new DefaultContext<>();
// 创建一个Map
Map<String, String> map = new HashMap<>();
map.put("a", "a_value");
map.put("b", "b_value");
// 将Map放入上下文中
context.put("map", map);
// 遍历Map
String express = "keySet = map.keySet();\n" +
"objArr = keySet.toArray();\n" +
"for (i = 0; i < objArr.length; i++) {\n" +
" key = objArr[i];\n" +
" System.out.println(map.get(key));\n" +
"}";
runner.execute(express, context, null, false, false);
} catch (Exception e) {
e.printStackTrace();
}
}
}
参考链接:https://zyfcodes.blog.csdn.net/article/details/134363363
1.4 与SpringBoot对接
在 Spring Boot 中,通常将 ExpressRunner 声明为 Bean,方便全局调用。
1.4.1 配置 QLExpress Bean
1.4.1.1 直接初始化(简单场景)
import com.ql.util.express.ExpressRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class QLExpressConfig {
@Bean
public ExpressRunner expressRunner() {
ExpressRunner runner = new ExpressRunner();
// 可选:设置缓存模式(默认开启)
runner.setShortCircuit(true);
return runner;
}
}
1.4.1.2 自定义配置(高级场景)
如果需要 预加载函数 或 安全控制,可以扩展配置:
@Bean
public ExpressRunner expressRunner() {
ExpressRunner runner = new ExpressRunner(true, true); // 开启缓存和短计算
// 注册自定义函数
runner.addFunction("double", params -> (Integer) params[0] * 2);
// 限制可访问的类(安全)
runner.addOperatorWithAlias("危险操作", "dangerOp", null);
return runner;
}
1.4.2 在 Service 中使用 QLExpress
1.4.2.1 执行简单表达式
import com.ql.util.express.ExpressRunner;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class RuleService {
@Autowired
private ExpressRunner expressRunner;
public Object executeRule(String expression) throws Exception {
// 绑定变量
DefaultContext<String, Object> context = new DefaultContext<>();
context.put("a", 10);
context.put("b", 20);
// 执行脚本
return expressRunner.execute(expression, context, null, false, false);
}
}
测试调用
@RestController
@RequestMapping("/rule")
public class RuleController {
@Autowired
private RuleService ruleService;
@GetMapping("/calculate")
public String calculate(@RequestParam String expr) throws Exception {
Object result = ruleService.executeRule(expr);
return "Result: " + result;
}
}
1.4.2.2 动态规则引擎(如风控规则)
假设需要根据用户积分判断等级:
public String evaluateUserLevel(int score) throws Exception {
String rule =
"if (score >= 90) { " +
" return 'VIP'; " +
"} else if (score >= 60) { " +
" return 'Normal'; " +
"} else { " +
" return 'Restricted'; " +
"}";
DefaultContext<String, Object> context = new DefaultContext<>();
context.put("score", score);
return (String) expressRunner.execute(rule, context, null, false, false);
}
1.4.3 高级用法
1.4.3.1 注册 Spring Bean 为 QLExpress 函数
如果想让 QLExpress 调用 Spring 管理的 Bean:
@Service
public class MathService {
public int add(int a, int b) {
return a + b;
}
}
@Bean
public ExpressRunner expressRunner(MathService mathService) {
ExpressRunner runner = new ExpressRunner();
// 将 Spring Bean 注册为 QLExpress 函数
runner.addFunction("add", mathService::add);
return runner;
}
1.4.3.2 安全控制(限制可访问的类)
防止恶意代码调用危险方法:
runner.addPreciseMethod(
"System", // 类名
"exit", // 方法名
null, // 方法对象(null表示禁止调用)
null
);
1.4.3.3 结合数据库动态加载规则
@Service
public class DynamicRuleService {
@Autowired
private ExpressRunner expressRunner;
@Autowired
private RuleRepository ruleRepository; // 假设存储规则脚本
public Object executeRule(Long ruleId, Map<String, Object> params) throws Exception {
String ruleScript = ruleRepository.findById(ruleId).getScript();
DefaultContext<String, Object> context = new DefaultContext<>();
context.putAll(params);
return expressRunner.execute(ruleScript, context, null, false, false);
}
}

浙公网安备 33010602011771号