QLExpress 上下文变量解析:从参数容器到规则运行时世界模型

目录

一、整体内容练习演示

二、各部分详细讲解和指导说明

(一)上下文的本质:规则的运行时内存模型

(二)基础上下文:变量即世界的"基础属性"

[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,正是这个世界的全部边界。

参考资料

相关推荐
张彦峰ZYF1 天前
从入门到可落地:QLExpress 基本语法体系化学习与实践指南
qlexpress·从入门到可落地·基本语法体系化学习与实践指南
张彦峰ZYF9 天前
QLExpress :一款从内部工具到开源社区核心的脚本引擎
qlexpress·开源社区核心的脚本引擎·工程价值优先·社区与业务并重·技术选择与时间成本·ai 时代规则引擎的重新定位
小码农叔叔2 年前
【微服务】java 规则引擎使用详解
规则引擎·drools·规则引擎使用详解·java使用规则引擎·qlexpress·drools使用详解·java整合drools