本文节选自《疯狂工作流讲义(第2版)》

京东购买地址:https://item.jd.com/12246565.html

工作流Activiti6电子书http://blog.csdn.net/boxiong86/article/details/78488562

工作流Activiti6教学视频:http://blog.csdn.net/boxiong86/article/details/78608585

流程控制逻辑

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

概述

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

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

设计流程对象

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

代码清单18-1codes\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-2codes\18\18.1\my-bpmn\src\org\crazyit\activiti\xml

public class BaseElement {

 

// XML元素的ID

private String id;

...省略settergetter方法

}

 

public class FlowElement extends BaseElement {

 

}

 

public class FlowNode extends FlowElement {

 

//流程出口

private SequenceFlow outgoFlow;

//流程入口

private SequenceFlow incomeFlow;

//流程节点的行为

private BehaviorInterface behavior;

...省略gettersetter方法

}

 

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;

...省略settergetter方法

}

代码清单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;

...省略settergetter方法

}

 

public class MyExecution {

 

//执行流的当前节点

private FlowNode currentNode;

private MyProcess process;

 

...省略gettersetter方法

 

}

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

创建流程节点行为

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

代码清单18-4codes\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知道流程将要往哪里走。

编写业务处理类

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

代码清单18-5codes\18\18.1\my-bpmn\src\org\crazyit\activiti\service\MyRuntimeService.java

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文档并转换为流程对象。

流程XML转换为Java对象

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

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

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类的initBehaviorinitSequenceFlow方法,其中initBehavior方法用于初始化节点的行为,initSequenceFlow用于设置流程的执行顺序,两个方法如代码清单18-7所示。

代码清单18-7codes\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中的顺序流来设定顺序流在节点的出入口。

编写客户端代码

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

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

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流程控制都交由流程元素本身决定

 本文节选自《疯狂工作流讲义(第2版)》

京东购买地址:https://item.jd.com/12246565.html

工作流Activiti6电子书http://blog.csdn.net/boxiong86/article/details/78488562

工作流Activiti6教学视频:http://blog.csdn.net/boxiong86/article/details/78608585

本书代码共享地址:https://gitee.com/yangenxiong/CrazyActiviti

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