疯狂Activiti6.0连载(十一)———Activiti6的流程控制逻辑


声明:本文转载自https://my.oschina.net/JavaLaw/blog/1552152,转载目的在于传递更多信息,仅供学习交流之用。如有侵权行为,请联系我,我会及时删除。

11 流程控制逻辑

本文节选自《疯狂Workflow讲义(第2版)》。

本书代码目录:https://gitee.com/yangenxiong/CrazyActiviti

        本小节将以一个简单的例子,讲述Activiti关于流程处理的逻辑。

11.1 概述

        在Activiti5以及jBPM4,对流程的控制使用的是流程虚拟机这套API,英文为Process Virtual Machine,简称PVM。PVM将流程中的各种元素抽象出来,形成了一套Java API。

        新发布的Activiti6.0版本中,PVM及相关的API已经被移除,取而代之的是一套全新的逻辑,本小节将以一个例子,讲述这套全新逻辑,是如何进行流程控制的,本小节的案例,目的是为了让读者了解新版本Activiti是如何进行流程控制的。

11.2 设计流程对象

        基于BPMN规范,Activiti创建了对应的模型,由于BPMN规范过于庞杂,为了简单起见,在本例中,我们也先创建自己的规范。代码清单18-1为一份定义我们自己流程的XML文档。

        代码清单18-1:codes\18\18.1\my-bpmn\resource\myBpmn.xml

<?xml version="1.0" encoding="UTF-8"?> <process id="testProcess"> 	<start id="start" /> 	<flows> 		<flow id="flow1" source="start" target="task" /> 		<flow id="flow2" source="task" target="end" /> 	</flows> 	<nodes> 		<task id="task" /> 	</nodes> 	<end id="end" /> </process>

 

        代码清单18-1是一份自定义的XML文档,process元素下有一个start节点、end节点、nodes节点以及flows节点,设计对应的Java对象来表示这些节点,代码清单18-2为这些Java类的代码。

        代码清单18-2:codes\18\18.1\my-bpmn\src\org\crazyit\activiti\xml

public class BaseElement {  	// XML元素的ID 	private String id; 	...省略setter和getter方法 }  public class FlowElement extends BaseElement {  }  public class FlowNode extends FlowElement {  	// 流程出口 	private SequenceFlow outgoFlow; 	 	// 流程入口 	private SequenceFlow incomeFlow; 	 	// 流程节点的行为 	private BehaviorInterface behavior; 	...省略getter和setter方法	 }  public class Start extends FlowNode {  }  public class End extends FlowNode {  }  public class Task extends FlowNode {  }  public class SequenceFlow extends FlowElement { 	 	private String source; 	 	private String target; 	...省略setter和getter方法 } 

        代码清单18-2中的几个类,分别对应流程XML文件中的几个节点,实际上,Activiti也有一套类似的模型,用于表示BPMN规范中的XML元素。注意FlowNode类中维护一个节点行为的对象,该对象在11.3小节讲述。除了以上的几个类外,还要创建表示流程的类与表示执行流的类,如代码清单18-3所示。

        代码清单18-3:

        codes\18\18.1\my-bpmn\src\org\crazyit\activiti\xml\MyProcess.java

        codes\18\18.1\my-bpmn\src\org\crazyit\activiti\MyExecution.java

public class MyProcess extends BaseElement { 	 	// 开始节点 	private Start start; 	 	// 结束节点 	private End end; 	 	// 多个顺序流节点 	private List<SequenceFlow> flows; 	 	// 多个节点 	private List<FlowNode> nodes; 	...省略setter和getter方法 } 

 

public class MyExecution {  	// 执行流的当前节点 	private FlowNode currentNode; 	 	private MyProcess process;  	...省略getter和setter方法  } 

        在MyExecution类中,维护一个当前执行流的节点对象,表示当前执行流所到达的节点。我们定义的流程规范中,只允许有一个开始事件和一个结束事件,允许出现多个顺序流节点和多个流程节点。接下来,为这些流程节点创建它们的行为类。

11.3 创建流程节点行为

        新建一个行为接口,表示流程节点所需要执行的行为,本例中只有两个行为实现:流程开始行为与任务行为,源文件如代码清单18-4所示。

