启动事件
本文中内容和案例节选自贺波老师的《深入Activiti流程引擎:核心原理与高阶实战》一书。这本书中的内容更为全面、详尽,对系统学习和深入掌握Activiti/Flowable的用法非常有帮助,推荐给大家。
QQ交流群1:
633168411已满
QQ交流群2: 582010059
开始事件表示流程的开始,定义流程如何启动,如流程在接收事件时启动,在指定时间启动,在接收信号,消息启动等等
在Flowable中,开始事件可以划分为以下9种类型:
- 空开始事件(none start event)
- 定时器开始事件(timer start event)
- 信号开始事件(signal start event)
- 消息开始事件(message start event)
- 错误开始事件(error start event)
- 条件开始事件(conditional start event)
- 参数监听开始事件(variableListener start event)
- 升级开始事件 (escalation start event)
- 注册表开始事件(registry start event)
在图标表示上,不同类型的开始事件以内部的白色小图标区分。在XML表示中,这些类型是通过声明不同的子元素来区分的。所以开始事件都属于捕获事件,从概念上讲,这些时间都会一直等待,需要由具体的动作或事件触发。
空启动事件
定义
空开始事件意味着没有指定启动流程实例的触发条件。它是最常见的一种开始事件,一般需要人工启动,或通过API触发
图形标记
XML内容
<startEvent id="aba1958401251475ca53b3f5d152fde3b" name="空开始"/>
使用示例
空开始事件无需指定触发条件,可以由API触发 runtimeService.startProcessInstanceBy
开头的各种方法发起流程实例。
空启动事件的自定义扩展
- formKey: 引用表单定义,用户需要在启动新流程实例时填写该表单。
<startEvent id="request" flowable:formKey="request" />
演示demo
定时器开始事件
定义
定时器启动事件(timer start event)用于在指定的时间启动一个流程,或者在一定周期内循环启动多次流程,如在2023年8月1日10时整发起年度目标审核流程, 或每月1日0时开始启动财务结算处理流程。当满足设定的时间条件时,定时器开始事件被触发,从而启动流程。
注意 使用定时器开始事件需要开启flowable的作业执行器
configuration.setAsyncExecutorActivate(true);
图形标记
定时开始事件显示为了一个圆圈,内部是一个小时钟
XML内容
定时开始事件的 XML 内容是普通开始事件的声明,包含一个定时器事件子元素。
<startEvent id="timerStart" >
<timerEventDefinition>
<timeDate>2023-07-14T12:12:14</timeDate>
</timerEventDefinition>
</startEvent>
定义定时器的标签是<timerEventDefinition>...</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
使用示例
注意
1、子流程中不能使用定时器启动事件。定时器是从流程部署开始计时,不需要去启动流程。
2、当定时启动任务已经超过运行时间,再次去部署流程时,就会把它当成一个普通的空启动事件来处理。部署新的流程后,旧版本的流程中的定时事件就会被移除。
演示demo
信号开始事件
定义
信号开始事件在接收到特定的信号后被触发,启动一个流程实例。如果多个流程含有相同信号名称的信号开始事件,那么它们都会被启动。
图形标记
消息开始事件是一个圆圈,中间是一个消息事件图标。图标是白色未填充的,来表示捕获(接收)行为。
XML内容
信号开始事件的 XML 内容是在普通开始事件申请中包含一个 signalEventDefinition 子元素
<!-- 信号定义 -->
<signal id="theSignal" name="The Signal" />
<process id="signalStartProcess">
<startEvent id="signalStart" >
<!-- 定义为开始信号事件 -->
<signalEventDefinition signalRef="theSignal" />
</startEvent>
</process>
以上 xml 代码片段中加粗的部分分别定义了 signal、startEvent,信号 signal 的 id 属性值为theSignal,信号开始事件 startEvent 中的 signalEventDefinition 子元素通过设置 signalRef 为 theSignal 引用了该信号。
界面操作
定义信号
选择信号
使用示例
信号启动事件的触发方式通常有以下几种:
1、流程中的抛出信号事件(Signal Intermediate Throwing Event)、信号结束事件(Signal End Event)发出信号,所有拥有相同名字信号启动事件的流程定义都会被启动。
2、通过 API(runtimeService.signalEventReceivedXXX 方法)抛出一个信号,所有拥有相同名字 信号启动事件的流程定义都会被启动。需要为 API 传递的 signalName,是由 signal 元素的 name 属 性决定的名字。signal 元素被 signalEventDefinition 的 signalRef 属性所引用。
3、作为普通开始事件,启动流程。
注意:信号定义的作用域分流程实例有效和全局有效
<signal id="startSigon" name="开始信号" flowable:scope="processInstance" />
<signal id="startSigon" name="开始信号" flowable:scope="global" />
演示demo
消息开始事件
定义
在 BPMN2.0 规范中,消息表示的是流程参与者的沟通信息对象,在一般流程中,流程的各个角色的沟通信息均有可能导致流程的开始。流程开始事件,可以理解为另外一种启动流程的方式或者途径,使用该开始事件,当达到“接收消息”的条件后启动流程。
图形标记
消息开始事件是一个圆圈,中间是一个消息事件图标,图标是白色未填充小信封,来表示捕获(接收)行为。
XML内容
<!-- 消息定义 -->
<message id="theMessage" name="newMessageName" />
<process id="messageStartEventProcess">
<startEvent id="messageStart" >
<!-- messageEventDefinition -->
<messageEventDefinition messageRef="theMessage" />
</startEvent>
<!-- -->
</process>
在以上xml代码片段中,首先定义了一个名称为theMessage的消息 ,消息值为newMessageName,然后在开始事件中使用 messageEventDefinition 元素引用该 message,从而构成了一个消息开始事件。
界面操作
定义消息
选择消息
使用示例
在发布包含一个或多个消息开始事件的流程定义时,需要考虑下面的条件:
1、流程的消息名称必须是唯一的,一个流程定义不得包含多个同名的启动消息。如果两个或以上消息开始事件应用了相同的事件,或两个或以上消息事件引用的消息名称相同,Flowable 会在发布流程定义时抛出异常。
2、消息开始事件的名称在所有已发布的流程定义中不能重复。如果一个或多个消息开始事件引用了相同名称的消息,而这个消息开始事件已经部署到不同的流程定义中,Flowable 就会在发布时抛出一个异常。
3、发布新版流程定义时,会取消上一版本的消息订阅。
4、只有顶级流程(toplevel process)才支持消息开始事件,内嵌子流程不支持消息事件。如果流程被调用环节(callActivity)启动,消息开始事件只支持以下两种情况:在消息开始事件以外,还有一个单独的空开始事件;流程只有一个消息开始事件,没有空开始事件。
通过消息开始事件启动流程实例有三种方法: ProcessInstance startProcessInstanceByMessage(String messageName);
ProcessInstance startProcessInstanceByMessage(String messageName, Map<String, Object>processVariables);
ProcessInstance startProcessInstanceByMessage(String messageName, String businessKey, Map<String, Object< processVariables);
其中,messageName 是 messageEventDefinition 的 messageRef 属性引用的 message 元素的 name属性。
注意:使用这些 API 有以下几个注意事项:
1、如果流程定义中有多个消息开始事件,那么 runtimeService.startProcessInstanceByMessage(...)会选择对应的开始事件。
2、如果流程定义中既有消息开始事件又有一个空开始事件,那么runtimeService.startProcessInstanceByKey(...)和 runtimeService.startProcessInstanceById(...)会使用空开始事件启动流程实例。
3、如果流程定义中有多个消息开始事件,而没有空开始事件,那么runtimeService.startProcessInstanceByKey(...)和 runtimeService.startProcessInstanceById(...)会抛出异常。
4、如果流程定义只有一个消息开始事件, runtimeService.startProcessInstanceByKey(...) 和runtimeService.startProcessInstanceById(...)会使用这个消息开始事件启动流程实例。
5、如果被启动的流程是一个调用子流程(callActivity)并且有多个开始事件,那么该流程定义中除了含有消息开始事件外,还需要有一个无指定开始是;或者该调用子流程中只有一个消息开始事件。
演示demo
错误开始事件
定义
错误开始事件可以触发一个异常子流程,它总是在另外一个流程在异常结束的时候触发。BPMN2.0 规定了错误开始事件只能使用在事件子流程(Event Sub-Process)中,该该事件不能 使用在其它流程中,包括最高级流程(Top-Level Process)、嵌套子流程(Sub-Process)和调用子流程(Call Activity)。
注意,BPMN 错误与 Java 异常不是一回事,BPMN 错误事件是建模业务异常(business exceptions)的方式,二者没有直接关联。
图形图标
错误开始事件是一个圆圈,包含一个错误事件标记。标记是白色未填充的,来表示捕获(接收)行为。
XML内容
<error id="errorStart" errorCode="501" />
<process id="errorStartEventProcess">
<startEvent id="errorStartEvent" >
<errorEventDefinition errorRef="theError" />
</startEvent>
</process>
在以上 xml 代码片段中,首先定义了一个 id 属性值为 theError 的错误,然后在开始事件中使用 errorEventDefinition 元素引用该 error,从而构成了一个错误开始事件。
注意:错误开始事件不能独立存在,必须是其他事件的子流程。
界面操作
错误定义
错误选择
使用示例
两种方式触发错误开始事件
1、错误结束事件抛出错误(注:错误结束事件也选择的是同一个错误定义)
2、自动执行主流程的服务任务抛出错误,接下来子流程的错误开始事件捕获到错误后执行。
@Slf4j
public class AutomaticReviewService implements JavaDelegate {
@Override
public void execute(DelegateExecution execution) {
String healthCodeStatus = (String) execution.getVariable("healthCodeStatus");
if (!"green".equals(healthCodeStatus)) {
String errorCode = "500";
log.error("健康码异常,抛出BPMN错误,errorCode为:{}", errorCode);
throw new BpmnError(errorCode);
}
}
}
演示demo
条件开始事件
定义
定义了一个事件,该事件在给定条件被求值为true时被触发,作为事件子流程的起始事件。
图形图标
条件开始事件是一个圆圈,包含一个条件事件标记。
XML内容
<bpmn:startEvent id="Event_1gjhevt">
<bpmn:conditionalEventDefinition id="ConditionalEventDefinition_1j8ag66">
<bpmn:condition xsi:type="bpmn:tFormalExpression">${name=='lwj'}</bpmn:condition>
</bpmn:conditionalEventDefinition>
</bpmn:startEvent>
界面操作
参数监听开始事件
定义
定义了一个事件,该事件在指定参数发生变化而触发的,作为事件子流程的起始事件。
图形图标
条件开始事件是一个圆圈,包含一个五边形标记。
variableChangeType类型有四种类型:all、create、update、createupdate 1、all:参数只要发生变化被触发 2、create:参数创建被触发 3、update:参数更新被触发 4、createupdate:参数创建或更新被触发
XML内容
<bpmn:startEvent id="Event_1gyqk9p" flowable:initiator="initiator">
<bpmn:extensionElements>
<flowable:variableListenerEventDefinition variableName="name" variableChangeType="create" />
</bpmn:extensionElements>
</bpmn:startEvent>