多实例活动

概览

多实例活动(multi-instance activity) 是定义业务流程中某个重复步骤的一种方式。 在编程概念中,多实例与 for each 结构等价:给定一个条目集合,对于集合中的每个条目, 都可以执行一个特定的步骤,甚至是一个完整的子流程, 执行的顺序可以是 串行(sequentially) 的也可以是 并行(parallel) 的。

多实例活动也是一个常规活动,但是需定义额外的属性,名为 multi-instance characteristics(多实例特性), 这样的活动在运行时可以执行多次。 下列活动可以定义为 多实例活动

  • 用户任务

  • 脚本任务

  • 服务任务

  • 嵌入子流程

  • 调用活动

图形表示法

多实例活动在图中显示为一个常规活动, 但是在任务或子流程元素的中下部有 并行串行 标记。 三条竖线表示实例并行执行,而三条横线表示顺序执行。

multi instance
XML 表示

活动的 XML 元素使用 multiInstanceLoopCharacteristics 子元素声明多实例特性。

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

isSequential 属性表示多实例的执行顺序。 该属性默认可以省略,默认值为 false(并行)。

特殊变量

多实例中的每个实例的执行过程都是由同一个父执行过程创建,根据 BPMN 2.0 规范的要求,这个父执行过程有以下变量:

nrOfInstances

实例总数。

nrOfActiveInstances

当前以激活(尚未完成)的实例数。 对于串行多实例,该值始终为 1。

nrOfCompletedInstances

已完成的实例数。

这些变量的值可以通过调用 execution.getVariable(x) 方法获取。

另外,每个创建的子执行过程中都有一个局部变量 (其他子执行过程不可见,也不在流程实例级别存储):

loopCounter

特定实例在 for-each 循环中的索引(index)。

属性

如需将一个活动设置为多实例,请在画布中选择该活动,然后在 BPMN Navigator 面板中, 选择所需的实例类型:

multi instance types

实例数

实例数可以通过两种方式定义:

  • 直接定义 — 通过设置一个 Cardinality 参数。

  • 由集合定义 — 实例数等于集合大小。

当设置为 cardinality 时,可以使用表达式,表达式需要解析为一个正数,如:

${nrOfOrders-nrOfCancellations}
设置集合来源和集合

 — 集合来源可以选择:表达式、 流程变量、以及用户 Provider。流程引擎会按照集合的大小创建同等数量的活动:

collection source user task
用表达式设置集合

使用表达式可以为多实例活动传入一个集合。 表达式中可以包含一个 bean 方法的调用,方法需要返回对象列表:

${smpl_OrderService.getOrders()}
用流程变量设置集合

最简单的方式是使用流程变量作为集合源。 可以是实体列表、字符串列表、数字列表或任何其他对象列表。

对于用户任务,集合必须是用户名列表(String)或用户列表 (User 或其他实现了 UserDetails 的类对象)。

还有,此时唯一可用的 Assignee source表达式。 如果集合是名为 usernamesString 集合,则表达式为:

${usernames_item}

如果集合是名为 usersUserUserDetails 集合,则表达式为:

${users_items.username}

可以手动编辑表达式,但是要注意。

用 UserProvider 设置集合

最后一种方式是可以实现 UserListProvider,并提供一组用户名的列表。

参阅 用户任务 部分的 用户列表 Provider

collection 设置完成后,系统会自动创建一个 (元素变量)Element variable。 这个变量在 for each 结构中扮演迭代器变量的角色。 可以根据需要重命名这个变量。

完成条件

多实例活动一般在所有实例都完成时结束。然而, 也可以指定一个表达式,在每个实例结束时进行计算。

当此表达式的计算结果为 true 时,多实例活动结束,并销毁所有剩余实例, 流程继续前进。这种表达式必须在 completionCondition 子元素中定义。

例如,

${nrOfCompletedInstances > nrOfActiveInstances}

这个表达式表示:当完成的任务数量大于激活的实例数量时, 所有剩余的任务将被丢弃。

此外,条件表达式中也可以调用 Spring bean 方法:

${smpl_ErrorService.failure()}

使用局部变量

假设,我们想要构建一个流程,流程中的多个执行者都会写入一个变量的值。 例如,多个老师为学生的作业打分。或者也可以是一个服务任务,需要写入某些值。

在这种情况下,应该在多实例活动中使用局部变量。 否则,每个实例都会覆盖外部变量的值。

local variables multi

基于输出的条件

当多实例是一个有输出的用户任务,则可以实现某种形式的 “投票”。

在这种情况下,系统会将每个用户做出的决策存储在 输出容器(outcomes container) 中, 这是一个 OutcomesContainer 类型的流程变量。 所有实例都完成后,可以在此容器中看到所有结果。

然后,可以在排他网关或包容网关的传出的顺序流上使用以下类型的完成条件:

  • 任何人完成输出

  • 每个人完成输出

  • 没有人完成输出

例如,我们的流程中有一个多实例的用户任务,两个输出分别为 YesNo

process example multi user task

然后我们可以用这样的基于输出的条件,如, “每个人都说 Yes”:

sequence flow with outcome based condition

边界事件

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

multi instance error

补偿

多实例任务可能需要补偿。 可能冒出第一个建模的想法是这样的:

multi instance compensation bad

是的,这样可以,但并不是在所有情况下都有效。 这对于串行的同步任务是可以的,但在其他情况下可能会导致错误。

最好用带补偿的嵌入子流程来实现,这种模式在任何情况下都有效。

multi instance compensation good

执行监听器

多实例与执行监听器结合使用会出现问题。

参阅 执行监听器