1、描述

多实例活动是定义用于业务流程的某些步骤的重复的方法。在编程概念中,多实例相当于每个构造:它允许您为给定集合中的每个项目按顺序或并行地执行某个步骤,甚至完成一个子流程。

多实例是具有定义(名为额外的属性规则的活动“多实例特性” ),其将导致在运行时将要执行的活动多次。以下活动可以成为_多实例活动:

  • 用户任务
  • 脚本任务
  • Java服务任务
  • Web服务任务
  • 业务规则任务
  • 电子邮件任务
  • 手动任务
  • 接收任务
  • (嵌入式)子流程
  • 呼叫活动

一个网关或事件 不能成为多实例。

按照BPMN 2.0规范的要求,为每个实例创建的执行的每个父执行将具有以下变量:

  • nrOfInstances:实例的总数
  • nrOfActiveInstances:当前活动(尚未完成)实例的数量。对于顺序多实例,这将始终是1。
  • nrOfCompletedInstances:已经完成的实例的数量。

这些值可以通过调用execution.getVariable(x)方法来检索。

此外,每个创建的执行都将有一个执行本地变量(对于其他执行不可见,并且不存储在流程实例级别):

  • loopCounter:指示特定实例的for-each循环中的索引。loopCounter变量可以使用Flowable的elementIndexVariable属性重命名。

2、图形表示法

如果一个活动是多实例,则在活动底部用三条短线表示。三条垂直线表示实例将并行执行,而三条水平线表示顺序执行。

flowable 多实例 elementVariable flowable 多实例 指定方式_OA

3、XML表示

要创建活动多实例,活动XML元素必须具有multiInstanceLoopCharacteristics子元素。

<multiInstanceLoopCharacteristics isSequential="false|true">
...
</multiInstanceLoopCharacteristics>

所述isSequential属性表示该活动的实例被顺序地或并行地执行。

进入活动时计算一次实例的数量。有几种配置方法。一种方法是使用loopCardinality子元素直接指定一个数字。

<multiInstanceLoopCharacteristics isSequential="false|true">
<loopCardinality>5</loopCardinality>
</multiInstanceLoopCharacteristics>

解析为正数的表达式也是允许的:

<multiInstanceLoopCharacteristics isSequential="false|true">
<loopCardinality>${nrOfOrders-nrOfCancellations}</loopCardinality>
</multiInstanceLoopCharacteristics>

另一种定义实例数的方法是使用loopDataInputRef子元素指定一个集合的过程变量的名称。对于集合中的每个项目,都将创建一个实例。可选地,可以使用inputDataItem子元素为实例设置集合的特定项目。这在以下XML示例中显示:

<userTask id="miTasks" name="My Task ${loopCounter}" flowable:assignee="${assignee}">
<multiInstanceLoopCharacteristics isSequential="false">
<loopDataInputRef>assigneeList</loopDataInputRef>
<inputDataItem name="assignee" />
</multiInstanceLoopCharacteristics>
</userTask>

假设变量assigneeList包含这些值[kermit, gonzo, fozzie]。在上面的代码片段中,三个用户任务将并行创建。每个执行都将有一个名为assignee包含集合的一个值的流程变量,在本例中用于分配用户任务。

loopDataInputRef的缺点,inputDataItem这些名称是相当难记,并且由于BPMN 2.0架构的限制,他们不能包含表达式。Flowable通过在以下方面提供collection和elementVariable属性来解决这个问题multiInstanceCharacteristics:

<userTask id="miTasks" name="My Task" flowable:assignee="${assignee}">
<multiInstanceLoopCharacteristics isSequential="true"
flowable:collection="${myService.resolveUsersForTask()}" flowable:elementVariable="assignee" >
</multiInstanceLoopCharacteristics>
</userTask>

请注意,该collection属性将被解析为一个表达式。当表达式解析为字符串而不是集合时,或者因为它是静态字符串值,或者表达式本身解析为字符串,则结果值将用作获取集合过程变量的变量名称。

例如,在下面的代码片断中,假设集合存储在assigneeList过程变量中:

