在流程运转的过程中,流程引擎会发出很多不同的事件,前面的文章,我们通过执行监听器和任务监听器捕获到对应事件并进行处理。除了这两个监听器以外,activiti从5.15版开始加入了全局事件监听器,这样很多重复的监听器就不需要在每个活动上去绑定。添加全局监听器有几种方式,包括 通过流程引擎文件方式进行配置、通过流程文档进行配置、动态添加全局事件监听器等方式,下面分别展示这几种方法:
通过流程引擎文件方式进行配置
首先创建全局监听器MyEventListener.java,全局监听器需要实现ActivitiEventListener接口
public class MyEventListener implements ActivitiEventListener{
public void onEvent(ActivitiEvent event) {
System.out.println("eventName:" + event.getType().name());
}
public boolean isFailOnException() {
return false;
}
}
这个监听器,我们只让它把所触发的事件都输出。
接着我们通过流程引擎配置文件进行配置,新建activitiEventListener.cfg.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="processEngineConfiguration"
class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
<property name="jdbcUrl"
value="jdbc:mysql://localhost:3306/db_activiti?useUnicode=true&&characterEncoding=utf8&serverTimezone=UTC" />
<property name="jdbcDriver" value="com.mysql.jdbc.Driver" />
<property name="jdbcUsername" value="root" />
<property name="jdbcPassword" value="" />
<property name="databaseSchemaUpdate" value="true" />
<property name="eventListeners">
<list>
<bean class="eventListener.MyEventListener"></bean>
</list>
</property>
</bean>
</beans>
17-21行新增自定义的全局事件监听器列表。bpmn图我们使用《activiti学习(二)——activiti流程的部署》的firstBPM.bpmn。然后我们通过初始化流程引擎、部署、启动流程等操作,可以观察全局事件监听器捕捉到的事件类型。
通过流程文档进行配置
在这个例子中,我们使用《activiti学习(一)——activiti流程引擎的配置与初始化操作》的activiti.cfg.xml的配置文件,然后把全局事件监听器添加到流程文档的process元素内,创建eventListenerBPM.bpmn:
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI"
typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath"
targetNamespace="http://www.activiti.org/test">
<process id="eventListenerBpmProcess" name="event listener process" isExecutable="true">
<extensionElements>
<activiti:eventListener class="eventListener.MyEventListener" />
</extensionElements>
<startEvent id="startevent1" name="Start"></startEvent>
<userTask id="usertask1" name="myTask1"></userTask>
<endEvent id="endevent1" name="End"></endEvent>
<sequenceFlow id="flow1" sourceRef="startevent1"
targetRef="usertask1"></sequenceFlow>
<sequenceFlow id="flow2" sourceRef="usertask1" targetRef="endevent1"></sequenceFlow>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_eventListenerBpmProcess">
<bpmndi:BPMNPlane bpmnElement="eventListenerBpmProcess"
id="BPMNPlane_eventListenerBpmProcess">
<bpmndi:BPMNShape bpmnElement="startevent1"
id="BPMNShape_startevent1">
<omgdc:Bounds height="41.0" width="35.0" x="505.0" y="40.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="usertask1" id="BPMNShape_usertask1">
<omgdc:Bounds height="55.0" width="105.0" x="470.0" y="150.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1">
<omgdc:Bounds height="35.0" width="35.0" x="505.0" y="240.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1">
<omgdi:waypoint x="522.0" y="81.0"></omgdi:waypoint>
<omgdi:waypoint x="522.0" y="150.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2">
<omgdi:waypoint x="522.0" y="205.0"></omgdi:waypoint>
<omgdi:waypoint x="522.0" y="240.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
9-11行通过扩展元素为process添加全局事件监听器。
动态添加全局事件监听器
这种方式是利用RuntimeService的addEventListener方法进行监听器的注册。我们使用最简单的firstBPM.bpmn和activiti.cfg.xml,在初始化流程引擎时,我们通过RuntimeService进行添加,之后再部署和执行流程,可观察效果:
public void getFromProcessEngineConfiguration() {
ProcessEngineConfiguration pec = ProcessEngineConfiguration
.createProcessEngineConfigurationFromResource("activiti.cfg.xml");
pe = pec.buildProcessEngine();
RuntimeService rs = pec.getRuntimeService();
rs.addEventListener(new MyEventListener());
}
第5行获取RuntimeService,第6行通过addEventListener添加新的全局事件监听器。注意必须在流程引擎创建后才可以通过getRuntimeService获取。
事件类型
activiti事件类型非常多,建议大家到其官网查阅:https://www.activiti.org/5.x/userguide/#eventDispatcherEventTypes
关闭事件转发器
事件转发器默认打开,如果不想使用全局事件监听器,可以关闭事件转发器,在cfg.xml中配置:
<bean id="processEngineConfiguration"
class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
......
<!-- 事件转发器,属性默认为true -->
<property name="enableEventDispatcher" value="false"></property>
</bean>
关闭事件转发器并不会影响任务监听器和执行监听器的监听,只会影响全局事件监听器。
日志监听器
日志监听器本质上也是一个全局事件监听器,主要记录流程实例和任务节点相关的事件和信息,并不会把所有事件都记录。记录的数据在act_evt_log表中。开启的方法是在cfg.xml中配置enableDatabaseEventLogging属性为true
<bean id="processEngineConfiguration"
class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
......
<property name="enableDatabaseEventLogging" value="true"></property>
</bean>
具体类型事件监听器
除了eventListeners之外,activiti还可以让用户专门监听指定的事件类型 。看下具体例子,监听器沿用上面的MyEventListener.java,重点是在cfg.xml里对processEngineConfiguration的typedEventListeners属性进行设置:
<bean id="processEngineConfiguration"
class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
......省略
<property name="typedEventListeners">
<map>
<entry key="PROCESS_STARTED">
<list>
<bean class="eventListener.MyEventListener"></bean>
</list>
</entry>
</map>
</property>
</bean>
上面我们设定这个监听器只监听“PROCESS_STARTED”事件,即流程开始事件。这样只有当触发PROCESS_STARTED事件才会被改监听器捕获。
全局事件监听器原理
全局事件监听器原理的源头——事件转发器。所有事件都是通过事件转发器调用已注册的监听器的onEvent方法实现的。首先我们看看事件转发器的初始化,在ProcessEngineConfigurationImpl.java
protected void init() {
......省略
initFailedJobCommandFactory();
initEventDispatcher();
initProcessValidator();
......
}
protected void initEventDispatcher() {
if(this.eventDispatcher == null) {
this.eventDispatcher = new ActivitiEventDispatcherImpl();
}
this.eventDispatcher.setEnabled(enableEventDispatcher);
if(eventListeners != null) {
for(ActivitiEventListener listenerToAdd : eventListeners) {
this.eventDispatcher.addEventListener(listenerToAdd);
}
}
if(typedEventListeners != null) {
for(Entry<String, List<ActivitiEventListener>> listenersToAdd : typedEventListeners.entrySet()) {
// Extract types from the given string
ActivitiEventType[] types = ActivitiEventType.getTypesFromString(listenersToAdd.getKey());
for(ActivitiEventListener listenerToAdd : listenersToAdd.getValue()) {
this.eventDispatcher.addEventListener(listenerToAdd, types);
}
}
}
}
第5行initEventDispatcher()开始初始化事件转发器。12-14行若没有配置用户自定义事件转发器,则创建默认事件转发器。16行根据配置设定是否开启事件转发。18-22行根据用户配置的eventListeners属性注册全局事件监听器。24-33行根据用户配置的typedEventListeners属性注册指定事件类型的全局事件监听器。
我们拿流程文档部署命令DeployCmd.java来看事件的具体触发:
public Deployment execute(CommandContext commandContext) {
......
if (commandContext.getProcessEngineConfiguration().getEventDispatcher().isEnabled()) {
commandContext.getProcessEngineConfiguration().getEventDispatcher().dispatchEvent(
ActivitiEventBuilder.createEntityEvent(ActivitiEventType.ENTITY_CREATED, deployment));
}
......
if (commandContext.getProcessEngineConfiguration().getEventDispatcher().isEnabled()) {
commandContext.getProcessEngineConfiguration().getEventDispatcher().dispatchEvent(
ActivitiEventBuilder.createEntityEvent(ActivitiEventType.ENTITY_INITIALIZED, deployment));
}
return deployment;
}
上面6-7行触发ENTITY_CREATED事件,13-14行触发ENTITY_INITIALIZED。acitiviti通过ActivitiEventDispatcherImpl的dispatchEvent进行事件转发,查看ActivitiEventDispatcherImpl.java:
public void dispatchEvent(ActivitiEvent event) {
if (enabled) {
eventSupport.dispatchEvent(event);
}
// Check if a process context is active. If so, we also call the
// process-definition specific listeners (if any).
if (Context.isExecutionContextActive()) {
ProcessDefinitionEntity definition = Context.getExecutionContext().getProcessDefinition();
if (definition != null) {
definition.getEventSupport().dispatchEvent(event);
}
} else {
// Try getting hold of the Process definition, based on the process
// definition-key, if a context is active
CommandContext commandContext = Context.getCommandContext();
if (commandContext != null) {
ProcessDefinitionEntity processDefinition = extractProcessDefinitionEntityFromEvent(event);
if (processDefinition != null) {
processDefinition.getEventSupport().dispatchEvent(event);
}
}
}
}
第3行委托ActivitiEventSupport进行事件转发。第8-23行为通过流程定义内部的ActivitiEventSupport进行转发。第8行判断流程实例是否开始运行,如果是则获取流程定义实体,通过流程定义实体内管理的ActivitiEventSupport进行转发。注意这个ActivitiEventSupport和第3行的不同,是流程定义实体内部创建的。这两种转发有什么不同呢?在activiti 5.15之前,只能在流程文档的process元素里面配置全局事件监听器,在流程文档中配置的事件监听器会由流程定义内部的ActivitiEventSupport进行转发。目前的代码设计是新老版本的兼容。之后我们跟踪ActivitiEventSupport.java看其如何转发:
public void dispatchEvent(ActivitiEvent event) {
if (event == null) {
throw new ActivitiIllegalArgumentException("Event cannot be null.");
}
if (event.getType() == null) {
throw new ActivitiIllegalArgumentException("Event type cannot be null.");
}
if (!eventListeners.isEmpty()) {
for (ActivitiEventListener listener : eventListeners) {
dispatchEvent(event, listener);
}
}
List<ActivitiEventListener> typed = typedListeners.get(event.getType());
if (typed != null && !typed.isEmpty()) {
for (ActivitiEventListener listener : typed) {
dispatchEvent(event, listener);
}
}
}
protected void dispatchEvent(ActivitiEvent event, ActivitiEventListener listener) {
try {
listener.onEvent(event);
} catch (Throwable t) {
if (listener.isFailOnException()) {
throw new ActivitiException("Exception while executing event-listener", t);
} else {
LOG.warn("Exception while executing event-listener, which was ignored", t);
}
}
}
11-13行获取注册的全局事件监听器并逐个转发,16-21行则根据事件类型获取注册的指定类型全局监听器并转发。26行调用监听器的onEvent方法。