xiaobaoqiu Blog

Think More, Code Less

Activiti事件处理

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);
}