<userTask id="miTasks" name="My Task" flowable:assignee="${assignee}">
<multiInstanceLoopCharacteristics isSequential="true"
flowable:collection="assigneeList" flowable:elementVariable="assignee" >
</multiInstanceLoopCharacteristics>
</userTask>

例如,假设myService.getCollectionVariableName()返回一个字符串值,该值将被用作变量名来获取存储为流程变量的集合。

<userTask id="miTasks" name="My Task" flowable:assignee="${assignee}">
<multiInstanceLoopCharacteristics isSequential="true"
flowable:collection="${myService.getCollectionVariableName()}" flowable:elementVariable="assignee" >
</multiInstanceLoopCharacteristics>
</userTask>

所有实例完成后,多实例活动结束。但是,可以指定每次实例结束时评估的表达式。当此表达式计算结果为true时,所有剩余的实例将被销毁,并且多实例活动结束,继续进程。这样的表达式必须在completionCondition子元素中定义。

<userTask id="miTasks" name="My Task" flowable:assignee="${assignee}">
<multiInstanceLoopCharacteristics isSequential="false"
flowable:collection="assigneeList" flowable:elementVariable="assignee" >
<completionCondition>${nrOfCompletedInstances/nrOfInstances >= 0.6 }</completionCondition>
</multiInstanceLoopCharacteristics>
</userTask>

在这个例子中,将为assigneeList集合的每个元素创建并行实例。但是,当60%的任务完成时,其他任务将被删除,并继续进行。

3、边界事件和多实例

由于多实例是常规活动,因此可以在其边界上定义边界事件。在中断边界事件的情况下,当事件被捕获时,所有仍然活动的实例将被销毁。

以下面的多实例子过程为例:

flowable 多实例 elementVariable flowable 多实例 指定方式_工作流_02

这里,当定时器触发时,子进程的所有实例都将被销毁,无论有多少实例或哪些内部活动尚未完成。

4、多实例和执行监听器

将执行监听器与多实例组合使用时有一个警告。举个例子,下面的BPMN 2.0 XML片段被定义在与multiInstanceLoopCharacteristics XML元素相同的级别上:

<extensionElements>
<flowable:executionListener event="start" class="org.flowable.MyStartListener"/>
<flowable:executionListener event="end" class="org.flowable.MyEndListener"/>
</extensionElements>

对于正常的BPMN活动,当活动启动并结束时,将会有这些监听器的调用。

但是,当活动是多实例时,行为是不同的:

  • 当输入多实例活动时,在执行任何内部活动之前,会引发启动事件。该循环计数器变量尚未设置(它为空)。
  • 对于访问的每个实际活动,都会引发一个启动事件。该循环计数器变量。

结束事件适用同样的逻辑:

  • 离开实际活动之后,甚至会抛出一个结局。该循环计数器变量。
  • 当多实例活动作为一个整体完成时,抛出一个结束事件。该循环计数器变量没有设置。
    例如:
<subProcess id="subprocess1" name="Sub Process">
<extensionElements>
<flowable:executionListener event="start" class="org.flowable.MyStartListener"/>
<flowable:executionListener event="end" class="org.flowable.MyEndListener"/>
</extensionElements>
<multiInstanceLoopCharacteristics isSequential="false">
<loopDataInputRef>assignees</loopDataInputRef>
<inputDataItem name="assignee"></inputDataItem>
</multiInstanceLoopCharacteristics>
<startEvent id="startevent2" name="Start"></startEvent>
<endEvent id="endevent2" name="End"></endEvent>
<sequenceFlow id="flow3" name="" sourceRef="startevent2" targetRef="endevent2"></sequenceFlow>
</subProcess>

在这个例子中,假设受让人列表有三个项目。以下情况在运行时发生:

  • 作为一个整体的多实例抛出一个启动事件。在开始执行监听器被调用。该循环计数器和受让人的变量将不会被设置(他们会为空)。
  • 每个活动实例都会引发一个启动事件。在开始执行监听器被调用三次。该循环计数器和受让人变量将被设置(NOT NULL)。
  • 所以,总共,启动执行监听器被调用四次。

请注意,同样适用于multiInstanceLoopCharacteristics也在子过程以外的其他项上定义的情况。例如,如果上面的例子是一个简单的userTask,那么同样的推理仍然适用。