条件事件定义了一个给定条件解析为true时就触发的事件。它可以被用作事件子流程的开始事件,也可以用作中间事件和边界事件。条件开始事件和条件边界事件可以是可中断和不可中断的。

在camunda中,条件事件可以在流程变量的帮助下触发,在下面的图中,所有的条件事件都被使用了:

img

正如你所见,一个中间条件事件就像是一个等待,会一直等到条件被满足。在这个例子中,如果这个处理器变成可用,且这个条件是true,例如: ${processorAvailable == true},这时条件会被满足,流程会执行到下一个活动。 如果条件边界事件的条件检测到申请被改变了,接着相关的检查申请会被中断。 在整个流程的执行过程中,这个申请是可以被取消的。如果条件开始事件满足条件(申请被取消),这个流程实例的执行会被这个事件子流程中断,这会取消这个申请的当前流程。

一、条件(Condition):

为了指明一个条件事件何时应该被触发,conditionalEventDefinition元素必须要指定一个condition子元素。

<conditionalEventDefinition>
 <condition type="tFormalExpression">${var1 == 1}</condition>
</conditionalEventDefinition>

这个指定的条件是支持EL表达式的,并且可以访问流程实例变量。每次流程变量发生改变时,都会重新计算一次条件表达式。 为了防止所有流程变量改变都对条件进行计算,这个计算可以被限制到特定的变量的改变。为此,可以使用扩展属性camunda:variableName和camunda:variableEvents 默认情况下,条件计算会被任何变量的改变而触发,不管是创建更新和删除(create/update/delete)任何变量。variableName可以被用作限制指定的变量的改变。variableEvents可以限制改变的类型。可以指定多个变量的改变,用逗号隔开。这些属性可以混合使用。 conditionalEventDefinition可以被定义成下面的例子的样子:

<conditionalEventDefinition camunda:variableName="var1" camunda:variableEvents="create, update">
 <condition type="tFormalExpression">${var1 == 1}</condition>
</conditionalEventDefinition>

上面的条件只有在var1变量在被创建或者被更新的时候才会被执行。这个属性对于不可中断的事件很有用。因为这些事件可以被多次触发。

二、条件边界事件(Conditional Boundary Event):

条件边界事件就像一个监视器一样,监视指定的条件是否满足,然后被触发。 ​ 中断和不可中断条件事件是有区别的,默认是可中断事件。不可中断事件会导致原始的活动不能被中断。这个实例会保持在活动的状态。并且会创建一个额外的执行路径,taking the outgoing transition of the event。一个不可中断条件事件可以被触发多次,只要这个被依附的活动时活动的。 ​ 下面这个xml代表一个不可中断条件事件,cancelActivity属性被设置为false:

<boundaryEvent id="conditionalEvent" attachedToRef="taskWithCondition" cancelActivity="false">
 <conditionalEventDefinition>
   <condition type="tFormalExpression">${var1 == 1}</condition>
 </conditionalEventDefinition>
</boundaryEvent>

三、中间条件捕获事件(Intermediate Conditional Catch Event):

中间条件事件就像一个等待,直到条件为true。当流程执行到捕获事件时,这个条件会首先被计算。如果满足条件,流程会执行到下一个活动。如果条件不满足,流程执行会停留在这个活动上,直到条件满足。 ​ 中间条件事件被定义成一个捕获事件。指定的子元素是conditionalEventDefinition:

<intermediateCatchEvent id="conditionalEvent">
 <conditionalEventDefinition>
   <condition type="tFormalExpression">${var1 == 1}</condition>
 </conditionalEventDefinition>
</intermediateCatchEvent>

四、条件开始事件(Conditional Start Event):

条件开始实际可以被用作通过计算某些条件来发起一个流程,一个流程可以有一个或者多个条件开始事件。 ​ 如果多个条件满足,则会发起响应数量的流程实例。

当部署一个条件开始流程定义时,我们需要考虑一下事项: 1.给定流程定义的条件开始事件的条件必须是唯一的,即一个流程定义中不允许有多个条件开始实际引用相同的条件。如果多个条件开始实际包含相同的条件,流程引擎会抛出异常。 2.流程版本:如果部署一个新版的流程定义,之前版本的条件订阅将会被取消。新版本中没有提供的条件事件也会被取消。

条件开始事件可以通过下面的方法触发:

List<ProcessInstance> instances = runtimeService
  .createConditionEvaluation()
  .setVariable("temperature", 24)
  .evaluateStartConditions();
// or
List<ProcessInstance> instances = runtimeService
  .createConditionEvaluation()
  .setVariables(variableMap)
  .evaluateStartConditions();

