事件
概览
在 BPMN 表示法中,事件表示在业务流程中发生的事情。 事件可以发起流程中活动的启动、继续或完成等动作。
事件还可用于对各种场景进行建模,例如接收消息、时间流逝或发生错误。 通过在 BPMN 图中引入事件,用户可以有效地捕获业务流程的行为和流向。
“事件” 这个词有两种不同的含义:
事件可以在不显示在流程图上的情况下发生。例如,消息事件应以编程方式引发。 |
事件分类
首先,事件因它们在过程中的位置而异,可能是启动、中间或结束。 因此可以分为 start、intermediate 或 end 事件。
事件的行为由激活事件的触发器定义。 例如,消息或定时器。
流程可以使用或产生事件。 因此,这些 BPMN 事件称为 'catching' 和 'throwing' 事件。
某些事件还可以添加到活动;此类事件称为 'boundary'。
以及,事件可能具有中断或继续流程的属性。 这里,事件可分为 interruptible 和 non-interruptible 事件。
为方便起见,下面的表格中总结了事件的分类:
标准 | 分类 |
---|---|
流程中的位置 |
Start, Intermediate, End, |
可触发 |
None, Timer, Message, Signal, Error, Compensation |
角色 |
Catching/Throwing |
是否附加活动 |
Boundary |
是否能中断流程 |
Interrupting 或 Non-interrupting |
流程中的位置
根据事件在流程中的位置,可分为:
所有事件在图中都显示为一个圆圈,圆圈线的类型(单线、双线、粗线) 表示事件在流程中的不同位置(启动、中间、结束)。
下面 XML 中是上图事件的一个示例:
<bpmn:process id="Process_1" isExecutable="true">
<bpmn:startEvent id="start-event" name="Start event"> (1)
<bpmn:outgoing>Flow_out_start</bpmn:outgoing>
</bpmn:startEvent>
. . .
<bpmn:intermediateThrowEvent id="intermediate-event" name="Intermediate event"> (2)
<bpmn:incoming>Flow_in</bpmn:incoming>
<bpmn:outgoing>Flow_out</bpmn:outgoing>
</bpmn:intermediateThrowEvent>
. . .
<bpmn:endEvent id="end-event" name="End event"> (3)
<bpmn:incoming>Flow_in_end</bpmn:incoming>
</bpmn:endEvent>
</bpmn:process>
1 | — 启动事件 |
2 | — 中间事件 |
3 | — 结束事件 |
捕获或抛出事件
在 BPMN 2.0 中,存在两个主要事件类别:catching 和 throwing 事件。
-
捕获(Catching): 当流程执行至该事件时,将等待触发器的激活。 触发器的类型由 XML 中的内部图标或类型声明定义。 捕获事件与抛出事件在图形上通过未填充的内部图标(通常为白色)进行区分。
-
抛出(Throwing): 当流程执行至该事件时,将激活触发器。 触发器的类型由 XML 中的内部图标或类型声明定义。 抛出事件与捕获事件在图形上通过填充的内部图标(通常为黑色)进行区分。
示例,请参考下面的捕获和抛出信号事件:
边界事件
边界事件是捕获(型)事件,依附于活动(任务、嵌入的子流程或调用活动)。 可以是附着在活动上的多个事件。 边界事件总是捕获事件,无法被抛出。
事件子流程 不能有边界事件。 |
在 XML 中,边界事件由特殊标签标记,具有 attachedToRef
属性,执行依附的活动:
<boundaryEvent id="Event_0gl2f4v" attachedToRef="Activity_1fsayqc">
<timerEventDefinition id="TimerEventDefinition_0w9bip4" />
</boundaryEvent>
流程中断
该事件可以中断正常的流程执行。 适用于事件子流程中的边界事件和启动事件。
-
中断(Interrupting) — 活动被中断,流程按照该事件的流向继续。
-
非中断(Non-interrupting) — 与主流程同步开启一个新的流程分支,不会中断主流程的流向。
中断事件在图中展示为一个常规的过程中事件,依附于活动(任务或子流程), 而非中断事件的边线为虚线。
非中断事件可以触发多次,每次都会开启一个新的流程执行(生成新令牌),直到任务完成。 例如,非中断循环计时器每 5 分钟触发一次,并向用户发送通知。
行为类型由 cancelActivity
属性定义。
默认设置为 true
,对于中断事件,可以省略该属性的配置。
对于非中断事件,需要显式设置为 false
。
示例,非中断定时器事件:
<boundaryEvent id="Event_01" cancelActivity="false" (1)
attachedToRef="user-task">
<timerEventDefinition id="TimerEventDefinition_14a8e0l" />
</boundaryEvent>
1 | — 定义非中断定事件。 |
事件定义
在 BPMN 中,消息(messages) 和 信号(signals) 是一种流程内或流程间不同元素通信的机制。 消息和信号在 BPMN 图中定义各种元素之间的交互和依赖关系方面都起着至关重要的作用。 错误(Errors) 与消息和信号类似,也可以用于控制流程的流向。
消息和信号在事件中使用之前必须先进行定义。 在某些情况下,可以省略错误定义。
如需在 Studio 中定义事件,打开流程模型后,不要选择任何内容,此时可以访问流程的属性, 找到信号和消息定义的部分:
创建消息时,需要定义其 id 和 name:
创建信号时,需要定义其 id 和 name; 必须选择一个 scope(范围)参数,Global(全局) 或 Process instance(仅流程实例):
创建错误时,需要定义其 id 和 name;此外,还可以设置 Error code(错误码):
消息、信号和错误在 XML 中通常位于 <process> 和 <diagram> 之间。
</process>
<message id="green" name="Green" /> (1)
<message id="yellow" name="Yellow" />
<message id="red" name="Red" />
<signal id="ready" name="Ready" flowable:scope="global" /> (2)
<signal id="stop" name="Stop" flowable:scope="processInstance" />
<error id="failure" name="Failure" errorCode="500" /> (3)
<error id="fatal" name="Fatal" /> <bpmndi:BPMNDiagram id="BPMNDiagram_process">
1 | — 消息定义 |
2 | — 信号定义 |
3 | — 错误定义 |
如果需要在不同的流程中使用同样的消息和信号, 必须在每个 BPMN 模型中都进行定义。 you must create their definitions in each BPMN model. |
事件子流程可以有中断或非中断启动事件。
-
中断 — 当事件子流程结束时,主流程也终止。
-
非中断 — 事件子流程并行执行并结束, 主流程继续。
在上图中,第一个子流程在时间结束时中断主流程。 第二个子流程执行服务任务并结束,而不会影响主流程。
事件订阅
事件订阅是一种机制,流程可以在继续执行特定事件之前等待特定事件的发生。 事件订阅类型包括:
-
消息(Message)
-
信号(Signal)
-
定时器(Timer)
-
错误(Error)
-
补偿(Compensation)
有两种情况:
-
启动事件 — 订阅在部署时创建。
-
中间和边界事件 — 在流程进行到该事件时创建订阅。
每个事件订阅都可以配置一些参数,这些参数用来定义事件如何与流程实例相关联。 包括指定事件的类型、名称以及任何触发该订阅的其他必要的关联参数。
事件订阅存储在数据库的 ACT_RU_EVENT_SUBSCR
表中。
其中包含有关事件类型、流程实例 ID 以及消息事件所需的其他关联参数的信息。
当相应的事件发生时(例如,收到一条消息或定时器到期), 流程引擎会检查与事件条件匹配的订阅信息。 对于消息事件,应用程序必须使用关联参数将消息与正确的流程实例相关联。
-
创建事件订阅
可以通过编程的方式为消息、信号和定时器事件创建事件订阅。例如,可以通过发送消息来启动流程实例:
// 用消息启动流程 ProcessInstance processInstance = runtimeService.startProcessInstanceByMessage("orderPlaced", processVariables);
-
查询事件订阅
可以查询已有的事件订阅,以查看哪些流程正在等待特定的事件。例如:
List<EventSubscription> messageSubscriptions = runtimeService.createEventSubscriptionQuery() .eventType("message") .list();
-
获取事件订阅
通过
RuntimeService
可以获取订阅的列表。 例如,获取信号订阅:// 查询所有的信号事件订阅 List<EventSubscription> signalSubscriptions = runtimeService.createEventSubscriptionQuery() .eventType("signal") .list();
如需获取其他类型的事件,可以用
"message"
、"error"
或"compensate"
参数值。对于定时器,需要使用
ManagementService
:List<Job> timerJobs = managementService.createTimerJobQuery().list();
-
触发事件订阅
可以在相应事件发生时以编程方式触发事件订阅。 例如,要触发消息事件:
runtimeService.messageEventReceived("orderPlaced", executionId, processVariables);
-
删除事件订阅
如果不再需要事件订阅,可以将其删除。 例如,要删除特定事件订阅:
runtimeService.createEventSubscriptionQuery() .eventType("message") .processInstanceId(processInstanceId) .singleResult();
参阅 监听器 部分。
启动事件
启动事件 是流程的入口点 当引擎尝试启动执行流程时,会在 BPMN 模型中搜索启动事件。
因此,一个流程 必须 具有一个 启动事件。 启动事件总是捕获(型):从概念上讲,该事件(在任何时候)都在等待,直到某个触发器发生。
启动事件可以是下列类型之一:
尽管 BPMN 支持多个启动事件,但从技术上讲,流程可能只有一个 空 启动事件。否则,将导致部署错误。 不要使用多个 空 启动事件,如下图所示:
但是,流程可以使用多个其他类型的启动事件:
可以使用多个消息(或信号)启动事件,前提是这些消息(或信号)不同。
空事件
空(None)事件是未指定的事件。
空启动事件
从技术上讲,空启动事件表示未指定用于启动该流程实例的触发器。 也就是说,引擎无法猜测该流程实例何时启动。
嵌入式子流程 具有空启动事件。 |
空启动事件通过没有内部图标(即,没有触发类型)的圆圈表示。
空启动事件用两个特殊的属性:
-
流程变量(Process variables) — 提供通过 API 启动流程的参数信息。
-
表单(Form) — 定义手动启动流程时展示的 UI。
可以通过单击 BPMN Inspector 面板中的 create 链接来定义启动事件中的流程变量:
输入变量名并按回车:
默认情况下,新变量使用 String
类型创建,也可以将其类型更改为所需的类型。
此处定义的流程变量不会在流程实例中创建。 必须以某种方式进行初始化。 例如,可以使用脚本任务。 |
如果在表单之前创建了流程变量,则会在表单中添加这些变量。
关于 表单 的配置,请参阅 流程表单 部分。
空启动事件的 XML 表示形式是一个常规的启动事件声明,但没有任何子元素。 (其他启动事件类型都有一个声明类型的子元素)。
<startEvent id="startEvent1" name="Start"> (1)
<extensionElements>
<jmix:processVariables>
<jmix:processVariable name="invoiceId" type="string" /> (2)
</jmix:processVariables>
<jmix:formData type="no-form" /> (3)
</extensionElements>
<outgoing>Flow_0h77bcd</outgoing>
</startEvent>
1 | — 启动事件的定义 |
2 | — 流程变量 |
3 | — 这里可以是一个表单的定义 |
API 触发空启动事件
当通过 API 调用名称类似 startProcessInstanceByXXX
的方法之一启动流程实例时,将使用空启动事件。
例如:
ProcessInstance processInstance = runtimeService
.startProcessInstanceByKey("process-id");
Flowable API 使用术语 'process definition key' ,相当于 Jmix Studio 中的 'process id'。 |
请参阅 Flowable API 部分。
定时器事件
BPMN 中的定时器事件是根据预定义的时间点或持续时长触发的事件。 定时器事件可以指定某些活动何时执行,以控制流程的走向。
定时器事件有两种类型:
另外,定时器也可以用作 边界事件,可以是中断或非中断。
定时器启动事件
定时器启动事件 用来在特定时间创建流程实例。 可用于仅启动一次或按特定时间间隔启动的流程。
定时器启动事件在图中展示为内部有时钟图标的圆圈。
定时器启动事件有一个特殊的 Timer Definition 属性,用于定义其类型和表达式。 参阅 定时器类型。
定时器启动事件的 XML 是一个常规的启动事件声明,带有定义定时器的子元素。 有关配置的详细信息,请参阅定时器定义。
<startEvent id="theStart">
<timerEventDefinition> (1)
. . . (2)
</timerEventDefinition>
</startEvent>
1 | — 定时器事件定义 |
2 | — 定义子元素 |
定时器中间事件
定时器中间事件(Timer intermediate event) 可以作为秒表。执行到该事件时,将启动定时器。 当定时器在指定的时间间隔或指定的时间到达后触发时,流程将继续。
定时器中间事件是一个 等待状态。
定时器中间事件在图中展示为中间捕获事件,内部有一个定时器图标。
定时器中间事件的属性与定时器启动事件一样。
定时器中间事件的 XML 定义是中间捕获事件。
此时,其表示类型的子元素是 timerEventDefinition
。
<intermediateCatchEvent id="timer">
<timerEventDefinition>
<timeDuration>PT8H</timeDuration> (1)
</timerEventDefinition>
</intermediateCatchEvent>
1 | — 定时器类型,例如 duration 。 |
定时器类型
系统支持以下三种定时器:
Duration |
— 在指定的时长之后触发。 |
Cycle |
— 根据 Cron 表达式 周期触发。 |
Date |
— 在指定的日期触发;如果指定的日期是以前的日期,则忽略该事件。 |
对于启动定时器事件,时长是从流程部署到服务器的那一刻就开始计算。 |
配置时间
设置定时器的时间参数有两种方式:
-
ISO 8601 格式的字符串
ISO 8601 标准
ISO 8601 是一项关于全球日期时间相关数据交换和通信的国际标准。
2035-06-17T07:42:14 |
— 日期时间:2035 年 6 月 17 日,7 时 42 分 14 秒 |
2050:01:01 |
— 日期时间:2050 年 1 月 1 日,0 时 0 分 0 秒 |
PT30D |
— 时长,30 天 |
PT10M |
— 时长,10 分 |
P3Y6M4DT12H30M5S |
— 时长,3 年 6 个月 4 天 12 小时 30 分钟 5 秒 |
R3PT10H |
— 周期,每 10 小时重复 3 次 |
不要使用小于 3 秒的短时长。BPM 不是一个实时系统。 |
参阅 ISO 8601 标准网站了解更多。
消息事件
消息事件是引用 消息 的事件。 用于对业务流程的不同部分之间或不同流程之间的通信进行建模。 消息事件表示在流程流中发送(抛出)或接收(捕获)消息。
消息事件有两种类型:
另外,消息事件也可以作为边界事件使用。
Jmix BPM 不支持消息抛出事件(中间、结束)。 参阅 workaround。 |
消息启动事件
消息启动事件(message start event) 可使用命名消息启动流程实例。
消息启动事件在图中显示为带有消息事件符号的圆圈。 如果符号未填充,则表示捕获 (接收) 行为。
消息启动事件必须具有 Message 属性,指向已有的 消息定义。这是必须的,否则会导致部署错误。
消息启动事件的 XML 表示是带有 messageEventDefinition
子元素的普通启动事件:
<startEvent id="Message_start_event>
<messageEventDefinition id="MessageEventDefinition_invoice"
messageRef="new-invoice-message" /> (1)
</startEvent>
1 | — 指向消息定义。 |
使用消息启动事件
一个流程可以有一个或多个消息启动事件,但是必须是不同的消息。
流程部署后,引擎会为每个消息启动事件创建一条消息 订阅。 任何该流程老版本的订阅都将关闭。
消息启动事件的名称在所有已部署的流程定义中必须唯一。 如果一个流程定义包含一个或多个消息启动事件,事件中引用了已部署的其他流程定义使用的同名启动消息, 流程引擎在部署时会抛出异常。 |
消息中间捕获事件
消息中间捕获事件(intermediate catching message event) 使用特定的名称捕获消息。
中间捕获消息事件在图中展示为典型的中间事件(双线的圆圈),内部有消息图标。消息图标未填充表示“捕获”。
消息中间捕获事件与消息启动事件的属性相同。
消息中间事件在 XML 中定义为中间捕获事件。
此时,其表示类型的子元素是 messageEventDefinition
。
<intermediateCatchEvent id="catch-message-event"
name="Catch message">
<messageEventDefinition id="MessageEventDefinition_16bx9rl"
messageRef="message-one" />
</intermediateCatchEvent>
当流程进入中间消息捕获事件时,将创建相应的消息订阅。 流程实例会在这里停止并等待,直到收到消息。 之后,捕获事件完成,流程继续执行。
消息中间捕获事件是一个 等待状态。
在下面的示例中,刘成刚将在 Activity 1
完成后等待消息,在收到消息后执行 Activity 2
。
边界消息事件
活动边界上依附的中间捕获消息,或简称 边界消息事件(boundary message event), 捕获与引用的消息定义同名的消息。
边界消息事件可以是可中断的(实双线)或不可中断的(虚双线)。
边界消息事件与消息启动事件的属性相同。
边界事件在 XML 中做为依附的父活动的子元素。消息事件必须具有 messageRef
属性,引用已有消息定义。
非可中断消息事件具有属性 cancelActivity
=false
。
<task id="Activity_task" name="Task" /> (1)
<boundaryEvent id="interruptible-message-event" name="Message 1" (2)
attachedToRef="Activity_task">
<messageEventDefinition id="MessageEventDefinition_1"
messageRef="messageOne" />
</boundaryEvent>
<boundaryEvent id="non-interruptible-message-event" name="Message 2" (3)
cancelActivity="false" (4)
attachedToRef="Activity_task">
<messageEventDefinition id="MessageEventDefinition_2" messageRef="messageTwo" />
</boundaryEvent>
1 | — 消息事件依附的任务。 |
2 | — 可中断的消息事件。 |
3 | — 不可中断的消息事件。 |
4 | — cancelActivity 属性。 |
信号事件
信号事件是引用 信号定义 的事件。 广播信号会触发所有匹配的信号事件。 信号具有范围,可以是 Global(全局) 或 Process instance(仅流程定义)。
Jmix BPM 中有下列信号事件:
不支持信号结束事件。请使用 workaround。 |
信号启动事件
信号启动事件(signal start event) 可以使用一个命名的 信号定义 启动流程实例。 流程可以有一个或多个信号启动事件,但信号定义必须不同。
信号启动事件在图中显示为带有信号事件符号的圆圈。如果符号未填充,则表示捕获 (接收) 行为。
信号启动事件具有 Signal
属性,指向特定的信号定义。这个是必需属性,否则会导致部署错误。
信号启动事件的 XML 表示是带有 signalEventDefinition
子元素的普通启动事件:
<startEvent id="signal-start-event" name="Start">
<signalEventDefinition id="SignalEventDefinition_00paqo6" (1)
signalRef="signal-one" /> (2)
<outgoing>Flow_0h77bcd</outgoing>
</startEvent>
1 | — 事件声明 |
2 | — 特定信号定义 |
使用信号启动事件
流程部署后,引擎会为每个信号启动事件创建一条信号 订阅。 任何该流程老版本的订阅都将关闭。
允许多个流程定义的信号启动事件引用同一信号。 当信号触发时,将激活所有的订阅并启动流程。
流程实例内可以使用 intermediate signal throw event 触发信号,或者通过 API signalEventReceived
触发。
在 API 调用中,请使用信号的 name,而不是 id。例如,我们有这样的信号定义:
<signal id="ready" name="Ready" flowable:scope="global" />
那么 API 方法可以这样调用:
runtimeService.signalEventReceived("Ready");
嵌入式子流程不支持信号启动事件。 |
信号中间捕获事件
信号中间捕获事件在图中展示为典型的中间事件(双线的圆圈),内部有信号图标。 图标未填充表示“捕获”。
信号中间捕获事件与信号启动事件的属性相同。
<intermediateCatchEvent id="signal-catch-event" name="Catch signal">
<incoming>Flow_0qwib28</incoming>
<outgoing>Flow_1itm8do</outgoing>
<signalEventDefinition id="SignalEventDefinition_1" (1)
signalRef="signal-one" /> (2)
</intermediateCatchEvent>
1 | — 信号事件声明 |
2 | — 引用信号定义 |
信号中间抛出事件
信号中间抛出事件(intermediate throwing signal event) 会抛出一个信号事件。 抛出的信号会广播至所有的捕获信号事件,包括启动和中间信号 订阅。
信号中间抛出事件在图中展示为典型的中间事件(双线的圆圈), 内部有信号图标。 图标已填充表示“抛出”。
中间信号捕获事件属性与信号启动事件相同, 但语义不同 — 是抛出,而不是捕获。
信号发布模式
信号可以同步或异步发布。
-
在默认配置中,信号是同步传递的。 抛出的流程实例会等待,直到信号发布至所有的捕获流程实例。 捕获流程实例也会在与抛出流程实例相同的事务中得到通知。 也就是说,如果其中一个通知的实例产生技术错误(抛出异常), 所有涉及的实例都会失败。
-
信号也可以异步传递。 此时,当抛出信号到达时,才确定哪些处理程序处于活动状态。 对于每个活动处理程序,异步通知消息作业(Job)由
JobExecutor
存储和传递。
信号中间事件在 XML 中定义为中间抛出事件。
这里需要使用 signalEventDefinition
子元素。
<intermediateThrowEvent id="Event_sync">
<signalEventDefinition id="SignalEventDefinition_14tnjbf"
signalRef="my-signal" /> (1)
</intermediateThrowEvent>
<intermediateThrowEvent id="Event_async">
<signalEventDefinition id="SignalEventDefinition_14tnjbf"
signalRef="my-signal" flowable:async="true" /> (2)
</intermediateThrowEvent>
1 | — 省略了属性 async ,默认为 false ,信号同步发布。 |
2 | — 信号异步发布。 |
错误事件
BPMN 中的错误事件通常用于对流程执行过程中可能出现的异常或错误情况进行建模。 可以依附于 BPMN 图中的活动或子流程,定义应如何处理依附对象的错误。 例如,通过触发错误处理方法,可以记录错误或通知相关参与者。
错误事件有以下类型:
错误启动事件
错误启动事件(error start event) 可用于触发 事件子流程。 不能用于启动流程实例。错误启动事件是中断的(类型)。
错误启动事件在图中展示为带有错误事件符号的圆圈。如果符号未填充,则表示捕获 (接收) 行为。
错误启动事件有一个特殊的属性 — Error,引用某些 错误定义。
与消息和信号不同,错误定义不是必须设置。 如果没有设置错误定义,则发生的每个错误事件都可以启动子流程。
错误启动事件的 XML 表示是带有 errorEventDefinition
子元素的常规启动事件声明:
<startEvent id="error-event" name="Error">
<errorEventDefinition id="ErrorEventDefinition_1" (1)
errorRef="failure" /> (2)
</startEvent>
1 | — 错误事件声明 |
2 | — 设置错误定义。 |
错误边界事件
错误边界事件(error boundary event) 可以捕获在依附的活动范围内抛出的错误。
错误边界事件在图中显示为边界上的中间事件(双线的圆圈), 内部有错误图标。 错误图标未填充以表示“捕获”(型)。
错误边界事件与错误启动事件具有相同的属性。
<serviceTask id="Activity_1" name="Check error"
. . .
</serviceTask>
<boundaryEvent id="error-boundary-event" name="Error"
attachedToRef="Activity_1">
<errorEventDefinition id="ErrorEventDefinition_1"
errorRef="failure" />
</boundaryEvent>
错误结束事件
当流程执行到 错误结束事件(error end event) 时,当前流程路径将结束并抛出错误。
错误结束事件在图中显示为典型的结束事件(粗线的圆圈),内部有错误图标。 错误图标已填充表示“抛出”。
错误结束事件有一个特殊的属性 — Error,该属性非必需。 如果设置,则需要引用某些已存在的 错误定义。
错误结束事件在 XML 以结束事件表示,带 errorEventDefinition
子元素。
<endEvent id="Event_01" name="Error">
<incoming>Flow_1i3jqxp</incoming>
<errorEventDefinition id="ErrorEventDefinition_11xfxfw" (1)
errorRef="failure" /> (2)
</endEvent>
1 | — 声明错误事件 |
2 | — 设置错误定义,可以省略。 |
使用错误结束事件
错误结束事件(error end event) 是一个抛出事件,必须有对应的捕获事件。 如果错误结束事件属于某个子流程,捕获事件可以是错误边界事件。
捕获事件也可以是错误启动事件,如果有子流程的话。
使用无捕获错误事件的错误结束事件会导致运行时错误。 |
参阅 错误处理 部分。
补偿事件
补偿事件可以撤销流程中已成功完成的步骤, 以修复这些步骤的结果。
补偿事件的类型:
补偿中间抛出事件
补偿中间抛出事件(Compensation intermediate throwing event) 可用于触发补偿。
触发补偿
可以为指定的活动触发补偿,也可以为补偿事件依附的范围触发补偿。 补偿行为是通过执行与活动关联的补偿处理逻辑来完成。
当为活动抛出补偿时, 关联的补偿处理逻辑的执行次数与活动成功完成需要执行的次数相同。
如果为当前范围抛出补偿,则当前范围内的所有活动都会得到补偿。 包括并发分支上的活动。
补偿按层次结构触发: 如果要补偿的活动是子流程, 则为子流程中包含的所有活动触发补偿。 如果子流程具有嵌套的内部活动,则以递归方式抛出补偿。 但是,补偿不会传播到流程的“上层”: 即,如果是在子流程内触发的补偿,则不会传播到子流程范围之外的活动。 BPMN 规范指出,补偿是针对“同一级别的子流程”的活动触发的。
在 Flowable 中,补偿按流程执行的相反顺序执行。也就是说,最后完成的那个活动,将首先得到补偿,依此类推。
中间抛出补偿事件可用于补偿成功完成的事务子流程。
中间补偿抛出事件在图中展示为典型的中间事件(双线圆圈), 内部有补偿图标。 补偿图标已填充以表示“抛出”。
补偿抛出事件无特定属性。
补偿中间事件在 XML 中定义为中间抛出事件。此时需使用 compensateEventDefinition
子元素。
<intermediateThrowEvent id="throwCompensation">
<compensateEventDefinition id="CompensateEventDefinition_0s3nsqo" />
</intermediateThrowEvent>
此外,可选参数 activityRef
可用于触发特定范围或活动的补偿:
<intermediateThrowEvent id="throwCompensation">
<compensateEventDefinition id="CompensateEventDefinition_0s3nsqo"
activityRef="bookHotel" /> (1)
</intermediateThrowEvent>
1 | — 为特定活动触发补偿。 |
示例
如果在包含子流程的范围内触发了补偿,并且该子流程包含带补偿处理逻辑的活动, 则当抛出补偿时,只有子流程已成功完成,补偿才会传播到子流程中。
如果在子流程内部的某些活动已经完成并具有依附的补偿处理逻辑, 则子流程本身尚未完成时,不会执行这些处理逻辑。 参考下面示例:
在这个流程中,我们有两个并发分支:一个是嵌入式子流程,另一个是 “charge credit card” 活动。 假设两个分支都已启动,并且第一个分支正在等待用户完成 “review bookings” 任务。
第二个分支在执行 “charge credit card” 活动,在这个活动中发生错误 触发 “cancel booking” 事件以启动补偿。
此时,第一分支的子流程尚未完成,也就是说补偿没有传递到这个分支。 因此,不会执行 “cancel hotel booking” 补偿处理逻辑。
如果用户任务(以及嵌入子流程)在 “cancel booking” 操作之前完成, 补偿将传播到嵌入子流程中。
补偿边界事件
补偿边界事件(Compensation boundary event) 可用于将补偿处理逻辑依附至活动。
补偿边界事件必须有方向地关联单个补偿处理逻辑。
补偿边界事件具有与其他边界事件不同的激活策略。 其他边界事件(比如信号边界事件)在所依附的活动启动时被激活。 活动完成后,这些边界事件将被禁用,并取消相应的事件订阅。
而补偿边界事件则不同。 补偿边界事件在所依附的活动成功完成时激活。 此时,才创建补偿事件的相应订阅。 当触发补偿事件或对应的流程实例结束时,删除订阅。 因此,其逻辑如下:
-
当补偿触发时,与补偿边界事件关联的补偿处理逻辑会被调用,其调用次数与所依附的活动成功完成的次数相同。
-
如果一个补偿边界事件所依附的活动具有 多实例 特性,则会为每个实例创建一个补偿事件的订阅。
-
如果流程实例结束,会取消所有补偿事件的订阅。
嵌入式子流程不支持补偿边界事件。 |
补偿边界事件在图中显示为边界上的典型中间事件(双线圆圈),内部有补偿图标。补偿图标未填充以表示“捕获”。 除了补偿边界事件之外,下图显示了补偿处理逻辑与边界事件的单向关联。
补偿边界事件必须有向关联至单一补偿处理逻辑。
补偿边界事件在 XML 中定义为典型的边界事件:
<boundaryEvent id="Event_1" attachedToRef="Activity_1">
<compensateEventDefinition id="CompensateEventDefinition_05" />
</boundaryEvent>
. . .
<association id="Association_02zt79e"
associationDirection="One"
sourceRef="Event_1" targetRef="Activity_1" />
取消事件
取消事件仅在 事务子流程 中使用。 只有在事务子流程中,才可以从工具箱找到取消事件。
有两种类型的取消事件:
取消结束事件
取消结束事件(cancel end event) 只能与 BPMN 事务子流程 结合使用。当流程到达取消结束事件事件时,将抛出一个取消事件,该事件必须由取消边界事件捕获。然后,取消边界事件取消当前事务并触发补偿。
取消结束事件在图中显示为典型的结束事件(粗线圆圈),内部带有取消图标。取消图标完全为黑色,表示“抛出”。
取消结束事件在 XML 中表示为带 cancelEventDefinition
子元素的结束事件。
<endEvent id="myCancelEndEvent">
<cancelEventDefinition />
</endEvent>
取消边界事件
一个依附在事务子流程边界的 中间捕获取消事件(intermediate catching cancel event),简称 取消边界事件(boundary cancel event),当事务被取消时触发。
当触发取消边界事件时,首先会中断当前范围内的所有激活的执行分支。 然后,为事务范围内的所有激活的补偿边界事件启动补偿。
补偿是同步执行的,边界事件在离开事务之前会等待补偿完成。 当补偿完成时,使用取消边界事件的出口顺序流,离开事务子流程。
|
取消边界事件,用内部有一个取消图标的标准中间事件(双线圆圈)表示。取消图标是白色的(未填充),代表“捕获”。
取消边界事件的 XML 定义为一个典型的边界事件:
<boundaryEvent id="boundary" attachedToRef="transaction" >
<cancelEventDefinition />
</boundaryEvent>
由于取消边界事件是中断的,因此 cancelActivity
属性不是必需。
终止结束事件
当到达 终止结束事件(terminate end event) 时,当前的流程实例或子流程会被终止。
终止结束事件,用内部有一个全黑圆的标准结束事件(粗圆圈)表示。
终止结束事件在 XML 中为结束事件,带有 terminateEventDefinition
子元素。
<endEvent id="myEndEvent >
<terminateEventDefinition flowable:terminateAll="true">
</terminateEventDefinition>
</endEvent>
|
BPMN 事件覆盖度
在 Jmix BPM 中,并非支持所有的 BPMN 2.0 事件,不支持的事件以粉红色标记。 也就是说,虽然 BPMN 2.0 标准定义了广泛的事件, 但 Jmix BPM 集成了 Flowable 引擎,该引擎可能没有实现标准中所有的事件类型。
不支持的事件
-
消息抛出的中间和结束事件
-
信号抛出结束事件
-
补偿结束事件
-
升级事件(Escalation events)的全部类型
-
条件事件(Conditional events)的全部类型
-
连接事件(Link events)的全部类型
从第三方设计工具导入 BPMN 模型时要小心: 不支持的事件虽然可以显示在关系图上,但可能会在运行时发生错误。 |
不支持事件的解决方法
在本节中,我们介绍如何为 Jmix BPM 中不支持的元素实现所需流程逻辑。
消息抛出事件
首先,在建模中,尽量使用 信号 代替 消息。多数情况下,这两种事件可以互换。
然后,使用服务任务调用 API。
服务任务可以实现为 Spring bean,示例:
@Component(value = "smpl_MyService")
public class MyService {
@Autowired
private RuntimeService runtimeService;
public void sendMessage(String messageName, String executionId) {
runtimeService.messageEventReceived(messageName, executionId);
}
}
升级事件
在某些情况下,可以使用 BPMN 错误事件替代升级事件(escalation events)。 BPMN 中的错误事件用于处理流程执行期间发生的错误或异常。 可用于对流程中的错误处理和恢复机制进行建模。
另一方面,升级事件用于将问题上报到组织或流程层次结构中的更高级别。 当问题无法在当前级别解决并且需要升级以采取进一步措施时,通常会使用升级事件。
因此,升级事件在技术上非常接近错误事件。