中间捕获事件
本文中内容和案例节选自贺波老师的《深入Activiti流程引擎:核心原理与高阶实战》一书。这本书中的内容更为全面、详尽,对系统学习和深入掌握Activiti/Flowable的用法非常有帮助,推荐给大家。
QQ交流群1:
633168411已满
QQ交流群2: 582010059
在开始事件和结束事件之间发生的事件都称为中间事件,BPMN2.0 规范中所指的中间事件也 包括边界事件,但本节中所介绍的中间事件是指可以单独作为流程元素的事件,即直接出现在流程 连线上的中间事件,这类事件即可以捕获触发器又可以抛出结果。中间事件会影响流程的流转路线, 但不会启动或直接终止流程的执行。
Flowable中间捕获事件分一下几类:
- 定时器中间铺获事件
- 信号中间捕获事件
- 消息中间捕获事件
- 条件中间捕获事件
- 注册中间捕获事件
- 参数监听中间捕获事件
定时器中间铺获事件
定义
定时中间事件指在流程中将一个定时器作为独立的节点来运行。定时中间事件是一个 Catching 事件,当流程到达定时中间事件节点的时候,就会启动一个定时器,一直等待被触发,只有当时间 到达了定时器触发,然后流程离开定时中间事件继续向下走。
图形标记
定时器中间事件显示成标准中间捕获事件,内部是一个定时器小图标,如图所示
XML内容
定时器中间事件定义为标准中间捕获事件,指定类型的子元素为 timerEventDefinition 元素:
<process id="timerIntermediateCatchingEventProcess">
<intermediateCatchEvent id="timer">
<timerEventDefinition>
<timeDuration>PT5M</timeDuration>
</timerEventDefinition>
</intermediateCatchEvent>
</process>
以上 xml 代码片段中加粗的部分定义了定时中间捕获事件 intermediateCatchEvent,它通过 timerEventDefinition 子元素设置定时器。 当包含定时中间事件的流程新版本被发布后,处于活动的旧版本的定时器会继续执行,直至旧 版本不再有新的实例产生。
定时器中间事件分三类: timeDate,timeDuration,timeCycle。
1. timeDate:设置在指定时间触发。
<startEvent id="timerStart">
<timerEventDefinition>
<timeDate>2023-07-14T12:12:14</timeDate>
</timerEventDefinition>
</startEvent>
timeDate 是使用 ISO 8601 格式指定一个确定的时间来触发事件,以上配置表示流程会在 2023-07-14T12:12:14 启动起来。
注意:ISO8601 时间格式,如果要加时间需要前面加 T。
2、timeDuration:置指定一个时间段之后执行。
<startEvent id="timerStart">
<timerEventDefinition>
<timeDuration>PT1S</timeDuration>
</timerEventDefinition>
</startEvent>
timeDuration 是指定定时器之前要等待多长时间。S 表示秒,M 表示分,D 表示天;P 表示时 间段,T 表示精确到时间的时间段。
注意:时间格式依然为 ISO 8601 格式,一年两个月三天四小时五分六秒内,可以写成 P1Y2M3DT4H5M6S,P 是开始标记,T 是时间和日期分割标记,没有日期只有时间 T 是不能省去 的,比如 1 小时执行一次应该写成 PT1H。
3、通过 timeCycle 设置指定定时器的运行周期
<startEvent id="timerStart">
<timerEventDefinition>
<timeCycle>R2/${EndDate}/PT1M</timeCycle>
</timerEventDefinition>
</startEvent>
timeCycle 指定重复执行的间隔,可以用来定期启动流程实例。timeCycle 的设置目前有两种方 式:ISO 8601 和 Cron 表达式。R 表示需要执行的次数,R2/PT1M 表示执行两次,每次间隔 1 分钟。 其中 endDate 是可选的配置,定时器将会在指定的时间停止工作。
注意:使用定时器开始事件需要启动 JobExecutor
操作界面
演示demo
信号中间捕获事件
定义
信号中间事件分为 Catching 事件和 Throwing 事件,即信号中间捕获事件(Signal Intermediate Catch Event)和信号中间抛出事件(Signal Intermediate Throwing Event)。 当流程执行到信号中间捕获事件时就会中断在这里,一直等待被触发,直接到该事件接收到相 应的信号后,流程沿后继路线继续执行。信号事件是全局的,与其他事件(比如错误事件)不同, 信号不会在捕获之后被消费。如果存在多个引用了相同信号的事件被激活,即使它们不在同一个流 程实例中,那么当接收到信号时,这些事件被一并被触发。 当流程执行到达信号抛出事件时,流程引擎会直接抛出信号,其他引用了与其相同的信号捕获 事件会被触发,信号发出后事件结束,流程沿后继路线继续执行。其抛出的信号可以被信号开始事 件(Signal Start Event)、信号中间捕获事件(Signal Intermediate Catch Event)、信号边界事件(Signal Boundary Interrputing Event)订阅处理。
图形标记
信号中间捕获事件显示为一个普通的中间事件(圆圈套圆圈),内部有一个信号小图标。信号 小图标是白色的(无填充),表示捕获语义,如图所示:
XML内容
信号中间捕获事件 信号中间事件定义为普通的中间捕获事件。对应类型的子元素是 signalEventDefinition 元素:
<signal id="theSignal" name="The Signal" />
<process id="signalIntermediateCatchEventProcess">
<intermediateCatchEvent id="signal">
<signalEventDefinition signalRef="theSignal" />
</intermediateCatchEvent>
</process>
在以上 xml 代码片段中,加粗的部分分别定义了 signal 和 intermediateCatchEvent,信号 signal 的 id 属性值为 theSignal,信号中间捕获事件 intermediateCatchEvent 中的 signalEventDefinition 子元 素通过设置 signalRef 为 theSignal 引用了该信号。 信号中间捕获事件触发的方式有两种: 1、流程中抛出信号事件(Signal Intermediate Throwing Event)、信号结束事件(Signal End Event)发出的信号。 2、通过 API 触发,在 Flowable 中通过调用 runtimeService.signalEventReceived 系列方法发出一 个指定的信号,参考信号边界事件中的介绍。
使用示例
下面看一个费用报销的例子,同时利用了信号中间捕获事件和信号中间抛出事件。发起流程提 交报销申请后,后续有三个并行分支,一个分支会进业务主管确认,另外两个分支都分别到达信号 中间捕获事件,这两个事件会一直等待信号,当业务主管确认完成后到达后续的信号中间抛出事件 发出信号,从而触发两个信号中间捕获事件使另两个分支分别推进至部门主管审批节点和财务主管 节点。该流程的流程图如图所示:
示例XML内容
<signal id="theSignal" name="信号" flowable:scope="global" />
<process id="SignalIntermediateCatchingEvent" name="中间信号捕获和抛出事件测试" isExecutable="true">
<startEvent id="aed01518ca37e4c5b820abada9e7edc17" />
<userTask id="aded12dde53c444b9b7a8a99d265b7b2d" name="付款申请">
<extensionElements>
<flowable:formData />
<flowable:assigneeType>static</flowable:assigneeType>
</extensionElements>
</userTask>
<sequenceFlow id="a273f2adba0ac45f1acbff9d4d821de61" sourceRef="aed01518ca37e4c5b820abada9e7edc17" targetRef="aded12dde53c444b9b7a8a99d265b7b2d" />
<sequenceFlow id="Flow_1sfqb6b" sourceRef="aded12dde53c444b9b7a8a99d265b7b2d" targetRef="Gateway_0ef4904" />
<parallelGateway id="Gateway_0ef4904" />
<sequenceFlow id="Flow_0ituttz" sourceRef="Gateway_0ef4904" targetRef="Event_0374yen" />
<intermediateCatchEvent id="Event_0374yen">
<signalEventDefinition signalRef="theSignal" />
</intermediateCatchEvent>
<userTask id="Activity_0hbesbz" name="部门主管审批">
<extensionElements>
<flowable:formData />
<flowable:assigneeType>static</flowable:assigneeType>
</extensionElements>
</userTask>
<sequenceFlow id="Flow_0ypus8y" sourceRef="Event_0374yen" targetRef="Activity_0hbesbz" />
<sequenceFlow id="Flow_00epdwi" sourceRef="Gateway_0ef4904" targetRef="Event_1mpvo0c" />
<userTask id="Activity_1mf7r17" name="财务主管审批">
<extensionElements>
<flowable:assigneeType>static</flowable:assigneeType>
<flowable:formData />
</extensionElements>
</userTask>
<sequenceFlow id="Flow_1eni8ec" sourceRef="Event_1mpvo0c" targetRef="Activity_1mf7r17" />
<sequenceFlow id="Flow_0vg3vj4" sourceRef="Gateway_0ef4904" targetRef="Activity_18sdoo1" />
<userTask id="Activity_18sdoo1" name="业务主管确认">
<extensionElements>
<flowable:formData />
<flowable:assigneeType>static</flowable:assigneeType>
</extensionElements>
</userTask>
<sequenceFlow id="Flow_0yffw90" sourceRef="Activity_0hbesbz" targetRef="Gateway_1ss0n3z" />
<parallelGateway id="Gateway_1ss0n3z" />
<sequenceFlow id="Flow_1jg3xvu" sourceRef="Activity_1mf7r17" targetRef="Gateway_1ss0n3z" />
<userTask id="Activity_153vvhu" name="财务打款">
<extensionElements>
<flowable:assigneeType>static</flowable:assigneeType>
<flowable:formData />
</extensionElements>
</userTask>
<sequenceFlow id="Flow_15cmtof" sourceRef="Gateway_1ss0n3z" targetRef="Activity_153vvhu" />
<endEvent id="Event_1a1foyk" />
<sequenceFlow id="Flow_0aj4r74" sourceRef="Activity_153vvhu" targetRef="Event_1a1foyk" />
<intermediateCatchEvent id="Event_1mpvo0c">
<signalEventDefinition signalRef="theSignal" />
</intermediateCatchEvent>
<sequenceFlow id="Flow_0un80br" sourceRef="Activity_18sdoo1" targetRef="Event_02cn8n4" />
<intermediateThrowEvent id="Event_02cn8n4">
<signalEventDefinition signalRef="theSignal" />
</intermediateThrowEvent>
<sequenceFlow id="Flow_0no5qu7" sourceRef="Event_02cn8n4" targetRef="Gateway_1ss0n3z" />
</process>
界面操作
演示demo
消息中间捕获事件
定义
消息中间事件指在流程中将一个消息事件作为独立的节点来运行。它是一种捕获事件,当流程 执行到消息中间事件时就会中断在这里,一直等待被触发,直接到该事件接收到相应的消息后,流 程沿后继路线继续执行。消息事件是一种引用了消息定义的事件,与信号事件不同的是,消息只能 指向一个接收对象,不能像信号事件一样全局广播。
图形标记
消息中间事件显示为普通中间事件(圆圈套圆圈),内部是一个消息小图标。消息图标是白色 的(无填充),表示捕获语义,如图所示:
XML 内容
消息中间事件定义为标准中间捕获事件,指定类型的子元素是 messageEventDefinition 元素:
<message id="theMessage" name="newMessageName" />
<process id="messageIntermediateCatchEventProcess">
<intermediateCatchEvent id="message">
<messageEventDefinition messageRef="theMessage" />
</intermediateCatchEvent>
</process>
在以上 xml 代码片段中,加粗的部分分别定义了 message 和 intermediateCatchEvent,消息 message 的 id 属 性 值 为 theMessage , 消 息 中 间 事 件 intermediateCatchEvent 中 的 messageEventDefinition 子元素通过设置 messageRef 为 theMessage 引用了该消息。
使用示例
消息中间事件的触发方式有两种
- 流程中抛出消息事件(Message Intermediate Throwing Event)、消息抛出事件、消息结束事件(Message EndEvent)发出的内部消息。
- 通过 API 触发,在 Flowable 中通过调用 runtimeService.messageEventReceived 系列方法发出一个指定的信号。runtimeService.messageEventReceived 系列方法下表格所示:
API 方法 | 含义 |
---|---|
runtimeService.messageEventReceived(String messageName, String executionId) | 把消息发送给指定执行流 |
runtimeService.messageEventReceived(String messageName, String executionId, HashMap<String, Object> processVariables) | 把消息发送给指定执行流,同时给更新流程变量 |