activiti学习(十一)——全局事件监听器的基本使用及其原理

在流程运转的过程中,流程引擎会发出很多不同的事件,前面的文章,我们通过执行监听器和任务监听器捕获到对应事件并进行处理。除了这两个监听器以外,activiti从5.15版开始加入了全局事件监听器,这样很多重复的监听器就不需要在每个活动上去绑定。添加全局监听器有几种方式:

通过流程引擎文件方式进行配置

首先创建全局监听器MyEventListener.java

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&amp;&amp;characterEncoding=utf8&amp;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方法。

发布了43 篇原创文章 · 获赞 5 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/sadoshi/article/details/105297357