        代码清单18-4:codes\18\18.1\my-bpmn\src\org\crazyit\activiti\behavior

public interface BehaviorInterface {  	/** 	 * 行为执行方法 	 */ 	void execute(MyExecution exe); }  // 开始行为 public class StartBehavior implements BehaviorInterface {  	public void execute(MyExecution exe) { 		System.out.println("执行开始节点"); 		// 获取当前节点 		FlowNode currentNode = exe.getCurrentNode(); 		// 获取顺序流 		SequenceFlow outgoFlow = currentNode.getOutgoFlow(); 		// 设置下一节点 		FlowNode nextNode = exe.getProcess().getNode(outgoFlow.getTarget()); 		exe.setCurrentNode(nextNode); 	} }  // 任务行为 public class TaskBehavior implements BehaviorInterface {  	public void execute(MyExecution exe) { 		System.out.println("执行任务节点"); 		// 获取当前节点 		FlowNode currentNode = exe.getCurrentNode(); 		// 获取顺序流 		SequenceFlow outgoFlow = currentNode.getOutgoFlow(); 		// 获取下一个节点 		FlowNode targetNode = exe.getProcess().getNode(outgoFlow.getTarget()); 		// 设置当前节点 		exe.setCurrentNode(targetNode); 	} } 

        代码清单中的StartBehavior,在流程节点执行时,会自动将当前节点设置为下一个节点。注意获取下一个节点,是通过SequenceFlow对象进行的。在模型中,顺序流是连接两个流程节点的桥梁,因此SequenceFlow知道流程将要往哪里走。

11.4 编写业务处理类

        业务处理类类似Activiti的服务组件,本例的服务组件只提供启动流程、完成任务这两个业务方法,用于观察流程走向。代码清单18-5为本例的服务组件。

        代码清单18-5:

