camunda el flow - modric2jeff/archive GitHub Wiki

核心组件

  1. Expression Manager

    • ExpressionManager 是 Camunda 引擎中负责表达式管理的顶层入口,负责创建各种类型的表达式对象(如值表达式 ValueExpression、方法表达式 MethodExpression
    • 内部持有对底层表达式语言(默认为 JUEL,也可替换成 MVEL、Groovy 等)的配置和工厂引用
  2. Expression Factory

    • 对接具体表达式语言的工厂接口,例如 JUEL 的 ExpressionFactoryImpl
    • 负责将字符串形式的表达式解析并编译成可执行的表达式对象
  3. Expression

    • 通用接口,定义了 getValue(VariableScope scope) 等方法

    • 主要实现:

      • ValueExpression(读取变量、属性引用、算术运算等)
      • MethodExpression(调用对象方法)
  4. Variable Scope

    • VariableScope 接口及其实现(如 DelegateExecutionDelegateTask),为表达式求值提供上下文数据
    • 变量查找遵循层次:局部变量 → 父作用域变量 → 全局流程变量
  5. ELContext & FunctionMapper

    • 在 JUEL 中,通过 ELContext 维护求值时的上下文环境(变量解析器、函数映射器、属性解析器等)
    • Camunda 扩展注册了自定义函数(如日期函数、XML 处理函数等)

总体架构

+-----------------------------------------------------------+
| Camunda Process Engine                                    |
|                                                           |
|  +------------------+     +------------------+            |
|  | BPMN 解析 & 部署  | --> | BPMN 执行引擎     |            |
|  +------------------+     +------------------+            |
|                                      |                    |
|                                      v                    |
|                             +------------------+          |
|                             | ExpressionManager|          |
|                             +------------------+          |
|                                      |                    |
|                  +-------------------+----------------+   |
|                  |                                      |  |
|     +----------------------+        +----------------------+  |
|     | ExpressionFactory    |        | Variable Scopes      |  |
|     +----------------------+        +----------------------+  |
|                  |                                      |  |
|                  v                                      |  |
|         +---------------------+                         |  |
|         | Parsed & Compiled   |                         |  |
|         | Expression (AST)    |                         |  |
|         +---------------------+                         |  |
|                  |                                      |  |
|                  v                                      |  |
|         +---------------------+                         |  |
|         | ELContext (JUEL)    |                         |  |
|         +---------------------+                         |  |
|                  |                                      |  |
|                  v                                      |  |
|         +---------------------+                         |  |
|         | 求值(变量替换、调用)|<--- VariableScope ------+  |
|         +---------------------+                            |
+-----------------------------------------------------------+

表达式处理流程

  1. 解析阶段

    • BPMN 文件或 API 中读取到字符串表达式(如 ${order.amount * 0.1}
    • ExpressionManager.createExpression(String) 调用底层 ExpressionFactory.createValueExpression(...)
    • 字符串被解析成内部 AST(Abstract Syntax Tree)
  2. 编译/缓存

    • 对于相同文本的表达式,Camunda 会在 ExpressionManager 级别进行缓存,避免重复解析
    • AST 被保存在 ValueExpression 对象中
  3. 求值阶段

    • 引擎执行到包含表达式的节点(如条件网关、任务属性、脚本任务等)时,调用 expression.getValue(variableScope)

    • 构造 ELContext,注入:

      • 当前 VariableScope(变量容器)
      • 自定义函数映射
      • 属性解析器(用于处理点操作,如 foo.bar
    • JUEL Runtime 遍历 AST:

      1. 遇到变量名时,委托给 VariableMapperVariableScope 中读取
      2. 遇到运算符时,执行相应算术/逻辑运算
      3. 遇到方法调用时,通过 MethodExpression 调用 Java 方法
    • 最终返回计算结果


示例:求值流程演示

假设在某个用户任务的条件表达式中使用:

<sequenceFlow id="flow1" 
   sourceRef="task1" 
   targetRef="task2" 
   conditionExpression="${invoice.amount > 1000}" />
  1. 部署时

    Expression conditionExpr = expressionManager.createExpression("${invoice.amount > 1000}");
    // 解析为 ValueExpression,并缓存 AST
    
  2. 执行时

    // 假设 currentExecution 是 DelegateExecution,实现了 VariableScope
    boolean takeFlow = (Boolean) conditionExpr.getValue(currentExecution);
    
  3. 内部求值步骤

    1. 构造 ELContext,绑定 currentExecution

    2. AST 根节点为 “>” 运算

    3. 对左右子树求值:

      • 左侧子树:解析 invoice.amount

        1. 读取变量 invoice(一个 Java Bean 或 Map)
        2. 通过属性解析器调用 getAmount()
      • 右侧子树:数字常量 1000

    4. 执行比较运算 >, 得到布尔结果

    5. 返回结果给流程引擎,决定流程走向


通过以上架构与流程,Camunda 7 的表达式引擎能够灵活地将 BPMN 中的动态逻辑与 Java 运行时数据无缝集成,支持复杂的条件判断、函数调用和脚本扩展。