Activiti6.0(八)监听器使用

目录

一、前言

二、监听器使用场景

三、监听器类别

1、执行监听器(ExecutionListener)

2、任务监听器(TaskListener)

3、事件监听器(ActivitiEventListener)

4、监听器执行优先级


一、前言

上一篇在讲分配处理人时其实就已经有用到监听器了,Activiti最灵活的点便是其提供了多种多样的监听器及对应的监听事件,能实现绝大部分业务需求,因此学会使用监听器也是掌握Activiti非常重要的一个步骤。

二、监听器使用场景

在使用Activiti时,其通常作为底层引擎使用,而引擎通常与业务是分开的,因此需要引擎去推动业务,即流程执行到某个阶段时,我们才去执行相对应的业务逻辑,这便依赖Activiti的监听器。常见的场景有:

1、环节人员动态分配

2、某个环节开始或结束时需要记录环节信息,或者插入一些记录数据

3、任务开始或结束时需要做一些自定义的业务

...

总之监听器就是用来满足各种各样复杂的业务需求

三、监听器类别

按照监听的类型来划分,大致有三种:

  • 执行监听器
  • 任务监听器
  • 事件监听器

下面一一介绍

1、执行监听器(ExecutionListener

该监听器可以配置在节点和线,主要有三种事件类型:

  • start:开始时触发
  • end:结束时触发
  • take:主要用于监控流程线,当流程流转该线时触发

既然要使用监听器,那肯定得先自己自定义一个,需要实现org.activiti.engine.delegate.ExecutionListener,如下:

package activiti.activiti.listener;

import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.delegate.ExecutionListener;

/**
 * Created by xujia on 2020/2/6
 */
public class ExecutionListenerImpl implements ExecutionListener {

    @Override
    public void notify(DelegateExecution delegateExecution) {
        String event = delegateExecution.getEventName();
        switch (event) {
            case "start" :
                System.out.println("start event");
                break;
            case "end" :
                System.out.println("end event");
                break;
            case "take" :
                System.out.println("take event");
                break;
        }
    }
}

然后便是在流程图处进行配置,首先看下我绘制的简单流程图:

我在第一环节处配置了两个监听器,分别是start和end事件,在线3处配置了take事件,配置方式如下:

当然上面监听器的配置也可以使用委托表达式,使用spring注入的方式来初始化自定义监听器,配置如下:

线3的配置同理,xml如下:

<?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/processdef">
  <process id="listenerTest" name="监听器测试" isExecutable="true">
    <startEvent id="startEvent1"></startEvent>
    <endEvent id="sid-39CC4B7B-E9FF-4C68-B2D2-4ADC6DD80560"></endEvent>
    <userTask id="sid-E27274BD-5FFE-4484-B0B7-EFAC35F180CA" name="第一环节" activiti:candidateUsers="test">
      <extensionElements>
        <activiti:executionListener event="start" class="activiti.activiti.listener.ExecutionListenerImpl"></activiti:executionListener>
        <activiti:executionListener event="end" class="activiti.activiti.listener.ExecutionListenerImpl"></activiti:executionListener>
        <modeler:user-info-firstname-test xmlns:modeler="http://activiti.com/modeler"><![CDATA[test]]></modeler:user-info-firstname-test>
        <modeler:activiti-idm-candidate-user xmlns:modeler="http://activiti.com/modeler"><![CDATA[true]]></modeler:activiti-idm-candidate-user>
        <modeler:initiator-can-complete xmlns:modeler="http://activiti.com/modeler"><![CDATA[false]]></modeler:initiator-can-complete>
      </extensionElements>
    </userTask>
    <userTask id="sid-E1583F4D-6998-48A6-B4EF-C0623ECD86A7" name="第二环节" activiti:candidateUsers="test">
      <extensionElements>
        <modeler:user-info-firstname-test xmlns:modeler="http://activiti.com/modeler"><![CDATA[test]]></modeler:user-info-firstname-test>
        <modeler:activiti-idm-candidate-user xmlns:modeler="http://activiti.com/modeler"><![CDATA[true]]></modeler:activiti-idm-candidate-user>
        <modeler:initiator-can-complete xmlns:modeler="http://activiti.com/modeler"><![CDATA[false]]></modeler:initiator-can-complete>
      </extensionElements>
    </userTask>
    <sequenceFlow id="sid-CEE5F74F-8FDA-44CA-BB47-5E9B0531F08B" name="线1" sourceRef="startEvent1" targetRef="sid-E27274BD-5FFE-4484-B0B7-EFAC35F180CA"></sequenceFlow>
    <sequenceFlow id="sid-CA16842F-0D76-4CD0-B38F-17030B7181BD" name="线2" sourceRef="sid-E27274BD-5FFE-4484-B0B7-EFAC35F180CA" targetRef="sid-E1583F4D-6998-48A6-B4EF-C0623ECD86A7"></sequenceFlow>
    <sequenceFlow id="sid-D0B1DEB4-58AB-4FB2-80D9-1EFC48C37D28" name="线3" sourceRef="sid-E1583F4D-6998-48A6-B4EF-C0623ECD86A7" targetRef="sid-39CC4B7B-E9FF-4C68-B2D2-4ADC6DD80560">
      <extensionElements>
        <activiti:executionListener event="take" class="activiti.activiti.listener.ExecutionListenerImpl"></activiti:executionListener>
      </extensionElements>
    </sequenceFlow>
  </process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_listenerTest">
    <bpmndi:BPMNPlane bpmnElement="listenerTest" id="BPMNPlane_listenerTest">
      <bpmndi:BPMNShape bpmnElement="startEvent1" id="BPMNShape_startEvent1">
        <omgdc:Bounds height="30.0" width="30.0" x="100.0" y="163.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="sid-39CC4B7B-E9FF-4C68-B2D2-4ADC6DD80560" id="BPMNShape_sid-39CC4B7B-E9FF-4C68-B2D2-4ADC6DD80560">
        <omgdc:Bounds height="28.0" width="28.0" x="701.0" y="164.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="sid-E27274BD-5FFE-4484-B0B7-EFAC35F180CA" id="BPMNShape_sid-E27274BD-5FFE-4484-B0B7-EFAC35F180CA">
        <omgdc:Bounds height="80.0" width="100.0" x="223.0" y="138.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="sid-E1583F4D-6998-48A6-B4EF-C0623ECD86A7" id="BPMNShape_sid-E1583F4D-6998-48A6-B4EF-C0623ECD86A7">
        <omgdc:Bounds height="80.0" width="100.0" x="450.0" y="138.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge bpmnElement="sid-CA16842F-0D76-4CD0-B38F-17030B7181BD" id="BPMNEdge_sid-CA16842F-0D76-4CD0-B38F-17030B7181BD">
        <omgdi:waypoint x="323.0" y="178.0"></omgdi:waypoint>
        <omgdi:waypoint x="450.0" y="178.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="sid-CEE5F74F-8FDA-44CA-BB47-5E9B0531F08B" id="BPMNEdge_sid-CEE5F74F-8FDA-44CA-BB47-5E9B0531F08B">
        <omgdi:waypoint x="130.0" y="178.0"></omgdi:waypoint>
        <omgdi:waypoint x="223.0" y="178.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="sid-D0B1DEB4-58AB-4FB2-80D9-1EFC48C37D28" id="BPMNEdge_sid-D0B1DEB4-58AB-4FB2-80D9-1EFC48C37D28">
        <omgdi:waypoint x="550.0" y="178.0"></omgdi:waypoint>
        <omgdi:waypoint x="701.0" y="178.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>

然后启动流程:

/**
     * 监听器测试
     */
    @Test
    public void listenerTest() {
        // 1、发布流程,当流程的key相同时则以版本来控制,每次启动流程实例时默认取最新版本
        Deployment deployment = repositoryService.createDeployment().name("监听器测试流程").addClasspathResource("processes/listener.bpmn20.xml").deploy();

        // 2、启动一个流程实例
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("listenerTest");

    }

启动时便能触发start事件,当完成第一环节任务时触发end事件,当完成第二环节任务时,由于需要经过线3,此时触发take事件。

总结:start和end事件也适用于开始和结束节点,可使用该监听器实现一些对全局的监听,比如流程开始时和流程结束时,以及实现某些流程线的监听。

2、任务监听器(TaskListener)

该监听器只能监听带有任务信息的节点,开始和结束节点由于在Activiti中没有任务因此无法监听,其支持四种监听事件:

  • create:任务创建时触发,此时所有属性已被设置完毕
  • assignment:任务被委派给某人后触发,如通过变量的方式设置处理人时会触发,先于create事件触发
  • complete:在任务完成后,且被从运行时数据(runtime data)中删除前触发。
  • delete:在任务将要被删除之前发生。

使用方式同执行监听器类似,首先自定义一个实现类,如下:

package activiti.activiti.listener;

import org.activiti.engine.delegate.DelegateTask;
import org.activiti.engine.delegate.TaskListener;

/**
 * Created by xujia on 2020/2/6
 */
public class AssigneeListener implements TaskListener {

    @Override
    public void notify(DelegateTask delegateTask) {
        String event = delegateTask.getEventName();
        switch (event) {
            case "create" :
                System.out.println("create event");
                break;
            case "assignment" :
                System.out.println("assignment event");
                break;
            case "complete" :
                System.out.println("complete event");
                break;
            case "delete" :
                System.out.println("delete event");
                break;
        }
    }
}

然后我在第一环节处配置了四个监听器

同时在第一环节我也配置了两个执行监听器,接着使用流程变量的方式为第一环节设置处理人"李四",启动流程:

@Test
    public void listenerTest() {
        // 1、发布流程,当流程的key相同时则以版本来控制,每次启动流程实例时默认取最新版本
        Deployment deployment = repositoryService.createDeployment().name("监听器测试流程").addClasspathResource("processes/listener.bpmn20.xml").deploy();

        Map<String, Object> variables = new HashMap<>();
        variables.put("userId", "李四");
        // 2、启动一个流程实例
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("listenerTest", variables);

    }

看到控制台依次打印如下信息:

start event
assignment event
create event

说明执行监听器的start事件先于任务监听器执行,由于使用流程变量对处理人进行赋值触发assignment事件,此时act_ru_task表也会新增一条第一环节的任务因此触发create事件,同时assignment事件先与create执行,然后完成第一环节任务:

@Test
    public void completeTask() {
        String taskId = "87502";
        taskService.complete(taskId);
    }

控制台依次打印:

complete event
delete event
end event

说明执行监听器的end事件后于任务监听器执行,由于完成了第一环节的任务因此触发complete事件,任务完成后会从act_ru_task表删除,因此触发delete事件。

3、事件监听器(ActivitiEventListener)

了解完上面的两种监听器后我们可以发现,执行监听器和任务监听器都是在流程设计时就需要配置,而且只针对某个流程,万一所有流程都需要配置,且他们的业务是共同的那么有没有某种监听器可以全局监听呢?Activiti当然提供了,这便是事件监听器。它允许在引擎触发指定的事件类型时收到提醒,同时它还允许在运行阶段动态的增加监听器。

支持的事件如下:(该列表选自Activiti5.16手册,6新增了部分类型,但是常用的都已经包含在下面了,感兴趣可自行查看ActivitiEvent枚举类

事件名称 描述 事件类型
ENGINE_CREATED 监听器监听的流程引擎已经创建完毕,并准备好接受API调用。 org.activiti...ActivitiEvent
ENGINE_CLOSED 监听器监听的流程引擎已经关闭,不再接受API调用。 org.activiti...ActivitiEvent
ENTITY_CREATED 创建了一个新实体。实体包含在事件中。 org.activiti...ActivitiEntityEvent
ENTITY_INITIALIZED 创建了一个新实体,初始化也完成了。如果这个实体的创建会包含子实体的创建,这个事件会在子实体都创建/初始化完成后被触发,这是与ENTITY_CREATED的区别。 org.activiti...ActivitiEntityEvent
ENTITY_UPDATED 更新了已存在的实体。实体包含在事件中。 org.activiti...ActivitiEntityEvent
ENTITY_DELETED 删除了已存在的实体。实体包含在事件中。 org.activiti...ActivitiEntityEvent
ENTITY_SUSPENDED 暂停了已存在的实体。实体包含在事件中。会被ProcessDefinitions, ProcessInstances 和 Tasks抛出。 org.activiti...ActivitiEntityEvent
ENTITY_ACTIVATED 激活了已存在的实体,实体包含在事件中。会被ProcessDefinitions, ProcessInstances 和 Tasks抛出。 org.activiti...ActivitiEntityEvent
JOB_EXECUTION_SUCCESS 作业执行成功。job包含在事件中。 org.activiti...ActivitiEntityEvent
JOB_EXECUTION_FAILURE 作业执行失败。作业和异常信息包含在事件中。 org.activiti...ActivitiEntityEvent and org.activiti...ActivitiExceptionEvent
JOB_RETRIES_DECREMENTED 因为作业执行失败,导致重试次数减少。作业包含在事件中。 org.activiti...ActivitiEntityEvent
TIMER_FIRED 触发了定时器。job包含在事件中。 org.activiti...ActivitiEntityEvent
JOB_CANCELED 取消了一个作业。事件包含取消的作业。作业可以通过API调用取消, 任务完成后对应的边界定时器也会取消,在新流程定义发布时也会取消。 org.activiti...ActivitiEntityEvent
ACTIVITY_STARTED 一个节点开始执行 org.activiti...ActivitiActivityEvent
ACTIVITY_COMPLETED 一个节点成功结束 org.activiti...ActivitiActivityEvent
ACTIVITY_SIGNALED 一个节点收到了一个信号 org.activiti...ActivitiSignalEvent
ACTIVITY_MESSAGE_RECEIVED 一个节点收到了一个消息。在节点收到消息之前触发。收到后,会触发ACTIVITY_SIGNALACTIVITY_STARTED,这会根据节点的类型(边界事件,事件子流程开始事件) org.activiti...ActivitiMessageEvent
ACTIVITY_ERROR_RECEIVED 一个节点收到了一个错误事件。在节点实际处理错误之前触发。 事件的activityId对应着处理错误的节点。 这个事件后续会是ACTIVITY_SIGNALLEDACTIVITY_COMPLETE, 如果错误发送成功的话。 org.activiti...ActivitiErrorEvent
UNCAUGHT_BPMN_ERROR 抛出了未捕获的BPMN错误。流程没有提供针对这个错误的处理器。 事件的activityId为空。 org.activiti...ActivitiErrorEvent
ACTIVITY_COMPENSATE 一个节点将要被补偿。事件包含了将要执行补偿的节点id。 org.activiti...ActivitiActivityEvent
VARIABLE_CREATED 创建了一个变量。事件包含变量名,变量值和对应的分支或任务(如果存在)。 org.activiti...ActivitiVariableEvent
VARIABLE_UPDATED 更新了一个变量。事件包含变量名,变量值和对应的分支或任务(如果存在)。 org.activiti...ActivitiVariableEvent
VARIABLE_DELETED 删除了一个变量。事件包含变量名,变量值和对应的分支或任务(如果存在)。 org.activiti...ActivitiVariableEvent
TASK_ASSIGNED 任务被分配给了一个人员。事件包含任务。 org.activiti...ActivitiEntityEvent
TASK_CREATED 创建了新任务。它位于ENTITY_CREATE事件之后。当任务是由流程创建时, 这个事件会在TaskListener执行之前被执行。 org.activiti...ActivitiEntityEvent
TASK_COMPLETED 任务被完成了。它会在ENTITY_DELETE事件之前触发。当任务是流程一部分时,事件会在流程继续运行之前, 后续事件将是ACTIVITY_COMPLETE,对应着完成任务的节点。 org.activiti...ActivitiEntityEvent
TASK_TIMEOUT 任务已超时,在TIMER_FIRED事件之后,会触发用户任务的超时事件, 当这个任务分配了一个定时器的时候。 org.activiti...ActivitiEntityEvent
PROCESS_COMPLETED 流程已结束。在最后一个节点的ACTIVITY_COMPLETED事件之后触发。 当流程到达的状态,没有任何后续连线时, 流程就会结束。 org.activiti...ActivitiEntityEvent
MEMBERSHIP_CREATED 用户被添加到一个组里。事件包含了用户和组的id。 org.activiti...ActivitiMembershipEvent
MEMBERSHIP_DELETED 用户被从一个组中删除。事件包含了用户和组的id。 org.activiti...ActivitiMembershipEvent
MEMBERSHIPS_DELETED 所有成员被从一个组中删除。在成员删除之前触发这个事件,所以他们都是可以访问的。 因为性能方面的考虑,不会为每个成员触发单独的MEMBERSHIP_DELETED事件。 org.activiti...ActivitiMembe

上面一大堆事件可能看的很头疼,常用的无非是ACTIVITY_STARTED、ACTIVITY_COMPLETED、TASK_ASSIGNED、TASK_CREATED、TASK_COMPLETED,一切以满足业务为基准~

配置全局监听器,这里以SpringBoot配置为例,老规矩首先自定义实现类:

@Component
public class ActivitiEventListenerImpl implements ActivitiEventListener {

    @Override
    public void onEvent(ActivitiEvent event) {
        // 这里可以根据需要自行强转event的实现类,如获取流转实例对象可强转为ActivitiEntityEventImpl,如获取环节信息可强转为ActivitiActivityEventImpl,等等等
        switch (event.getType()) {
            case TASK_CREATED:
                System.out.println("TASK_CREATED event");
                break;
            case TASK_COMPLETED:
                System.out.println("TASK_COMPLETED event");
                break;
            case ACTIVITY_COMPLETED:
                System.out.println("ACTIVITY_COMPLETED event");
                break;
            case ACTIVITY_STARTED:
                System.out.println("ACTIVITY_STARTED event");
                break;
        }
    }

    @Override
    public boolean isFailOnException() {
        return false;
    }
}

然后进行Activiti的初始化配置,这里使用Api方式配置:

@Component
public class ActivitiConfig implements ProcessEngineConfigurationConfigurer {

    @Autowired
    private ActivitiEventListenerImpl activitiEventListener;

    @Override
    public void configure(SpringProcessEngineConfiguration configuration) {
        Map<String, List<ActivitiEventListener>> activitiEventListenerMap = new HashMap<>();
        // 设置全局监听器
        activitiEventListenerMap.put(ActivitiEventType.ACTIVITY_STARTED.name(), Lists.newArrayList(activitiEventListener));
        activitiEventListenerMap.put(ActivitiEventType.ACTIVITY_COMPLETED.name(), Lists.newArrayList(activitiEventListener));
        activitiEventListenerMap.put(ActivitiEventType.TASK_CREATED.name(), Lists.newArrayList(activitiEventListener));
        activitiEventListenerMap.put(ActivitiEventType.TASK_COMPLETED.name(), Lists.newArrayList(activitiEventListener));
        configuration.setTypedEventListeners(activitiEventListenerMap);
    }
}

可以看到我分别注册了四个事件的监听,所有节点的开始与完成,以及所有任务的开始与完成,在自定义的监听器内均会收到提,最后启动上面的流程,看到控制台输出:

ACTIVITY_STARTED event
ACTIVITY_COMPLETED event
start event
ACTIVITY_STARTED event
assignment event
create event
TASK_CREATED event

来分析下输出信息,首先流程实例启动触发开始环节的事件监听器-节点开始与节点完成事件,流转到第一环节后,按顺序依次触发执行监听器的开始事件,事件监听器的节点开始事件,任务监听器的处理人分配与任务创建事件,事件监听器的任务创建事件。

从输出来看,执行监听器先与事件监听器,任务监听器先于事件监听器,然后完成该任务:

complete event
TASK_COMPLETED event
delete event
end event
ACTIVITY_COMPLETED event
ACTIVITY_STARTED event
TASK_CREATED event

由于完成了第一环节的任务,因此首先触发的是任务监听器的完成事件,然后是事件监听器的任务完成,接着是任务监听的删除事件,最后是执行监听器的节点结束事件以及事件监听器的节点结束事件,此时流转到第二环节,依次触发对应的事件监听。

关于ActivitiEventListener接口的onEvent方法的ActivitiEvent接口:

public interface ActivitiEvent {

  /**
   * @return type of event.
   */
  ActivitiEventType getType();

  /**
   * @return the id of the execution this event is associated with. Returns null, if the event was not dispatched from within an active execution.
   */
  String getExecutionId();

  /**
   * @return the id of the process instance this event is associated with. Returns null, if the event was not dispatched from within an active execution.
   */
  String getProcessInstanceId();

  /**
   * @return the id of the process definition this event is associated with. Returns null, if the event was not dispatched from within an active execution.
   */
  String getProcessDefinitionId();

}

该接口自身只有四个方法,在实际业务场景下通常无法满足,这时候我们就需要在对应的触发事件类型下去获取对应的实现类,实现类与事件类型是相关的,可以看到它有很多实现类以及接口:

在运行阶段添加或删除监听器:

可以通过Api(RuntimeService)实现

/**
   * 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 occurs, which type is in the given types.
   * 
   * @param listenerToAdd
   *          the listener to add
   * @param types
   *          types of events the listener should be notified for
   */
  void addEventListener(ActivitiEventListener listenerToAdd, ActivitiEventType... types);

  /**
   * Removes the given listener from this dispatcher. The listener will no longer be notified, regardless of the type(s) it was registered for in the first place.
   * 
   * @param listenerToRemove
   *          listener to remove
   */
  void removeEventListener(ActivitiEventListener listenerToRemove);

注意在运行期添加的监听器在引擎重启后就消失了。

4、监听器执行优先级

如果在同一个节点同时配置三种监听器时,事件监听器相对来说优先级总是最低的,而其余两个监听器之间则要分情况来说:如果是开始,则执行监听器优先级高;如果是结束,则任务监听器优先级高。总而言之,粗糙点来说,执行监听器>任务监听器>事件监听器。

猜你喜欢

转载自blog.csdn.net/m0_38001814/article/details/104197670