activiti學習(十)——自定義對象解析器

上一篇博文分析了對象解析器的原理。本文來編寫一個自定義對象解析器。考慮這樣的場景:生產中我們常常在流程的上一環節選擇下環節的處理人,然後再提交流程。之後流程運轉到下一個環節後,會在對應的處理人名下,即userTask的candidate或assignee是上環節選中的處理人。

上述的實現方式可以是這樣,對userTask添加自定義任務監聽器,該監聽器主要作用是把流程實例中的變量設置爲userTask的candidate或assignee。這樣在流程執行的時候,上一環節選擇了處理人,寫入流程變量中,待下一環節的任務監聽器把變量讀出來設置到userTask的candidate或assignee。但是在bpmn圖上爲每個userTask上添加這樣的監聽器,隨着流程和userTask增多,維護起來非常繁瑣。生產系統常常有十幾個不同的流程,每個流程數個或十數個節點,而且隨着業務變化,流程圖也要時不時進行修改,手動對每個userTask添加監聽器不現實。有沒有辦法爲任務節點統一添加監聽器呢?答案是有的!我們可以自定義一個userTask的對象解析器,在解析每個userTask時,爲其添加監聽器,這樣我們就沒必要在bpmn圖上手動添加了。不僅省時省力,而且方便統一管理。

首先我們自定義一個監聽器AssignmentTaskListener.java

public class AssignmentTaskListener implements TaskListener{

	public void notify(DelegateTask delegateTask) {
		String assignee = (String)delegateTask.getVariable("assignee");
		delegateTask.setAssignee(assignee);
	}
}

改監聽器主要作用就是在觸發的時候,把流程變量"assignee"中的值設置到userTask的assignee中。

接着我們擴展userTask的解析器,自定義MyUserTaskParseHandler.java

public class MyUserTaskParseHandler extends UserTaskParseHandler{

	private List<TaskListener> taskListenerList;
	
	@Override
	protected void executeParse(BpmnParse bpmnParse, UserTask userTask) {
		//由父類完成元素解析
		super.executeParse(bpmnParse, userTask);
		
		for(TaskListener taskListener : taskListenerList) {
			TaskDefinition taskDefinition = (TaskDefinition)bpmnParse.getCurrentActivity().getProperty(UserTaskParseHandler.PROPERTY_TASK_DEFINITION);
			taskDefinition.addTaskListener(TaskListener.EVENTNAME_CREATE, taskListener);
		}
	}

	public List<TaskListener> getTaskListenerList() {
		return taskListenerList;
	}

	public void setTaskListenerList(List<TaskListener> taskListenerList) {
		this.taskListenerList = taskListenerList;
	}
}

第8行先調用父類進行元素解析。10-12行對當前userTask添加create事件的任務監聽器。爲何不在assignment事件觸發時添加任務監聽器呢?因爲如果bpmn圖中userTask沒有設置assignee的話,流程引擎就不會觸發assignment事件,自然就不會調用任務監聽器了。

接着我們新建一個流程引擎配置文件,命名爲activitiParseHandler.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="customDefaultBpmnParseHandlers">
			<list>
				<bean class="parseHandler.MyUserTaskParseHandler">
					<property name="taskListenerList">
						<list>
							<bean class="parseHandler.AssignmentTaskListener"></bean>
						</list>
					</property>
				</bean>
			</list>
		</property>
	</bean>
</beans>

17-27爲自定義對象解析器。在初始化流程引擎時,對象解析器通過map方式管理,自定義添加的對象解析器會替換getHandledType()返回值相同的默認對象解析器。MyUserTaskParseHandler繼承了UserTaskParseHandler,getHandledType()返回的都是UserTask.class,因此前者會替換後者。20-24行通過xml注入監聽器,增加配置靈活度。

然後我們部署流程:


	private ProcessEngine pe;

	public void getFromProcessEngineConfiguration() {
		ProcessEngineConfiguration pec = ProcessEngineConfiguration
				.createProcessEngineConfigurationFromResource("activitiParseHandler.cfg.xml");
		pe = pec.buildProcessEngine();
	}
	
	public void deploy() {
		RepositoryService repositoryService = pe.getRepositoryService();
		DeploymentBuilder deploymentBuilder = repositoryService.createDeployment();
		
		InputStream inputStream = null;
		try {
			inputStream = App.class.getClassLoader().getResource("bpmn/parseHandlerBPM.bpmn").openStream();
			deploymentBuilder.addInputStream("parseHandler.bpmn", inputStream);
			deploymentBuilder.name("parseHandlerDeployment");
			Deployment deployment = deploymentBuilder.deploy();
			System.out.println("流程部署ID:" + deployment.getId());
			System.out.println("流程部署名:" + deployment.getName());
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	

查看act_ru_procdef表:

接下來我們啓動parseHandlerBPM:1:4流程,啓動時加入流程變量:

	public void startProcessById() {
		RuntimeService runtimeService = pe.getRuntimeService();
		Map<String, Object> var = new HashMap<String, Object>();
		var.put("assignee", "張三");
		ProcessInstance pi = runtimeService.startProcessInstanceById("parseHandlerBPM:1:4", var);
	}

啓動成功後,此時流程應該運行到userTask1節點上,觀察act_ru_task:

ASSIGNEE屬性值爲“張三”,證明自定義對象解析器成功爲userTask1添加監聽器,並且監聽器根據流程變量爲userTask1設置了ASSIGNEE。接下來嘗試把流程提交到userTask2節點:

	public void completeTask() {
		TaskService taskService = pe.getTaskService();
		Map<String, Object> var = new HashMap<String, Object>();
		var.put("assignee", "李四");
		taskService.complete("2505", var);
	}

提交完成後,觀察act_ru_task:

 通過截圖看到“李四”也成功設置爲userTask2的ASSIGNEE。

在企業流程應用中,設置環節處理人的方法很多,上述僅僅是一個示例。除了把下環節處理人寫到流程變量,也可以寫到數據庫表中,或者通過其他方式去實現,這裏不再細述。使用自定義對象解析器,我們除了可以實現本文提到的爲節點添加監聽器的功能外,還可以實現自定義擴展屬性的讀取。例如在bpmn圖中,我們爲某個元素添加了自定義擴展屬性。在解析元素時,讀取這些自定義擴展屬性,可以擴展元素的功能。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章