Activiti7 UEL表达式

4,012 阅读7分钟

这是我参与更文挑战的第7天,活动详情查看: 更文挑战

UEL表达式

  • 概述

    Activiti 使用 UEL 进行表达式解析。UEL 代表统一表达式语言,是 EE6 规范的一部分(有关详细信息,请参阅EE6 规范)。为了在所有环境中支持最新 UEL 规范的所有功能,我们使用了 JUEL 的修改版本。

    例如,表达式可用于Java 服务任务、执行侦听器、任务侦听器和条件序列流。虽然有两种类型的表达式,值表达式和方法表达式,但 Activiti 对它进行了抽象,因此它们都可以在expression需要的地方使用。

值表达式

  • 描述

    解析为一个值。默认情况下,所有过程变量都可以使用。此外,所有 spring-beans(如果使用 Spring)都可用于表达式。

    • 举例
${myVar}
${myBean.myProperty}

方法表达式

  • 描述

    调用一个方法,带或不带参数。**调用不带参数的方法时,请确保在方法名称后添加空括号(因为这可以将表达式与值表达式区分开来)。**传递的参数可以是文字值或自行解析的表达式。

    • 举例
${printer.print()}
${myBean.addNewOrder('orderName')}
${myBean.doSomething(myVar, execution)}

请注意,这些表达式支持解析bean、列表、数组和映射。

除了所有流程变量之外,还有一些默认对象可用于表达式:

  • executionDelegateExecution保存有关正在进行的执行的附加信息的 。
  • taskDelegateTask保存有关当前任务的附加信息的 。注意:仅适用于从任务侦听器评估的表达式。
  • authenticatedUserId:当前已通过身份验证的用户的 id。如果没有用户通过身份验证,则该变量不可用。

有关更具体的用法和示例,请查看Spring 中的表达式、Java 服务任务、执行侦听器、 任务侦听器或条件序列流。

变量

Activiti7 变量

  • 概述

    每个流程实例都需要并使用数据来执行它存在的步骤。在 Activiti 中,这些数据称为变量,它们存储在数据库中。变量可用于表达式(例如在独占网关中选择正确的传出序列流)、调用外部服务时的 java 服务任务(例如提供输入或存储服务调用的结果)等。

    流程实例可以有变量(称为流程变量),但也可以有执行(这是流程处于活动状态的特定指针)和用户任务可以有变量。一个流程实例可以有任意数量的变量。每个变量都存储在ACT_RU_VARIABLE数据库表中的一行中。

  • 举例

    • 启动流程引擎出入变量
    ProcessInstance processInstance = runtimeService.createProcessInstanceBuilder()
                    .processDefinitionId(actReProcdef.getId())
                    .variable("instanceVariable", instanceVariable) // 传递启动流程变量
                    .start();
    
    • 可以在流程执行期间添加变量(单前登录人完成流程节点任务)
        HashMap<String, Object> mapManagerVra = new HashMap<>();
        mapDirectorVra.put("assigneeManger", "salaboy"); // 在流程运行中添加变量
        mapDirectorVra.put("assignee", "salaboy");
        taskService.complete(taskId, mapManagerVra);
    

本地变量

  • 概述

    实际上变量都将从数据库中获取,该变量仅在该执行中可见,在执行树中不可见。如果数据不应该传播到流程实例级别,或者该变量对于流程实例中的某个路径具有新值(例如,当使用并行路径时),这会很有用。也可以再次获取变量,如下所示。请注意,TaskService上也存在类似的方法。这意味着任务,如执行,可以有局部变量是活着只是为了任务的持续时间。

  Map<String, Object> getVariables(String taskId);

  Map<String, Object> getVariablesLocal(String taskId);
  
  List<VariableInstance> getVariableInstancesLocalByTaskIds(Set<String> taskIds);

  Map<String, VariableInstance> getVariableInstancesLocal(String taskId);

  void removeVariable(String taskId, String variableName);

  void removeVariableLocal(String taskId, String variableName);

  void removeVariables(String taskId, Collection<String> variableNames);
用途
  • 描述

    变量通常用于Java 委托、表达式、执行或任务侦听器、脚本等。在这些构造中,当前执行任务对象可用,并可用于变量设置和/或检索。下面代码是实现Java代理类后执行变量设置。

public class ToUppercase implements JavaDelegate {

  public void execute(DelegateExecution execution) throws Exception {
    String var = (String) execution.getVariable("input"); // 在数据库表中获取input变量的值
    var = var.toUpperCase();
    execution.setVariable("input", var);  // 向数据库表中更新input值
    execution.getVariables(Collection<String> variableNames); // 获取数据库表变量集合的值
    execution.setVariables(Map<String, object> variables);    // 设置数据库表变量集合值
    execution.setVariable(String variableName, Object value); // 设置数据库表单个变量名和值
  }
}
变量获取值实现原理
  • 描述

    由于历史(和向后兼容的原因),在执行上述任何调用时,实际上所有变量都将从数据库中获取。这意味着,如果您有 10 个变量,并且仅通过*getVariable("myVariable")*获得一个,那么其他 9 个将在后台获取并缓存。这还不错,因为后续调用不会再次访问数据库。例如,当您的流程定义具有三个顺序服务任务(因此是一个数据库事务)时,使用一次调用来获取第一个服务任务中的所有变量可能比单独获取每个服务任务中所需的变量更好。请注意,这适用于用于获取和设置变量。

    当然,当使用大量变量或者只是想要严格控制数据库查询和流量时,这是不合适的。从 Activiti 5.17 开始,引入了新方法来对此进行更严格的控制,方法是添加具有可选参数的新方法,该参数告诉引擎是否需要在幕后获取和缓存所有变量,如下代码:

