⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 码猿技术专栏 张彦峰ZYF 「张彦峰ZYF」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注微信公众号:【芋道源码】有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言都将得到认真回复。甚至不知道如何读源码也可以请教噢。
  4. 新的源码解析文章实时收到通知。每周更新一篇左右。
  5. 认真的源码交流微信群。

在现代业务系统中,如何实现快速、灵活的规则配置和动态决策,成为了企业提升响应速度和智能化水平的关键。阿里巴巴开源的 QLExpress 引擎,以其轻量、高效、简洁的优势,为复杂业务逻辑的动态处理提供了一种创新的解决方案。

无论是需要实时调整规则的电商促销,还是依赖规则动态性的金融风控,QLExpress 都能以其灵活的表达式和易用的规则配置,实现高效而精准的业务决策。

如果你正在寻找一款强大、灵活的动态脚本引擎,那么 QLExpress 可能正是你需要的工具!

一、QLExpress快速了解

QLExpress(Quick Language Express)是阿里巴巴开源的一门动态脚本引擎解析工具,起源于阿里巴巴的电商业务,旨在解决业务规则、表达式、数学计算等动态脚本的解析问题。其具有以下基本特点:

  • 线程安全: QLExpress被设计为线程安全的动态脚本引擎,它使用threadlocal类型的临时变量,确保在引擎运算过程中的并发场景下的线程安全性。
  • 高效执行: 为了提高执行效率,QLExpress在编译过程中可以将比较耗时的脚本编译结果缓存到本地机器。此外,运行时的临时变量创建采用了缓冲池技术,以确保高效的运行时性能,使其与一些性能优秀的脚本引擎(如Groovy)相当。
  • 弱类型脚本语言: QLExpress采用弱类型脚本语言,语法类似于Groovy和JavaScript。这使得业务规则的表达更加灵活,虽然相对于强类型脚本语言可能略慢,但在业务的灵活性方面提供了很大的优势。
  • 安全控制: QLExpress提供了一些运行时参数的设置,以进行安全控制。通过这些参数,可以预防一些潜在的安全问题,如死循环或对高危系统API的调用。
  • 代码精简、依赖最小: QLExpress的设计追求代码的精简和最小依赖,其jar包大小为250k,适用于所有Java的运行环境。这使得它在各种环境中都能轻松部署和运行,包括在Android系统的低端POS机上广泛应用。

总体而言,这些特性使QLExpress成为一个在阿里电商业务场景中得到广泛应用的强大工具,具有高效、灵活和安全的特点。

二、QLExpress与常用规则引擎对比

Drools适用于复杂的业务规则,而Aviator和QLExpress适用于相对简单的表达式计算和规则。EasyRule更适合简单规则场景,特别是面向非专业开发人员的情况。最终选择取决于具体需求,包括业务规则的复杂性、性能要求、开发人员技能水平以及项目的特定场景。

三、快速引用和一般工作原理简诉

1.引用与基本演示

在 Maven 项目中引入 QLExpress,需要在项目的 pom.xml 文件中添加相关的依赖:

<dependencies>
<dependency>
<groupId>com.ql</groupId>
<artifactId>qlExpress</artifactId>
<version>3.2.2</version> <!-- 使用实际版本号 -->
</dependency>
</dependencies>

以下展示简单演示如何使用 QLExpress 计算折扣后的金额。在实际项目中,可能需要更复杂的脚本和上下文,以适应业务需求。

package org.zyf.javabasic.qlexpress;

import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;

/**
* @program: zyfboot-javabasic
* @description: 演示如何使用 QLExpress 计算折扣后的金额
* @author: zhangyanfeng
**/
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();
}
}
}

2.一般工作原理说明

QLExpress的一般工作原理,包括语法树分析、上下文和执行过程。

  • 语法树分析: QLExpress会先将输入的脚本进行词法分析和语法分析,生成一棵语法树。这个语法树表示了脚本的结构,将其组织成可以被执行的形式。
  • 上下文: 在QLExpress中,上下文是一个关键概念。它是脚本执行时的环境,包含变量、函数等信息。在执行脚本之前,你可以向上下文中添加变量,定义函数,设置一些执行参数等。这样,脚本执行时可以引用上下文中的内容。
  • 执行过程: QLExpress的执行过程包括编译和运行两个主要阶段。在编译阶段,脚本被解析并生成可以执行的指令序列。在运行阶段,这些指令被执行,从而实现脚本的功能。