        codes\18\18.1\my-bpmn\src\org\crazyit\activiti\service\MyRuntimeService.java

package org.crazyit.activiti.service;  import org.crazyit.activiti.MyExecution; import org.crazyit.activiti.xml.End; import org.crazyit.activiti.xml.FlowNode; import org.crazyit.activiti.xml.MyProcess; import org.crazyit.activiti.xml.SequenceFlow; import org.crazyit.activiti.xml.Start;  public class MyRuntimeService { 	 	/** 	 * 启动流程的方法 	 */ 	public MyExecution startProcess(MyProcess process) { 		// 创建执行流 		MyExecution exe = new MyExecution(); 		exe.setProcess(process); 		Start startNode = process.getStart(); 		// 设置流程当前节点 		exe.setCurrentNode(startNode); 		// 让流程往前进行 		startNode.getBehavior().execute(exe);		 		return exe; 	} 	 	/** 	 * 完成任务 	 */ 	public void completeTask(MyExecution exe) { 		// 获取当前的流程节点 		FlowNode current = exe.getCurrentNode(); 		// 执行节点的行为 		current.getBehavior().execute(exe);		 	} } 

        开始流程的方法,会直接创建执行流,然后获取开始节点并执行其行为。完成任务的方法,获取当前节点,再执行其行为。前面定义的两个节点行为,均是获取下一个节点,作为执行流的当前节点,即让流程向“前”执行。接下来,需要编写XML解析类,解析定义的XML文档并转换为流程对象。

11.5 流程XML转换为Java对象

        本例为了简单起见,使用了XStream作为工具,将读取的XML文件转换为Java对象,代码清单18-6为XStream工具类。

        代码清单18-6:codes\18\18.1\my-bpmn\src\org\crazyit\activiti\xml\XStreamUtil.java

package org.crazyit.activiti.xml;  import java.io.File; import java.io.FileInputStream;  import com.thoughtworks.xstream.XStream;  public class XStreamUtil {  	private static XStream xstream = new XStream(); 	 	static { 		// 配置XStream 		xstream.alias("process", MyProcess.class); 		xstream.alias("flow", SequenceFlow.class); 		xstream.alias("task", Task.class); 		xstream.alias("start", Start.class); 		xstream.alias("end", End.class); 		xstream.useAttributeFor(BaseElement.class, "id"); 		xstream.useAttributeFor(SequenceFlow.class, "source"); 		xstream.useAttributeFor(SequenceFlow.class, "target"); 	} 	 	// 将XML文件转换为Process实例 	public static MyProcess toObject(File file) { 		try { 			FileInputStream fis = new FileInputStream(file); 			MyProcess p = (MyProcess)xstream.fromXML(fis); 			// 初始化行为与各节点的顺序流 			p.initBehavior(); 			p.initSequenceFlow(); 			fis.close(); 			return p; 		} catch (Exception e) { 			e.printStackTrace(); 			return null; 		} 	} } 

        在工具类的static语句块中,定义了哪个节点转换为哪个Java类。在toObject方法中,将XML转换得到MyProcess实例后,再调用MyProcess类的initBehavior和initSequenceFlow方法,其中initBehavior方法用于初始化节点的行为,initSequenceFlow用于设置流程的执行顺序,两个方法如代码清单18-7所示。

        代码清单18-7:codes\18\18.1\my-bpmn\src\org\crazyit\activiti\xml\MyProcess.java

	/** 	 * 为各节点设置出入的顺序注 	 */ 	public void initSequenceFlow() { 		// 开始事件的顺序流(设置出口) 		this.start.setOutgoFlow(getSequenceFlowBySource(this.start.getId())); 		// 结束事件顺序流(设置入口) 		this.end.setIncomeFlow(getSequenceFlowByTarget(this.end.getId())); 		// 设置其余节点的顺序流 		for(FlowNode node : nodes) { 			for(SequenceFlow flow : flows) { 				if(flow.getSource().equals(node.getId())) { 					node.setOutgoFlow(flow); 				} 				if(flow.getTarget().equals(node.getId())) { 					node.setIncomeFlow(flow); 				} 			} 		} 	} 	 	/** 	 * 初始化节点行为 	 */ 	public void initBehavior() { 		// 开始与结束节点 		this.start.setBehavior(new StartBehavior());		 		for(FlowNode node : nodes) { 			if(node instanceof Task) { 				node.setBehavior(new TaskBehavior()); 			} 		} 	}

        在initSequenceFlow方法中,主要设置各个节点的出入顺序流,开始事件只有出口,不存在上一个节点,结束事件只有入口,不存在下一个节点。最后遍历流程中的其他节点,根据XML中的顺序流来设定顺序流在节点中的出入口。

11.6 编写客户端代码

        与我们前面使用Activiti一样,编写客户端代码,加载我们定义的流程文件,启动流程并且完成任务,如代码清单18-8所示。

        代码清单18-8:codes\18\18.1\my-bpmn\src\org\crazyit\activiti\TestMain.java

package org.crazyit.activiti;  import java.io.File; import java.net.URI;  import org.crazyit.activiti.service.MyRuntimeService; import org.crazyit.activiti.xml.MyProcess; import org.crazyit.activiti.xml.XStreamUtil;   public class TestMain {  	public static void main(String[] args) throws Exception { 		String path = TestMain.class.getResource("/").toString(); 		File xmlFile = new File(new URI(path + "/myBpmn.xml")); 		// 解析流程文件 		MyProcess process = XStreamUtil.toObject(xmlFile); 		// 启动流程 		MyRuntimeService runtimeService = new MyRuntimeService(); 		MyExecution exe = runtimeService.startProcess(process); 		// 查询流程当前节点 		System.out.println("当前流程节点:" + exe.getCurrentNode().getId()); 		// 完成任务 		runtimeService.completeTask(exe); 		System.out.println("当前流程节点:" + exe.getCurrentNode().getId()); 	}  } 

        运行代码清单18-8,输出结果如下:

执行开始节点 当前流程节点:task 执行任务节点 当前流程节点:end

        根据结果可知,开始流程后,当前流程节点到达task,调用服务方法完成任务后,流程到达end。

        本例使用了一个迷你版的流程引擎来讲述Activivit对于流程的控制逻辑,实际中,Activiti在这部分的设计更为复杂。在Activiti5时代,流程虚拟机(PVM)用于定义流程走向,而在Activiti6,流程控制都交由流程元素本身决定。

        本文节选自《疯狂Workflow讲义(第2版)》。

本文发表于2017年10月18日 00:34
(c)注:本文转载自https://my.oschina.net/JavaLaw/blog/1552152,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。如有侵权行为,请联系我们,我们会及时删除.

阅读 2218 讨论 0 喜欢 0

抢先体验

扫码体验
趣味小程序
文字表情生成器

闪念胶囊

你要过得好哇,这样我才能恨你啊,你要是过得不好,我都不知道该恨你还是拥抱你啊。

直抵黄龙府,与诸君痛饮尔。

那时陪伴我的人啊,你们如今在何方。

不出意外的话,我们再也不会见了,祝你前程似锦。

这世界真好,吃野东西也要留出这条命来看看

快捷链接
网站地图
提交友链
Copyright © 2016 - 2021 Cion.
All Rights Reserved.
京ICP备2021004668号-1