Activiti - 使用基础
本文旨在介绍工作流概念、BPMN语言/标准、Activiti7引擎使用流程等工作流业务开发所涉及的主要内容。
本文介绍的Activiti引擎版本系列为Activiti 7系列。
1. 工作流
工作流可以被看做是一种抽象出来的业务流程。
比如,某公司中员工的请假申请需要先经过经理审批同意,然后再经过总经理审批同意。用流程图来表示则类似下图:
很多业务的流程都类似这种请假申请,它们有着比较明确的推进流程和顺序,因此可以用流程图的形式表示出来。
这种流式状态的业务模型就被称为工作流。
2. 为什么要使用工作流引擎
对于上一部分的请假申请业务,使用编程语言手动实现这样的流程是有可能的。但是随着业务越来越复杂,靠人力去维护这些代码的底层逻辑是越来越困难且效率较低的。而人们也发现,手动实现工作流的过程中有许多共同的步骤,所以诞生了工作流引擎这种框架,让计算机根据流程图内在的逻辑自动进行处理和维护等。
工作流引擎提取出了工作流模型中的共性,如流程的推进方式、任务节点的处理方法、流程图的实现方法等。通过使用工作流引擎,可以实现工作流程的自动化,提高业务处理的效率和灵活性。
3. BPMN语言
上一部分中我们使用工作流引擎来处理各种业务模型。
但是计算机并不能直接识别图片形式的流程图,因此协会BPMI(业务流程管理协会,The Business Process Management Initiative)开发了一套业务流程建模符号,名为BPMN(Business Process Modeling Notation)。
使用BPMN语言,可以让全世界的开发者形成统一的开发标准。这也促进了工作流引擎的发展。
现在使用的BPMN语言标准是由OMG(对象管理组织,Object Management Group)推出的BPMN2.0标准。
可以使用Camunda Modeler或者Activiti Designer等BPMN绘制工具来图形化地绘制一个BPMN流程图,形成的文件扩展名为.bpmn。这种文件本质上是一种XML文件,以XML的形式描述了流程图的内容。
例如,第一部分中的请假申请流程图文件,用编辑器打开的形式如下:
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" id="Definitions_1ijy1ka" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="3.0.0">
<bpmn:process id="myEvection" name="出差申请" isExecutable="true">
<bpmn:startEvent id="StartEvent_1" name="开始">
<bpmn:outgoing>SequenceFlow_0j491nw</bpmn:outgoing>
</bpmn:startEvent>
<bpmn:userTask id="Task_1bdlgip" name="创建出差申请" camunda:assignee="zhangsan">
<bpmn:incoming>SequenceFlow_0j491nw</bpmn:incoming>
<bpmn:outgoing>SequenceFlow_0yu6chk</bpmn:outgoing>
</bpmn:userTask>
<bpmn:userTask id="Task_0n2xq2w" name="经理审批" camunda:assignee="jerry">
<bpmn:incoming>SequenceFlow_0yu6chk</bpmn:incoming>
<bpmn:outgoing>SequenceFlow_1kjbgj7</bpmn:outgoing>
</bpmn:userTask>
<bpmn:userTask id="Task_01dgw6j" name="总经理审批" camunda:assignee="jack">
<bpmn:incoming>SequenceFlow_1kjbgj7</bpmn:incoming>
<bpmn:outgoing>SequenceFlow_0a8p3wb</bpmn:outgoing>
</bpmn:userTask>
<bpmn:endEvent id="EndEvent_1e2pbqb" name="结束">
<bpmn:incoming>SequenceFlow_0a8p3wb</bpmn:incoming>
</bpmn:endEvent>
<bpmn:sequenceFlow id="SequenceFlow_0j491nw" sourceRef="StartEvent_1" targetRef="Task_1bdlgip" />
<bpmn:sequenceFlow id="SequenceFlow_0yu6chk" sourceRef="Task_1bdlgip" targetRef="Task_0n2xq2w" />
<bpmn:sequenceFlow id="SequenceFlow_1kjbgj7" sourceRef="Task_0n2xq2w" targetRef="Task_01dgw6j" />
<bpmn:sequenceFlow id="SequenceFlow_0a8p3wb" sourceRef="Task_01dgw6j" targetRef="EndEvent_1e2pbqb" />
</bpmn:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="myEvection">
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
<dc:Bounds x="156" y="103" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="163" y="146" width="22" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="UserTask_0sb3vlg_di" bpmnElement="Task_1bdlgip">
<dc:Bounds x="250" y="81" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="UserTask_1ls6slz_di" bpmnElement="Task_0n2xq2w">
<dc:Bounds x="426" y="81" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="UserTask_0hbw9zo_di" bpmnElement="Task_01dgw6j">
<dc:Bounds x="608" y="81" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="EndEvent_1e2pbqb_di" bpmnElement="EndEvent_1e2pbqb">
<dc:Bounds x="793" y="103" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="800" y="146" width="22" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="SequenceFlow_0j491nw_di" bpmnElement="SequenceFlow_0j491nw">
<di:waypoint x="192" y="121" />
<di:waypoint x="250" y="121" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="SequenceFlow_0yu6chk_di" bpmnElement="SequenceFlow_0yu6chk">
<di:waypoint x="350" y="121" />
<di:waypoint x="426" y="121" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="SequenceFlow_1kjbgj7_di" bpmnElement="SequenceFlow_1kjbgj7">
<di:waypoint x="526" y="121" />
<di:waypoint x="608" y="121" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="SequenceFlow_0a8p3wb_di" bpmnElement="SequenceFlow_0a8p3wb">
<di:waypoint x="708" y="121" />
<di:waypoint x="793" y="121" />
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn:definitions>
其中,有许多内容是可以在BPMN绘制工具中修改的,如部分id、名称等,它们也将作为流程定义的一部分被Activiti等工作流引擎识别。
4. Activiti引擎简介&使用流程概述
Activiti是目前最常用的工作流引擎。它能够识别BPMN语言绘制的流程图,并提供了许多服务和接口对流程进行管理,如流程定义的部署、流程实例的创建、流程任务的管理等。
服务Service
Activiti提供了许多服务接口,用于进行工作流的执行和管理。通常而言,我们通过这些接口对Activiti的数据库进行操作,而不是直接操作或查询其中某个表的内容。
主要使用的服务如下:
服务名称 | 功能 |
---|---|
RepositoryService | Activiti的资源管理类 |
RuntimeService | Activiti的流程运行管理类 |
TaskService | Activiti的任务管理类 |
HistoryService | Activiti的历史管理类 |
ManagementService | Activiti的引擎管理类 |
FormService | Activiti的表单管理类 |
本文中使用的Activiti版本为7.0.0,即Activiti7系列。
下面列举了Activiti基本的工作流程,并标记出了需要调用的主要服务。
(1) 准备开发环境
- JDK1.8 或更新的版本
- MySQL 5 或更新的版本
- Tomcat8.5 或更新的版本
- Camunda Modeler或者Activiti Designer等流程设计工具(用于绘制BPMN流程图)
注意,Activiti需要连接数据库来使用,需要在activiti.cfg.xml文件中编写服务器的连接信息。
(2) 调用引擎和服务
接下来的使用流程都需要使用Activiti提供的各种服务,这些服务需要首先调用Activiti的流程引擎。
通常情况下,调用Activiti的默认流程引擎方式为:
// 创建Activiti工作所需的环境
@Test
public void testCreateDbTable() {
// 使用classpath下的activiti.cfg.xml中的配置创建processEngine
// 获取默认的流程引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 输出引擎信息
System.out.println(processEngine);
}
同时,Activiti的各种操作都需要在数据库中进行,初次调用引擎时,Activiti(版本为Activiti7)会在指定的数据库中对所有表进行更新操作。如果表不存在,则自动创建共25张运行所需的表。
但是如果在activiti.cfg.xml配置文件中将<property name="databaseSchemaUpdate" value="true"/>
项的value值置为false,那么Activiti就不会自动创建环境。此时如果没有表或者版本不匹配,将抛出异常。
获取引擎之后,就可以从引擎中调用各种服务,比如调用Repository Service的方法如下:
// 调用RepositoryService
@Test
public void testGetRepositoryService()
{
// 获取流程引擎ProcessEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 获取RepositoryService
RepositoryService repositoryService = processEngine.getRepositoryService();
// 然后还可以调用服务中的接口,例如:
// Deployment deploy = repositoryService.createDeployment()
// ...
}
其他服务调用方法类似。
(3) 部署流程定义
流程定义:指某个流程的整体,可以理解为一个类。在请假申请的例子中,流程定义就是指流程图中表示的整个流程本身。而在Activiti引擎中,流程定义则可以理解为bpmn文件上传后解析出的流程信息。
绘制好BPMN文件并导出png图片之后,需要使用流程部署操作将其上传到Activiti数据库中,并解析生成各种运行所需的内容,如流程定义等。
这部分内容需要使用RepositoryService。
(4) 启动流程实例
当流程定义部署完成后,就可以启动一个流程实例了。
流程实例:指某个流程定义上运行的实例,可以理解为一个对象。比如,对于请假申请而言,发起一次请假申请,就相当于在该流程定义上启动一个实例。
这部分内容需要使用RuntimeService。
(5) 任务查询
流程实例启动之后,会自动按照顺序推进。从业务逻辑上讲,此时应该轮到负责人进行审批等操作。而负责人得知自己的待办任务的方式,就是通过任务查询来实现。
比如,通过任务的负责人名称查询到自己负责的节点上的待办任务,返回任务节点的id等信息。
这部分内容需要使用TaskService。
(6) 任务处理
查询到自己负责的任务之后,负责人可以通过调用TaskService中的一些接口对任务进行处理。
比如,负责人通过完成任务使得流程实例进入到下一个任务节点。
这部分内容需要使用TaskService。
(7) 查询流程定义的信息
有些时候,为了查询某个流程相关的信息,我们需要对流程定义进行查询。
比如,通过流程定义的Key(即BPMN文件中的id属性)查询到该流程最新的版本号。
这部分内容需要使用RepositoryService。
(8) 删除流程
当某个流程废弃的时候,就可以使用RepositoryService中的删除功能将该流程的定义和部署删除掉。
然而,如果将要被删除的流程定义上仍有实例正在运行,那么一般的删除方法会返回错误。这时候就需要在调用删除方法的时候加入另外的参数,实现级联删除,即实例和流程定义与部署一同删除。
注:无论使用这两种方法中的哪一种,都不会影响到HistoryService管辖的历史记录。
这部分内容需要使用RepositoryService。
(9) 查询流程历史信息
流程相关的历史数据会在流程实例的运行过程中被记录下来,如历史流程实例、历史活动和历史流程任务等。
曾经执行的或者正在执行的流程实例会被记录并存放在历史数据表中。通过流程实例的id或者名称等信息就可以查询到表中该流程实例相关的信息。
这部分内容需要使用HistoryService。
致谢
非常感谢各位阅读我的博客,也希望自己的一点拙见能够帮到各位。
转载请标注转自千寒的CSDN博客,谢谢!