这些提供的变量被用作计算条件,也被传递到新的流程实例中作为变量。下面的xml表明,一个条件开始事件是一个带有conditionalEventDefinition子元素的普通事件 可选属性:conditionalEventDefinition元素的可选属性variableName允许指定一个变量名,这个变量名上的变量专门用作条件事件的执行(be evaluated exclusively)

<startEvent id="conditionalStartEvent">
 <conditionalEventDefinition camunda:variableName="temperature">
   <condition type="tFormalExpression">${temperature > 20}</condition>
 </conditionalEventDefinition>
</startEvent>

五、事件子流程的条件开始事件(Conditional Start Event for Event Sub Process):

与条件边界事件类似,对于事件子流程的条件开始事件可以是可中断的,也可以是不可中断的 ​ 注意:事件子流程只能有一个开始事件 ​ 下面的xml表明条件开始事件是一个带有conditionalEventDefinition子元素的普通开始事件

<subProcess id="EventSubProcess" triggeredByEvent="true">
 <startEvent id="conditionalStartEvent">
   <conditionalEventDefinition>
     <condition type="tFormalExpression">${var1 == 1}</condition>
   </conditionalEventDefinition>
 </startEvent>
</subProcess>

六、触发条件事件(Trigger Conditional Events):

触发在作用域内的实例化(Triggering on Scope Instantiation): ​ 当一个BPMN作用域被实例化,这个条件事件可以在这个作用域内计算。这个行为叫做触发作用域内实例化。 考虑下面这个流程模型: ​ 当一个流程实例被发起,即这个流程定义的作用域被实例化了。条件子流程的条件将会在空开始事件执行之前被计算。如果满足,它会立即出发,并且空开始事件将不会被执行。这也同样适用条件边界事件和条件中间事件。

通过变量api进行触发(Triggering via Variable API):

除了在作用域实例化时触发,条件事件也可以在流程变量发生改变时触发。如果变量被创建更新和删除。 从外部设置变量(Set Variable From Outside): 变量可以通过外部的变量api进行修改。下面是一个在设置流程实例变量的例子:

//给流程实例设置变量set variable on process instance
runtimeService.setVariable(processInstance.getId(), "variable", 1);

通过委派代码设置变量(Set Variable From Delegation Code): 变量不仅仅可以通过外部api设置,也可以通过委托代码在流程实例内部设置。

//例子:
public class SetVariableDelegate implements JavaDelegate {
 @Override
 public void execute(DelegateExecution execution) throws Exception {
   execution.setVariable("variable", 1);
}
}

在委派代码中设置时,变量的改变不会立即触发条件事件,以免干扰剩下的代码的执行,相反,更改在活动实例生命周期的一个阶段结束时被记录并集体分派。 下面的图展示了一个活动实例的不同阶段:

img

1.开始关联到实例活动的开始阶段,在这时会执行输入映射和调用执行开始监听器 Starting corresponds to the starting phase of the activity instance. At this time the input mappings and execution start listeners are called. 2.执行关联到实例活动的执行阶段 Execute corresponds to the executing phase of the activity instance. 3.结束关联到实例活动的结束阶段,在这时会执行输出映射和调用执行结束监听器 Ending corresponds to the ending phase of the activity instance. At this time the output mappings and execution end listeners are called.

例如,假设变量在一个开始执行监听活动中被设置。条件事件会在所有的开始监听器被执行后并且实例活动已经准备进入执行阶段.

七、自上而下执行(Top-Down Evaluation):

变量的改变导致条件被重新计算,这个触发重新计算的方式是自上而下的。这意味着计重新算在BPMN作用域的条件事件的条件被改变时开始。然后一步一步的执行到嵌套的BPMN作用域内(被包含的子流程内)。这会一直执行到一个条件事件被触发然后回中断当前作用域内的实例(取消所有的子流程)或者直到没有更深的嵌套作用域。 ​ 例如下面的流程模型:

如果一个变量被设置到子流程的实例的context中,这个子流程的条件边界事件首先被执行。如果满足条件,执行就会被中断,否则UserTask B的条件边界事件在满足条件的情况下会被触发。

八、作用域计算(Scoped Evaluation):

当实例作用域内context的变量改变了,只会触发变量是可见的条件事件,不会影响到不相关的作用域实例。这意味着如果变量改变,只有在实例作用域内的context的条件事件的或者他们的子作用域的监听会被计算。 ​ 例如下面的流程实例:

img

如果我们开始了一个上面的流程实例,并且UserTask B和UserTask A是活动的,那么此时流程实例活动的层次关系是:

ProcessInstance
  UserTask A
  SubProcess
    UserTask B

如果流程变量被设置到子流程实例的context中,那么只有UserTask B的条件边界事件会被执行。UserTask A的边界事件不会被触发,因为这个变量在这个context中不可见。


世间山河广,祝你得偿所愿!