1.事件处理
Activiti 内部充斥着各种各样的事件,详见:org.activiti.engine.delegate.event.ActivitiEventType的定义。
在 Activiti 5.15 以前,处理事件需要在流程设计的时候显示的加入各种Listener,比如:
1
2
3
4
5
6
7
<userTask id="leaderVerifyTask" activiti:taskType="2">
<extensionElements>
<activiti:taskListener event="create" delegateExpression="${taskCreateListener}"/>
<activiti:taskListener event="assignment" delegateExpression="${taskAssignmentListener}"/>
<activiti:taskListener event="complete" delegateExpression="${taskCompleteListener}"/>
</extensionElements>
</userTask>
Spring会注入 taskCreateListener 这个bean来作为 leaderVerifyTask 这个任务的创建事件响应逻辑。
这种方式的问题在于,流程设计的时候需要知道 代码中具体的类或者bean的名称,因此无法做到将流程的设计交与开发人员之外的人来做。
在 Activiti 5.15 中加入了全局的事件监听器 ActivitiEventListener,我们只需要实现一个自己的全局的事件处理器,然后自定义各种事件的处理器。
/**
* 流程服务的全局 Listener
*
* @author xiaobaoqiu Date: 16-12-19 Time: 下午7:16
*/
public class GlobalProcessEventListener implements ActivitiEventListener {
/**
* 各类 Event 的处理器
* 其中事件类型参见 ActivitiEventType 定义
*
* @see org.activiti.engine.delegate.event.ActivitiEventType
*/
private Map<ActivitiEventType, ProcessEventHandler> handlers = new HashMap<>();
@Override
public void onEvent(ActivitiEvent event) {
ProcessEventHandler handler = handlers.get(event.getType());
if (handler != null) {
handler.handle(event);
}
}
@Override
public boolean isFailOnException() {
return false;
}
public Map<ActivitiEventType, ProcessEventHandler> getHandlers() {
return handlers;
}
public void setHandlers(Map<ActivitiEventType, ProcessEventHandler> handlers) {
this.handlers = handlers;
}
}
/**
* 流程事件处理
*
* @author xiaobaoqiu Date: 16-12-21 Time: 下午1:33
*/
public interface ProcessEventHandler {
void handle(ActivitiEvent event);
}
ActivitiEventListener能做到和流程设计无关,只需要在配置 Activiti 的时候将其注入,并且可以在其中配置能处理哪些类型的事件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
<property 其他配置 />
<property name="eventListeners">
<list>
<ref bean="globalProcessEventListener"/>
</list>
</property>
</bean>
<!--流程全局事件处理器-->
<bean id="globalProcessEventListener" class="com.qunar.scm.ct.biz.process.event.GlobalProcessEventListener">
<property name="handlers">
<map>
<entry key="TASK_CREATED">
<bean class="com.qunar.xxx.ProcessProcessTaskCreateListener"/>
</entry>
<entry key="TASK_COMPLETED">
<bean class="com.qunar.xxx.ProcessProcessTaskCompleteListener"/>
</entry>
<entry key="TASK_ASSIGNED">
<bean class="com.qunar.xxx.ProcessProcessTaskAssignmentListener"/>
</entry>
</map>
</property>
</bean>
参考:
https://www.activiti.org/userguide/index.html#eventDispatcherConfiguration
2.Listener异常处理
需要注意的一点是 GlobalProcessEventListener 中的异常处理,我们在实现 GlobalProcessEventListener 的时候,ActivitiEventListener有一个接口需要实现:
1
2
3
4
5
/**
* @return whether or not the current operation should fail when this listeners execution
* throws an exception.
*/
boolean isFailOnException();
即当 listener 抛出一个异常的时候,当前操作时序应该失败(其实就是是否需要吞掉 listener 的异常).
我们可以看看 Activiti 相关的源代码,在 ActivitiEventSupport 中的 dispatchEvent 函数,我们可以清晰的看到,listener.isFailOnException()为false则会讲异常吞掉,只是打一行日志.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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 {
// Ignore the exception and continue notifying remaining listeners. The
// listener
// explicitly states that the exception should not bubble up
LOG.warn("Exception while executing event-listener, which was ignored", t);
}
}
}
我的建议(也是我现在的做法)是:isFailOnException()设置为true,然后在对应的 ProcessEventHandler 自己处理需要吞掉异常的场景.
3.Listener注册
通常而言,我们的 ProcessEventHandler 需要使用到 Activiti 的Service,比如我的一个简单场景,我需要在 UserTask 获取一些业务信息,新词我需要使用 RuntimeService 来查询一些信息
1
2
3
4
5
6
7
8
9
10
11
/**
* 任务创建事件
*
* @author xiaobaoqiu Date: 16-12-23 Time: 下午7:09
*/
public class ProcessTaskCreateListener extends AbstractProcessTaskListener {
@Resource
private RuntimeService runtimeService;
...
}
按照第一节的写法,就会出现Bean循环依赖的问题:
1
2
3
4
5
1. ProcessEngineConfiguration 实例创建会触发 GlobalProcessEventListener 创建;
2.GlobalProcessEventListener 实例创建会触发 ProcessProcessTaskCreateListener 创建;
3.ProcessProcessTaskCreateListener 实例创建会触发 RuntimeService 创建;
4.RuntimeService 实例创建依赖 ProcessEngineFactoryBean 创建;
5.ProcessEngineFactoryBean 创建依赖 ProcessEngineConfiguration实例;
并且 ProcessEngineFactoryBean 是 Activiti 自己实现的 FactoryBean,用来获得 ProcessEngine 实例 :
1
2
3
public class ProcessEngineFactoryBean implements FactoryBean<ProcessEngine>, DisposableBean, ApplicationContextAware {
...
}
这个问题一个比较好的解法是, 将 GlobalProcessEventListener 的注册延后到 GlobalProcessEventListener 初始化完成之后,因此涉及到两个问题:
1.GlobalProcessEventListener 什么时机去做自己注册;
2.GlobalProcessEventListener 如何将自己注册到 SpringProcessEngineConfiguration 中;
其中第一个问题很好回答, GlobalProcessEventListener 自己初始化完成之后去自注册,这个时候依赖的各种Bean已经创建初始化完成了.因此只需要实现 ApplicationContextAware 接口,然后在 setApplicationContext 中做注册的工作.
1
2
3
4
5
6
7
8
9
public class GlobalProcessEventListener implements ActivitiEventListener, ApplicationContextAware {
/**
* 将当前 EventListener 注册到 Activiti 中
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
...
}
}
第二个问题,如何自己注册上去,这个看看源代码就会找到方法, 我们在配置文件中给 SpringProcessEngineConfiguration 配置 EventListener 调用的其实是 setEventListeners 方法
1
2
3
public void setEventListeners(List<ActivitiEventListener> eventListeners) {
this.eventListeners = eventListeners;
}
我们看远吗发现其中使用 eventListeners 的只有一个地方,就是将其赋值给 ActivitiEventDispatcher
1
2
3
4
5
if(eventListeners != null) {
for(ActivitiEventListener listenerToAdd : eventListeners) {
this.eventDispatcher.addEventListener(listenerToAdd);
}
}
而 ActivitiEventDispatcher 才是使用 EventListener 的地方.我们再看源码发现 ActivitiEventDispatcher 提供了 addEventListener 的入口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public interface ActivitiEventDispatcher {
/**
* Adds an event-listener which will be notified of ALL events by the dispatcher.
* @param listenerToAdd the listener to add
*/
void addEventListener(ActivitiEventListener listenerToAdd);
/**
* Adds an event-listener which will only be notified when an event of the given types occurs.
* @param listenerToAdd the listener to add
* @param types types of events the listener should be notified for
*/
void addEventListener(ActivitiEventListener listenerToAdd, ActivitiEventType... types);
并且 SpringProcessEngineConfiguration 有 getEventDispatcher 来获得 ActivitiEventDispatcher 实例,因此我们可以通过 getEventDispatcher 来拿到 ActivitiEventDispatcher 实例,然后将当前的 EventListener 通过 addEventListener 的方法增加到 ActivitiEventDispatcher 的 EventListener 列表中.
因此,最终的实现代码很简单:
1
2
3
4
5
6
7
8
/**
* 将当前 EventListener 注册到 Activiti 中
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringProcessEngineConfiguration configuration = applicationContext.getBean(SpringProcessEngineConfiguration.class);
configuration.getEventDispatcher().addEventListener(this);
}