/**
*当为参数fetchAllVariables使用true 时,行为将与上面描述的完全一样:在获取或设置变量时,将获取并缓存所有其他变量。
但是,当使用false作为值时,将使用特定查询并且不会获取或缓存其他变量。只有这里有问题的变量的值才会被缓存以供后续使用。
*/
Map<String, Object> getVariables(Collection<String> variableNames, boolean fetchAllVariables);
Object getVariable(String variableName, boolean fetchAllVariables);
void setVariable(String variableName, Object value, boolean fetchAllVariables);

临时变量

  • 概述

    临时变量是行为类似于常规变量但不持久化的变量。

临时变量适用范围
  • 没有为临时变量存储历史记录。
  • 本地变量一样,临时变量在设置时放在最高父级。这意味着在执行时设置变量时,实际上存储在流程实例执行中。与本地变量一样,如果应在特定执行或任务上设置变量,则存在该方法的局部变体。
  • 临时变量只能在流程定义中的下一个等待状态之前访问。在那之后,流程走了。等待状态在这里表示流程实例中的点,在该点上它被持久化到数据存储。请注意,在此定义中,异步活动也是等待状态
  • 临时变量只能被设置setTransientVariable(名称,值),但调用时也返回瞬时变量的getVariable(名称)(*getTransientVariable(名称)*也存在,只检查瞬态变量)。这样做的原因是为了简化表达式的编写,并且使用变量的现有逻辑适用于两种类型。
  • 临时变量隐藏具有相同名称的持久变量。这意味着当在流程实例上设置了持久变量和临时变量并且使用了getVariable("someVariable") 时,将返回临时变量值。

可以在大多数暴露变量的地方获取和/或设置临时变量:

  • JavaDelegate实现execute方法的获取DelegateExecution对象。

    public class TkListener7 implements JavaDelegate {
        private static final Logger LOGGER = LoggerFactory.getLogger(TkListener7.class);
    
        @Override
        public void execute(DelegateExecution execution) {
            // 实现对变量操作
        }
    }
    
  • ExecutionListener实现notify方法的获取DelegateExecution对象。

    public class TkListener7 implements ExecutionListener {
        private static final Logger LOGGER = LoggerFactory.getLogger(TkListener7.class);
    
        @Override
        public void notify(DelegateExecution execution) {
          // 实现对变量操作
        }
    }
    
  • 在上TaskListener实现notify方法的获取DelegateTask对象

    public class TkListener7 implements TaskListener {
        private static final Logger LOGGER = LoggerFactory.getLogger(TkListener7.class);
    
        @Override
        public void notify(DelegateTask delegateTask) {
         // 实现对变量操作
        }
    }  
    
  • 通过脚本任务执行执行变量

  • 在运行服务启动流程实例时

  • 完成任务时

  • 调用runtimeService.trigger方法时

方法命名规则
  • 如下方法遵循流程变量的命名约定:
void setTransientVariable(String variableName, Object variableValue);
void setTransientVariableLocal(String variableName, Object variableValue);
void setTransientVariables(Map<String, Object> transientVariables);
void setTransientVariablesLocal(Map<String, Object> transientVariables);

Object getTransientVariable(String variableName);
Object getTransientVariableLocal(String variableName);

Map<String, Object> getTransientVariables();
Map<String, Object> getTransientVariablesLocal();

void removeTransientVariable(String variableName);
void removeTransientVariableLocal(String variableName);
临时变量使用场景
  • 临时变量使用BPMN图例

image-20210616152139769.png

  • 场景描述

    让我们假设Fetch Data服务任务调用一些远程服务(例如使用 REST)。我们还假设需要一些配置参数,并且需要在启动流程实例时提供。此外,这些配置参数对于历史审计目的并不重要,因此我们将它们作为临时变量传递,Java代码如下

ProcessInstance processInstance = runtimeService.createProcessInstanceBuilder()
       .processDefinitionKey("someKey")
       .transientVariable("configParam01", "A")
       .transientVariable("configParam02", "B")
       .transientVariable("configParam03", "C")
       .start();

  • 获取数据描述

    请注意,在到达用户任务并持久化到数据库之前,这些变量将可用。例如,在“其他工作”用户任务中,它们不再可用。另请注意,如果Fetch Data是异步的,则在该步骤之后它们也将不可用。

    获取数据(简化)可能是这样的

public static class FetchDataServiceTask implements JavaDelegate {
  public void execute(DelegateExecution execution) {
    String configParam01 = (String) execution.getVariable(configParam01);
    // ...

    RestReponse restResponse = executeRestCall();
    execution.setTransientVariable("response", restResponse.getBody());
    execution.setTransientVariable("status", restResponse.getStatus());
  }
}