在流程运转的过程中,流程引擎会发出很多不同的事件,前面的文章,我们通过执行监听器和任务监听器捕获到对应事件并进行处理。除了这两个监听器以外,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:
17-21行新增自定义的全局事件监听器列表。bpmn图我们使用《activiti学习(二)——activiti流程的部署》的firstBPM.bpmn。然后我们通过初始化流程引擎、部署、启动流程等操作,可以观察全局事件监听器捕捉到的事件类型。
在这个例子中,我们使用《activiti学习(一)——activiti流程引擎的配置与初始化操作》的activiti.cfg.xml的配置文件,然后把全局事件监听器添加到流程文档的process元素内,创建eventListenerBPM.bpmn:
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中配置:
......
关闭事件转发器并不会影响任务监听器和执行监听器的监听,只会影响全局事件监听器。
日志监听器本质上也是一个全局事件监听器,主要记录流程实例和任务节点相关的事件和信息,并不会把所有事件都记录。记录的数据在act_evt_log表中。开启的方法是在cfg.xml中配置enableDatabaseEventLogging属性为true
......
除了eventListeners之外,activiti还可以让用户专门监听指定的事件类型 。看下具体例子,监听器沿用上面的MyEventListener.java,重点是在cfg.xml里对processEngineConfiguration的typedEventListeners属性进行设置:
......省略
上面我们设定这个监听器只监听“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> 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 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方法。