在这个框架下,二次定制的功能扩展可以包括:

  • 自定义操作符和函数: 可以通过实现自定义的操作符和函数,使得脚本能够执行特定的业务逻辑。
  • 修改执行流程: 可以在执行阶段插入自定义的逻辑,改变脚本执行的流程。
  • 定制编译过程: 在编译阶段定制特定的优化或变换,以满足特殊需求。
  • 扩展上下文功能: 可以添加一些上下文的拦截器,使得在脚本执行前后可以执行额外的逻辑。

这样的扩展点允许更好地适应特定的业务场景和需求。

四、基本语法学习

1.操作符

QLExpress 支持一系列操作符,包括算术运算符、比较运算符、逻辑运算符等。

定义一个 Calculator 类,其中包含一些数字和一个用户对象,然后使用 QLExpress 进行一些简单的运算和条件判断。

package org.zyf.javabasic.qlexpress;

import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;

/**
* @program: zyfboot-javabasic
* @description: 一些简单的运算和条件判断
* @author: zhangyanfeng
**/
publicclass 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);
}
}

2.Java对象操作

基本的 Java 语法和对象操作在 QLExpress 中同样适用,演示在 QLExpress 中使用 Java 语法和进行对象操作:

package org.zyf.javabasic.qlexpress;

import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;

/**
* @program: zyfboot-javabasic
* @description: 基本的 Java 语法和对象操作
* @author: zhangyanfeng
**/
publicclass 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);
}

// 用户对象类
staticclass User {
private String name;
privateint age;

public User(String name, int age) {
this.name = name;
this.age = age;
}

public String getName() {
return name;
}

public int getAge() {
return age;
}
}
}

3.脚本中定义function

在QLExpress中,可以通过 function 关键字来定义函数。以下是一个简单的示例:

package org.zyf.javabasic.qlexpress;

import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;

/**
* @program: zyfboot-javabasic
* @description: 通过 function 关键字来定义函数
* @author: zhangyanfeng
**/
publicclass 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();
}
}
}

4.扩展操作符

在 QLExpress 中,可以通过自定义操作符(Operator)来扩展语言的功能。

package org.zyf.javabasic.qlexpress;

import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;

import java.util.ArrayList;
import java.util.List;

/**
* @program: zyfboot-javabasic
* @description: 自定义操作符(Operator)来扩展语言的功能
* @author: zhangyanfeng
**/
publicclass QLExpressOperatorExample {
public static void main(String[] args) {
try {
// 示例 1:替换 if then else 关键字
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;
}
}
}
}

5.绑定Java类或对象的methon

在 QLExpress 中,可使用 addFunctionOfClassMethod 和 addFunctionOfServiceMethod 方法来绑定 Java 类或对象的方法。

package org.zyf.javabasic.qlexpress;

import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;

/**
* @program: zyfboot-javabasic
* @description: 可以使用 addFunctionOfClassMethod 和 addFunctionOfServiceMethod 方法来绑定 Java 类或对象的方法
* @author: zhangyanfeng
* @create: 2023-11-19 16:13
**/
publicclass 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();
}
}

publicstaticclass 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;
}
}
}

6.宏定义(macro)

在QLExpress中,宏定义(macro)允许将一个表达式片段命名为宏,并在其他地方引用这个宏。当需要在多个地方使用相同的复杂表达式时非常有用,可以提高代码的可读性和维护性。

package org.zyf.javabasic.qlexpress;

import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;

/**
* @program: zyfboot-javabasic
* @description: macro 宏定义
* @author: zhangyanfeng
**/
publicclass 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();
}
}
}

7.编译脚本查询外部需要定义的变量和函数

在QLExpress中,编译脚本并查询外部需要定义的变量和函数可以通过 ExpressRunner 提供的一些方法来实现。

package org.zyf.javabasic.qlexpress;

import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;

/**
* @program: zyfboot-javabasic
* @description: 编译脚本,查询外部需要定义的变量和函数。
* @author: zhangyanfeng
**/
publicclass 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();
}
}
}

看下对应打印结果:

