目录
[1. 上下文变量的声明方式](#1. 上下文变量的声明方式)
[2. 表达式对上下文的"读"](#2. 表达式对上下文的“读”)
[1. 规则可以修改上下文](#1. 规则可以修改上下文)
[2. 不存在的变量 ≠ 报错](#2. 不存在的变量 ≠ 报错)
[(四)复杂上下文:Context 承载真实业务世界](#(四)复杂上下文:Context 承载真实业务世界)
[1. 对象作为上下文变量:领域模型直连规则](#1. 对象作为上下文变量:领域模型直连规则)
[2. Map 作为上下文:配置即规则输入](#2. Map 作为上下文:配置即规则输入)
[3. 数组 / 集合作为上下文:规则驱动统计与分析](#3. 数组 / 集合作为上下文:规则驱动统计与分析)
[1. 全局变量:Context 中的长期状态](#1. 全局变量:Context 中的长期状态)
[2. 局部变量:首次赋值即进入上下文](#2. 局部变量:首次赋值即进入上下文)
[3. 变量覆盖:规则对世界的"重定义"](#3. 变量覆盖:规则对世界的“重定义”)
[1. 多规则共享一个 Context](#1. 多规则共享一个 Context)
[2. 上下文即状态机](#2. 上下文即状态机)
[(七)自定义上下文:Context 是可扩展的](#(七)自定义上下文:Context 是可扩展的)
[1. 重写 get():劫持变量访问](#1. 重写 get():劫持变量访问)
[2. 上下文继承:世界的"快照复制"](#2. 上下文继承:世界的“快照复制”)
干货分享,感谢您的阅读!
在规则引擎体系中,一个经常被低估却决定系统上限的设计点是:规则究竟运行在什么样的"世界"之中。
如果说 QLExpress 的表达式语法回答的是"规则能写什么",那么上下文变量(Context)解决的则是一个更根本的问题------规则在运行时能看见哪些状态、又能对这些状态产生怎样的影响。
在很多初学者的理解中,Context 只是一个用于传参的 Map:
在执行前塞值,在执行中取值,在执行后被遗忘。
但这种理解,恰恰忽略了 QLExpress 最具工程价值的部分。
事实上,在 QLExpress 的设计中,上下文并不是一个"只读参数袋",而是一个可读、可写、可共享、可扩展的运行时内存模型。规则不仅可以基于上下文进行判断,还可以直接修改上下文状态,并将结果沉淀下来,供后续规则继续使用。这使得 QLExpress 天然具备了承载规则链、状态机、业务流程驱动等复杂场景的能力。
本文将围绕一个完整的 ContextVariablesDemo 示例,从代码与执行结果出发,逐层拆解 QLExpress Context 的核心语义,帮助你建立一个清晰的认知:Context 不是规则的"参数",而是规则运行时的"世界本身"。

一、整体内容练习演示
我这里给出的 ContextVariablesDemo,实际上尽量完整演示了上下文从"参数容器"到"运行时世界模型"的演进过程。
展示详细代码如下:
java
package org.zyf.javabasic.qlexpress.basic.context;
import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;
import java.util.HashMap;
import java.util.Map;
/**
* @program: zyfboot-javabasic
* @description: QLExpress上下文变量演示 - 全面展示上下文管理和变量作用域
* @author: zhangyanfeng
* @create: 2025-12-25 23:54
**/
public class ContextVariablesDemo {
private ExpressRunner runner;
public ContextVariablesDemo() {
this.runner = new ExpressRunner();
System.out.println("✅ QLExpress上下文变量演示引擎初始化完成");
}
/**
* 演示基本上下文操作
*/
public void demonstrateBasicContextOperations() {
System.out.println("\n=== QLExpress基本上下文操作演示 ===\n");
try {
// 1. 创建和使用基本上下文
System.out.println("🏗️ 1. 基本上下文创建和使用演示:");
DefaultContext<String, Object> context = new DefaultContext<>();
context.put("name", "张彦峰");
context.put("age", 30);
context.put("salary", 15000);
context.put("isManager", true);
Object result1 = runner.execute("name + '今年' + age + '岁'", context, null, true, false);
Object result2 = runner.execute("isManager ? '管理员' : '普通员工'", context, null, true, false);
Object result3 = runner.execute("salary > 10000 ? '高薪' : '一般'", context, null, true, false);
System.out.printf(" 基本信息: %s%n", result1);
System.out.printf(" 职位类型: %s%n", result2);
System.out.printf(" 薪资等级: %s%n%n", result3);
// 2. 上下文变量修改
System.out.println("🔄 2. 上下文变量修改演示:");
System.out.printf(" 修改前 - age: %s, salary: %s%n", context.get("age"), context.get("salary"));
runner.execute("age = age + 1", context, null, true, false);
runner.execute("salary = salary * 1.1", context, null, true, false);
System.out.printf(" 修改后 - age: %s, salary: %s%n%n", context.get("age"), context.get("salary"));
// 3. 上下文变量检查
System.out.println("🔍 3. 上下文变量检查演示:");
Object hasName = runner.execute("name != null", context, null, true, false);
Object hasEmail = runner.execute("email != null", context, null, true, false); // 不存在的变量
System.out.printf(" name变量存在: %s%n", hasName);
System.out.printf(" email变量存在: %s%n%n", hasEmail);
} catch (Exception e) {
System.err.println("❌ 基本上下文操作演示失败: " + e.getMessage());
e.printStackTrace();
}
}
/**
* 演示复杂上下文数据
*/
public void demonstrateComplexContextData() {
System.out.println("\n=== QLExpress复杂上下文数据演示 ===\n");
try {
DefaultContext<String, Object> context = new DefaultContext<>();
// 1. 对象类型上下文变量
System.out.println("🏗️ 1. 对象类型上下文变量演示:");
Employee emp = new Employee("张彦峰", 30, "技术部", 15000.0);
context.put("employee", emp);
Object result1 = runner.execute("employee.name + ' - ' + employee.department", context, null, true, false);
Object result2 = runner.execute("employee.salary > 12000", context, null, true, false);
Object result3 = runner.execute("employee.age >= 25 && employee.salary > 10000", context, null, true, false);
System.out.printf(" 员工信息: %s%n", result1);
System.out.printf(" 薪资检查: %s%n", result2);
System.out.printf(" 综合条件: %s%n%n", result3);
// 2. Map类型上下文变量
System.out.println("📋 2. Map类型上下文变量演示:");
Map<String, Object> config = new HashMap<>();
config.put("maxUsers", 1000);
config.put("timeout", 30);
config.put("enableCache", true);
config.put("version", "1.2.3");
context.put("config", config);
Object result4 = runner.execute("config.get('maxUsers') > 500", context, null, true, false);
Object result5 = runner.execute("config.get('enableCache') ? '缓存开启' : '缓存关闭'", context, null, true, false);
Object result6 = runner.execute("'系统版本: ' + config.get('version')", context, null, true, false);
System.out.printf(" 用户数量检查: %s%n", result4);
System.out.printf(" 缓存状态: %s%n", result5);
System.out.printf(" 版本信息: %s%n%n", result6);
// 3. 数组类型上下文变量
System.out.println("📊 3. 数组类型上下文变量演示:");
String[] departments = {"技术部", "市场部", "财务部", "人事部"};
int[] budgets = {500000, 300000, 200000, 150000};
context.put("departments", departments);
context.put("budgets", budgets);
String script =
"totalBudget = 0;" +
"highBudgetDepts = '';" +
"count = 0;" +
"for (i = 0; i < budgets.length; i++) {" +
" totalBudget = totalBudget + budgets[i];" +
" if (budgets[i] > 250000) {" +
" if (count > 0) highBudgetDepts = highBudgetDepts + ', ';" +
" highBudgetDepts = highBudgetDepts + departments[i];" +
" count = count + 1;" +
" }" +
"}" +
"return '总预算: ¥' + totalBudget + ', 高预算部门: ' + highBudgetDepts;";
Object result7 = runner.execute(script, context, null, true, false);
System.out.printf(" 预算分析: %s%n%n", result7);
} catch (Exception e) {
System.err.println("❌ 复杂上下文数据演示失败: " + e.getMessage());
e.printStackTrace();
}
}
/**
* 演示上下文作用域
*/
public void demonstrateContextScope() {
System.out.println("\n=== QLExpress上下文作用域演示 ===\n");
try {
// 1. 全局上下文
System.out.println("🌍 1. 全局上下文演示:");
DefaultContext<String, Object> globalContext = new DefaultContext<>();
globalContext.put("globalVar", "我是全局变量");
globalContext.put("counter", 0);
Object result1 = runner.execute("globalVar + ', 初始计数: ' + counter", globalContext, null, true, false);
System.out.printf(" %s%n%n", result1);
// 2. 局部变量创建
System.out.println("📍 2. 局部变量创建演示:");
String localScript =
"localVar = '我是局部变量';" +
"counter = counter + 1;" +
"return globalVar + ', ' + localVar + ', 计数: ' + counter;";
Object result2 = runner.execute(localScript, globalContext, null, true, false);
System.out.printf(" 执行结果: %s%n", result2);
System.out.printf(" 执行后全局counter: %s%n%n", globalContext.get("counter"));
// 3. 变量覆盖
System.out.println("🔄 3. 变量覆盖演示:");
globalContext.put("message", "原始消息");
String overrideScript =
"originalMessage = message;" +
"message = '被覆盖的消息';" +
"return '原始: ' + originalMessage + ', 现在: ' + message;";
Object result3 = runner.execute(overrideScript, globalContext, null, true, false);
System.out.printf(" 覆盖演示: %s%n", result3);
System.out.printf(" 执行后全局message: %s%n%n", globalContext.get("message"));
// 4. 条件作用域
System.out.println("❓ 4. 条件作用域演示:");
globalContext.put("userLevel", 5);
String conditionalScript =
"if (userLevel >= 5) {" +
" privilegeMessage = '您有高级权限';" +
" canAccess = true;" +
"} else {" +
" privilegeMessage = '权限不足';" +
" canAccess = false;" +
"}" +
"return privilegeMessage + ', 可访问: ' + canAccess;";
Object result4 = runner.execute(conditionalScript, globalContext, null, true, false);
System.out.printf(" 权限检查: %s%n%n", result4);
} catch (Exception e) {
System.err.println("❌ 上下文作用域演示失败: " + e.getMessage());
e.printStackTrace();
}
}
/**
* 演示上下文传递和共享
*/
public void demonstrateContextSharing() {
System.out.println("\n=== QLExpress上下文传递和共享演示 ===\n");
try {
// 1. 多个表达式共享上下文
System.out.println("🔗 1. 多个表达式共享上下文演示:");
DefaultContext<String, Object> sharedContext = new DefaultContext<>();
sharedContext.put("balance", 1000);
// 第一个表达式:消费
runner.execute("balance = balance - 300", sharedContext, null, true, false);
System.out.printf(" 消费后余额: %s%n", sharedContext.get("balance"));
// 第二个表达式:充值
runner.execute("balance = balance + 500", sharedContext, null, true, false);
System.out.printf(" 充值后余额: %s%n", sharedContext.get("balance"));
// 第三个表达式:检查状态
String statusScript =
"if (balance > 1000) {" +
" return '富裕';" +
"} else if (balance > 500) {" +
" return '一般';" +
"} else {" +
" return '紧张';" +
"}";
Object status = runner.execute(statusScript, sharedContext, null, true, false);
System.out.printf(" 财务状态: %s%n%n", status);
// 2. 上下文数据累积
System.out.println("📈 2. 上下文数据累积演示:");
DefaultContext<String, Object> accContext = new DefaultContext<>();
accContext.put("totalSales", 0);
accContext.put("transactionCount", 0);
// 模拟多次交易
int[] salesAmounts = {150, 200, 300, 80, 250};
for (int i = 0; i < salesAmounts.length; i++) {
accContext.put("currentSale", salesAmounts[i]);
String script =
"totalSales = totalSales + currentSale;" +
"transactionCount = transactionCount + 1;" +
"return '交易' + transactionCount + ': ¥' + currentSale + ', 累计: ¥' + totalSales;";
Object result = runner.execute(script, accContext, null, true, false);
System.out.printf(" %s%n", result);
}
// 计算平均值
Object avgSale = runner.execute("Math.round(totalSales / transactionCount * 100) / 100.0", accContext, null, true, false);
System.out.printf(" 平均交易额: ¥%s%n%n", avgSale);
// 3. 上下文状态管理
System.out.println("📊 3. 上下文状态管理演示:");
DefaultContext<String, Object> stateContext = new DefaultContext<>();
// 初始化状态
String initScript =
"currentUser = 'admin';" +
"loginTime = System.currentTimeMillis();" +
"sessionActive = true;" +
"operationCount = 0;" +
"return '用户 ' + currentUser + ' 已登录';";
Object initResult = runner.execute(initScript, stateContext, null, true, false);
System.out.printf(" 初始化: %s%n", initResult);
// 执行操作
for (int i = 1; i <= 3; i++) {
String opScript =
"if (sessionActive) {" +
" operationCount = operationCount + 1;" +
" result = '执行操作' + operationCount;" +
"} else {" +
" result = '会话已失效';" +
"}" +
"return result;";
Object opResult = runner.execute(opScript, stateContext, null, true, false);
System.out.printf(" %s%n", opResult);
}
// 注销
String logoutScript =
"sessionActive = false;" +
"logoutTime = System.currentTimeMillis();" +
"duration = (logoutTime - loginTime) / 1000;" +
"return currentUser + ' 注销,会话时长: ' + duration + '秒,共执行' + operationCount + '次操作';";
Object logoutResult = runner.execute(logoutScript, stateContext, null, true, false);
System.out.printf(" 注销: %s%n%n", logoutResult);
} catch (Exception e) {
System.err.println("❌ 上下文传递和共享演示失败: " + e.getMessage());
e.printStackTrace();
}
}
/**
* 演示自定义上下文
*/
public void demonstrateCustomContext() {
System.out.println("\n=== QLExpress自定义上下文演示 ===\n");
try {
// 1. 自定义上下文实现
System.out.println("🛠️ 1. 自定义上下文实现演示:");
CustomContext customContext = new CustomContext();
customContext.put("user", "张彦峰");
customContext.put("role", "admin");
Object result1 = runner.execute("user + ' 的角色是 ' + role", customContext, null, true, false);
System.out.printf(" 自定义上下文结果: %s%n", result1);
// 展示自定义上下文的特殊功能
System.out.printf(" 访问统计: %s%n", customContext.getAccessCount());
System.out.printf(" 访问历史: %s%n%n", customContext.getAccessHistory());
// 2. 上下文继承
System.out.println("🔄 2. 上下文继承演示:");
DefaultContext<String, Object> parentContext = new DefaultContext<>();
parentContext.put("company", "ZYF科技");
parentContext.put("version", "1.0");
DefaultContext<String, Object> childContext = new DefaultContext<>();
// 将父上下文的数据复制到子上下文
for (String key : parentContext.keySet()) {
childContext.put(key, parentContext.get(key));
}
// 子上下文添加自己的数据
childContext.put("module", "用户管理");
childContext.put("user", "张彦峰");
Object result2 = runner.execute("company + ' - ' + module + ' v' + version + ' (用户: ' + user + ')'", childContext, null, true, false);
System.out.printf(" 继承上下文结果: %s%n%n", result2);
} catch (Exception e) {
System.err.println("❌ 自定义上下文演示失败: " + e.getMessage());
e.printStackTrace();
}
}
/**
* 运行所有演示
*/
public void runAllDemonstrations() {
System.out.println("🚀 开始QLExpress上下文变量完整演示...\n");
demonstrateBasicContextOperations();
demonstrateComplexContextData();
demonstrateContextScope();
demonstrateContextSharing();
demonstrateCustomContext();
System.out.println("✅ QLExpress上下文变量演示完成!");
System.out.println("\n📚 学习要点:");
System.out.println(" 1. 上下文创建: DefaultContext是最常用的上下文实现");
System.out.println(" 2. 变量存取: context.put()设置, 表达式中直接使用变量名");
System.out.println(" 3. 复杂数据: 支持对象、Map、数组等复杂数据类型");
System.out.println(" 4. 作用域: 表达式中可创建局部变量,会影响上下文状态");
System.out.println(" 5. 数据共享: 多个表达式可共享同一个上下文实例");
System.out.println(" 6. 自定义扩展: 可继承实现IExpressContext接口定制上下文行为");
}
/**
* 员工类 - 用于演示对象类型上下文变量
*/
public static class Employee {
private String name;
private int age;
private String department;
private double salary;
public Employee(String name, int age, String department, double salary) {
this.name = name;
this.age = age;
this.department = department;
this.salary = salary;
}
public String getName() { return name; }
public int getAge() { return age; }
public String getDepartment() { return department; }
public double getSalary() { return salary; }
@Override
public String toString() {
return String.format("Employee{name='%s', age=%d, department='%s', salary=%.2f}",
name, age, department, salary);
}
}
/**
* 自定义上下文 - 演示上下文扩展
*/
public static class CustomContext extends DefaultContext<String, Object> {
private int accessCount = 0;
private StringBuilder accessHistory = new StringBuilder();
@Override
public Object get(Object name) {
accessCount++;
accessHistory.append(name).append(", ");
return super.get(name);
}
public int getAccessCount() {
return accessCount;
}
public String getAccessHistory() {
return accessHistory.toString();
}
}
/**
* 主方法 - 运行演示
*/
public static void main(String[] args) {
ContextVariablesDemo demo = new ContextVariablesDemo();
demo.runAllDemonstrations();
}
}
基本验证打印结果如下:
Go
✅ QLExpress上下文变量演示引擎初始化完成
🚀 开始QLExpress上下文变量完整演示...
=== QLExpress基本上下文操作演示 ===
🏗️ 1. 基本上下文创建和使用演示:
基本信息: 张彦峰今年30岁
职位类型: 管理员
薪资等级: 高薪
🔄 2. 上下文变量修改演示:
修改前 - age: 30, salary: 15000
修改后 - age: 31, salary: 16500.0
🔍 3. 上下文变量检查演示:
name变量存在: true
email变量存在: false
=== QLExpress复杂上下文数据演示 ===
🏗️ 1. 对象类型上下文变量演示:
员工信息: 张彦峰 - 技术部
薪资检查: true
综合条件: true
📋 2. Map类型上下文变量演示:
用户数量检查: true
缓存状态: 缓存开启
版本信息: 系统版本: 1.2.3
📊 3. 数组类型上下文变量演示:
预算分析: 总预算: ¥1150000, 高预算部门: 技术部, 市场部
=== QLExpress上下文作用域演示 ===
🌍 1. 全局上下文演示:
我是全局变量, 初始计数: 0
📍 2. 局部变量创建演示:
执行结果: 我是全局变量, 我是局部变量, 计数: 1
执行后全局counter: 1
🔄 3. 变量覆盖演示:
覆盖演示: 原始: 原始消息, 现在: 被覆盖的消息
执行后全局message: 被覆盖的消息
❓ 4. 条件作用域演示:
权限检查: 您有高级权限, 可访问: true
=== QLExpress上下文传递和共享演示 ===
🔗 1. 多个表达式共享上下文演示:
消费后余额: 700
充值后余额: 1200
财务状态: 富裕
📈 2. 上下文数据累积演示:
交易1: ¥150, 累计: ¥150
交易2: ¥200, 累计: ¥350
交易3: ¥300, 累计: ¥650
交易4: ¥80, 累计: ¥730
交易5: ¥250, 累计: ¥980
平均交易额: ¥196.0
📊 3. 上下文状态管理演示:
初始化: 用户 admin 已登录
执行操作1
执行操作2
执行操作3
注销: admin 注销,会话时长: 0秒,共执行3次操作
=== QLExpress自定义上下文演示 ===
🛠️ 1. 自定义上下文实现演示:
自定义上下文结果: 张彦峰 的角色是 admin
访问统计: 2
访问历史: user, role,
🔄 2. 上下文继承演示:
继承上下文结果: ZYF科技 - 用户管理 v1.0 (用户: 张彦峰)
✅ QLExpress上下文变量演示完成!
📚 学习要点:
1. 上下文创建: DefaultContext是最常用的上下文实现
2. 变量存取: context.put()设置, 表达式中直接使用变量名
3. 复杂数据: 支持对象、Map、数组等复杂数据类型
4. 作用域: 表达式中可创建局部变量,会影响上下文状态
5. 数据共享: 多个表达式可共享同一个上下文实例
6. 自定义扩展: 可继承实现IExpressContext接口定制上下文行为
Process finished with exit code 0
二、各部分详细讲解和指导说明
(一)上下文的本质:规则的运行时内存模型
DefaultContext 的真实角色,在代码中我在反复使用:
java
DefaultContext<String, Object> context = new DefaultContext<>();
从设计上看,DefaultContext 并不仅仅是:
-
参数输入容器
-
变量 Map
而是承担了以下职责:
| 职责 | 说明 |
|---|---|
| 变量解析 | 表达式中直接使用变量名 |
| 状态存储 | 表达式可修改上下文 |
| 跨表达式共享 | 多次 execute 共享状态 |
| 副作用承载 | 规则执行的"结果沉淀" |
结论一句话:
QLExpress 的上下文 = 规则引擎的「运行时内存 + 状态机」
(二)基础上下文:变量即世界的"基础属性"
1. 上下文变量的声明方式
在 demonstrateBasicContextOperations() 中:
java
context.put("name", "张彦峰");
context.put("age", 30);
context.put("salary", 15000);
context.put("isManager", true);
这里体现了 QLExpress 的一个关键设计:
上下文变量没有声明阶段,只有"存在与否"
-
不需要类型声明
-
不需要预注册
-
表达式中直接按变量名使用
java
"name + '今年' + age + '岁'"
这使得规则具备极强的配置化与动态性。
2. 表达式对上下文的"读"
java
isManager ? '管理员' : '普通员工'
salary > 10000 ? '高薪' : '一般'
这里的语义非常重要:
-
表达式中的变量读取
-
直接映射到 Context.get(key)
-
没有中间缓存、没有拷贝
我在后面 CustomContext 中重写 get(),正是验证了这一点。
(三)上下文可变性:规则不是"纯函数"
1. 规则可以修改上下文
这是 QLExpress 与很多"表达式计算器"最大的不同点之一。
java
runner.execute("age = age + 1", context, null, true, false);
runner.execute("salary = salary * 1.1", context, null, true, false);
重要语义结论:
QLExpress 的表达式是"命令式规则",而不是纯表达式
-
规则可以产生副作用
-
规则可以累积状态
-
上下文是可写的
这也是 QLExpress 能胜任 规则引擎 / 决策引擎 场景的根本原因。
2. 不存在的变量 ≠ 报错
java
Object hasEmail = runner.execute("email != null", context, ...);
这里体现了上下文的一个"弱约束"特性:
-
未放入 Context 的变量
-
默认值为
null -
不会直接抛异常
工程含义:
上下文变量天然支持"可选字段"与"灰度规则"
(四)复杂上下文:Context 承载真实业务世界
1. 对象作为上下文变量:领域模型直连规则
java
context.put("employee", emp);
在表达式中:
java
employee.name
employee.salary > 12000
employee.age >= 25 && employee.salary > 10000
这里 QLExpress 做了三件事:
-
自动识别 Java Bean
-
支持 Getter / 属性访问
-
规则直接操作领域对象
这是规则引擎"业务友好性"的核心能力之一。
2. Map 作为上下文:配置即规则输入
java
context.put("config", config);
表达式中:
java
config.get('maxUsers')
config.get('enableCache')
这类写法非常适合:
-
动态配置判断
-
灰度开关
-
系统参数规则化
Context 在这里等同于"配置中心快照"。
3. 数组 / 集合作为上下文:规则驱动统计与分析
java
context.put("departments", departments);
context.put("budgets", budgets);
随后在表达式中完成:
-
累计
-
条件过滤
-
文本拼接
java
for (i = 0; i < budgets.length; i++) { ... }
关键点:
上下文并不限制变量复杂度,
规则本身才是"复杂度控制者"。
(五)上下文作用域:变量的"生存周期"
1. 全局变量:Context 中的长期状态
java
globalContext.put("counter", 0);
在多次 execute 中:
java
counter = counter + 1;
结论非常重要:
上下文变量的生命周期 ≥ 表达式执行周期
只要 Context 实例不销毁:
-
状态持续存在
-
规则可反复叠加影响
2. 局部变量:首次赋值即进入上下文
java
localVar = '我是局部变量';
在 QLExpress 中:
-
没有真正的"栈变量"
-
所有赋值都会写入 Context
这是很多初学者最容易误解的一点。
QLExpress 的"局部变量"本质是"延迟注入的上下文变量"
3. 变量覆盖:规则对世界的"重定义"
java
message = '被覆盖的消息';
如果 Context 中已有同名变量:
-
新值直接覆盖旧值
-
无作用域隔离
工程建议:
上下文变量命名必须具备"规则级规范"
(六)上下文共享:规则链的核心机制
1. 多规则共享一个 Context
java
sharedContext.put("balance", 1000);
随后:
-
消费规则
-
充值规则
-
状态判断规则
依次执行,上下文即账户本身。
这正是示例中:
java
balance = balance - 300
balance = balance + 500
的本质含义。
2. 上下文即状态机
在"状态管理"示例中:
Go
sessionActive
operationCount
loginTime
logoutTime
Context 已经演变为:
一个完整的会话状态对象
规则不再只是计算,而是在"驱动状态迁移"。
(七)自定义上下文:Context 是可扩展的
1. 重写 get():劫持变量访问
java
@Override
public Object get(Object name) {
accessCount++;
accessHistory.append(name);
return super.get(name);
}
这一段代码非常高级,它证明了:
-
变量访问是可拦截的
-
Context 是规则引擎的"扩展点"
可以实现:
-
变量访问审计
-
规则调试追踪
-
安全控制
-
指标采集
2. 上下文继承:世界的"快照复制"
java
for (String key : parentContext.keySet()) {
childContext.put(key, parentContext.get(key));
}
这在工程上非常实用:
-
子规则隔离执行
-
父规则结果作为输入
-
避免状态污染
三、总结
通过本章的完整示例与逐段解析,可以看到 QLExpress 上下文变量机制所体现的,并非语法层面的便利性,而是一种明确的运行时模型设计选择。
从整体上看,可以将 QLExpress 的 Context 理解为规则执行期间的共享状态空间:
-
它不仅负责变量解析,还承担了规则执行过程中的状态存储与演进;
-
它允许规则产生副作用,使多条规则之间形成自然的因果关系;
-
它能够承载对象、配置、数组等真实业务数据,使规则不再脱离领域模型;
-
它在多次
execute之间保持连续性,从而构建出规则链、流程与状态机; -
它本身是可扩展的,允许通过自定义上下文拦截、统计甚至审计规则行为。
正因如此,QLExpress 才不仅仅是一个"表达式计算器",而是一个可以真正落地于规则引擎、风控决策、配置化业务系统中的运行时规则平台。
在实际工程中,是否正确理解并设计 Context,往往直接决定了:
-
规则系统是"可配置逻辑",还是"失控脚本集合";
-
是清晰可控的状态演进,还是难以追踪的隐式副作用;
-
是具备扩展空间的规则平台,还是一次性工具代码。
一句话总结本章的核心认知:
在 QLExpress 中,规则不是在"计算数据",而是在"驱动一个世界的状态变化",而 Context,正是这个世界的全部边界。
参考资料
-
QLExpress 官方 GitHub 项目
https://github.com/alibaba/QLExpress -
QLExpress 官方 Wiki(语法与设计说明)
-
阿里巴巴规则引擎相关实践分享(搜索推荐)
-
Martin Fowler:Rules Engine(规则引擎设计思想)
https://martinfowler.com/bliki/RulesEngine.html -
State Machine & Rule-Based System Design