执行的表达式:double 平均分 = (语文 + 数学 + 英语 + 综合考试) / 4.0; return 平均分
单词分解结果:{double},{平均分},{=},{(},{语文},{+},{数学},{+},{英语},{+},{综合考试},{)},{/},{4.0},{;},{return},{平均分}
预处理后结果:{double},{平均分},{=},{(},{语文},{+},{数学},{+},{英语},{+},{综合考试},{)},{/},{4.0},{;},{return},{平均分}
单词分析结果:double:CONST_CLASS,平均分:ID,=:=,(:(,语文:ID,+:+,数学:ID,+:+,英语:ID,+:+,综合考试:ID,):),/:/,4.0:CONST_DOUBLE,;:;,return:return,平均分:ID
最后的语法树:
1: STAT_BLOCK:STAT_BLOCK STAT_BLOCK
2: STAT_SEMICOLON:STAT_SEMICOLON STAT_SEMICOLON
3: =:= =
4: def:def def
5: double:CONST_CLASS CONST_CLASS
5: 平均分:CONST_STRING CONST
4: /:/ /
5: (:CHILD_EXPRESS CHILD_EXPRESS
6: +:+ +
7: +:+ +
8: +:+ +
9: 语文:ID ID
9: 数学:ID ID
8: 英语:ID ID
7: 综合考试:ID ID
5: 4.0:CONST_DOUBLE CONST
2: STAT_SEMICOLON:STAT_SEMICOLON STAT_SEMICOLON
3: return:returnreturn
4: 平均分:ID ID


1:LoadData Class:double
2:LoadData 平均分
3:OP : def OPNUMBER[2]
4:LoadAttr:语文
5:LoadAttr:数学
6:OP : + OPNUMBER[2]
7:LoadAttr:英语
8:OP : + OPNUMBER[2]
9:LoadAttr:综合考试
10:OP : + OPNUMBER[2]
11:LoadData 4.0
12:OP : / OPNUMBER[2]
13:OP : = OPNUMBER[2]
14:clearDataStack
15:LoadAttr:平均分
16:return [value]

外部需要定义的变量:
var : 数学
var : 综合考试
var : 英语
var : 语文
执行的表达式:double 平均分 = (语文 + 数学 + 英语 + 综合考试) / 4.0; return 平均分
单词分解结果:{double},{平均分},{=},{(},{语文},{+},{数学},{+},{英语},{+},{综合考试},{)},{/},{4.0},{;},{return},{平均分}
预处理后结果:{double},{平均分},{=},{(},{语文},{+},{数学},{+},{英语},{+},{综合考试},{)},{/},{4.0},{;},{return},{平均分}
单词分析结果:double:CONST_CLASS,平均分:ID,=:=,(:(,语文:ID,+:+,数学:ID,+:+,英语:ID,+:+,综合考试:ID,):),/:/,4.0:CONST_DOUBLE,;:;,return:return,平均分:ID
最后的语法树:
1: STAT_BLOCK:STAT_BLOCK STAT_BLOCK
2: STAT_SEMICOLON:STAT_SEMICOLON STAT_SEMICOLON
3: =:= =
4: def:def def
5: double:CONST_CLASS CONST_CLASS
5: 平均分:CONST_STRING CONST
4: /:/ /
5: (:CHILD_EXPRESS CHILD_EXPRESS
6: +:+ +
7: +:+ +
8: +:+ +
9: 语文:ID ID
9: 数学:ID ID
8: 英语:ID ID
7: 综合考试:ID ID
5: 4.0:CONST_DOUBLE CONST
2: STAT_SEMICOLON:STAT_SEMICOLON STAT_SEMICOLON
3: return:returnreturn
4: 平均分:ID ID


1:LoadData Class:double
2:LoadData 平均分
3:OP : def OPNUMBER[2]
4:LoadAttr:语文
5:LoadAttr:数学
6:OP : + OPNUMBER[2]
7:LoadAttr:英语
8:OP : + OPNUMBER[2]
9:LoadAttr:综合考试
10:OP : + OPNUMBER[2]
11:LoadData 4.0
12:OP : / OPNUMBER[2]
13:OP : = OPNUMBER[2]
14:clearDataStack
15:LoadAttr:平均分
16:return [value]


外部需要定义的函数:
执行的表达式:double 平均分 = (语文 + 数学 + 英语 + 综合考试) / 4.0; return 平均分
单词分解结果:{double},{平均分},{=},{(},{语文},{+},{数学},{+},{英语},{+},{综合考试},{)},{/},{4.0},{;},{return},{平均分}
预处理后结果:{double},{平均分},{=},{(},{语文},{+},{数学},{+},{英语},{+},{综合考试},{)},{/},{4.0},{;},{return},{平均分}
单词分析结果:double:CONST_CLASS,平均分:ID,=:=,(:(,语文:ID,+:+,数学:ID,+:+,英语:ID,+:+,综合考试:ID,):),/:/,4.0:CONST_DOUBLE,;:;,return:return,平均分:ID
最后的语法树:
1: STAT_BLOCK:STAT_BLOCK STAT_BLOCK
2: STAT_SEMICOLON:STAT_SEMICOLON STAT_SEMICOLON
3: =:= =
4: def:def def
5: double:CONST_CLASS CONST_CLASS
5: 平均分:CONST_STRING CONST
4: /:/ /
5: (:CHILD_EXPRESS CHILD_EXPRESS
6: +:+ +
7: +:+ +
8: +:+ +
9: 语文:ID ID
9: 数学:ID ID
8: 英语:ID ID
7: 综合考试:ID ID
5: 4.0:CONST_DOUBLE CONST
2: STAT_SEMICOLON:STAT_SEMICOLON STAT_SEMICOLON
3: return:returnreturn
4: 平均分:ID ID


1:LoadData Class:double
2:LoadData 平均分
3:OP : def OPNUMBER[2]
4:LoadAttr:语文
5:LoadAttr:数学
6:OP : + OPNUMBER[2]
7:LoadAttr:英语
8:OP : + OPNUMBER[2]
9:LoadAttr:综合考试
10:OP : + OPNUMBER[2]
11:LoadData 4.0
12:OP : / OPNUMBER[2]
13:OP : = OPNUMBER[2]
14:clearDataStack
15:LoadAttr:平均分
16:return [value]


脚本执行结果: 100.25

8.不定参数的使用

在QLExpress中,可以通过使用不定参数(动态参数)来处理方法的参数。

package org.zyf.javabasic.qlexpress;

import com.ql.util.express.DefaultContext;
import com.ql.util.express.DynamicParamsUtil;
import com.ql.util.express.ExpressRunner;

/**
* @program: zyfboot-javabasic
* @description: 不定参数的使用
* @author: zhangyanfeng
**/
publicclass 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();
}
}

9.集合的快捷用法

在QLExpress中,你可以使用一些快捷的语法来操作集合。

package org.zyf.javabasic.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;

/**
* @program: zyfboot-javabasic
* @description: 集合操作
* @author: zhangyanfeng
**/
publicclass 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();
}
}
}

10.集合的遍历

类似java的语法,只是ql不支持for(obj:list){}的语法,只能通过下标访问。

package org.zyf.javabasic.qlexpress;

import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;

import java.util.HashMap;
import java.util.Map;

/**
* @program: zyfboot-javabasic
* @description: 使用foreach关键字结合索引遍历集合
* @author: zhangyanfeng
**/
publicclass 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();
}
}

}

四、总结

本文全面介绍了 QLExpress 作为阿里巴巴开源的一款轻量级动态脚本引擎的主要功能和应用场景,并深入讲解了其在规则引擎和业务场景中的使用优势。通过对比其他规则引擎的性能与灵活性,QLExpress 展现了其在处理动态规则、复杂计算逻辑、实时决策等方面的独特优势,尤其适用于需要动态配置业务规则的场景。在实际应用中,QLExpress 的简洁语法、快速上手和高性能表现,使其成为了企业动态业务处理的有效工具。

未来,QLExpress 还可以进一步优化和拓展,以支持更多场景和定制化功能。对于希望在系统中灵活应用规则逻辑、提升业务决策效率的开发者来说,QLExpress 是一个值得关注和尝试的工具。

希望通过本文的介绍,大家能更好地理解和应用 QLExpress,从而在项目中实现更高效、更灵活的规则管理。

文章目录
  1. 1. 一、QLExpress快速了解
  2. 2. 二、QLExpress与常用规则引擎对比
  3. 3. 三、快速引用和一般工作原理简诉
    1. 3.1. 1.引用与基本演示
    2. 3.2. 2.一般工作原理说明
  4. 4. 四、基本语法学习
    1. 4.1. 1.操作符
    2. 4.2. 2.Java对象操作
    3. 4.3. 3.脚本中定义function
    4. 4.4. 4.扩展操作符
    5. 4.5. 5.绑定Java类或对象的methon
    6. 4.6. 6.宏定义(macro)
    7. 4.7. 7.编译脚本查询外部需要定义的变量和函数
    8. 4.8. 8.不定参数的使用
    9. 4.9. 9.集合的快捷用法
    10. 4.10. 10.集合的遍历
  5. 5. 四、总结