BPMN 2.0介绍
定义流程
创建一个新的XML文件并为其命名,确保文件以.bpmn20.xml或.bpmn结尾,否则引擎将不会选择该文件进行部署。
BPMN 2.0模式的根元素是definitions
元素,在此元素内,可以定义多个流程定义(尽管建议在每个文件中只有一个流程定义,因为这样可以简化开发流程的后期维护)。空的流程定义如下所示,请注意,最少definitions
元素仅需要xmlns
和targetNamespace
声明,targetNamespace
可以是任何东西,对于对流程定义进行分类很有用。
<definitions
xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:activiti="http://activiti.org/bpmn"
targetNamespace="Examples">
<process id="myProcess" name="My First Process">
..
</process>
</definitions>
process
元素具有两个属性:
-
id:此属性是必需的,并且映射到Activiti
ProcessDefinition
对象的key属性,然后,可以通过RuntimeService
上的startProcessInstanceByKey
方法,使用此id来启动流程定义的新流程实例,此方法将始终采用流程定义的最新部署版本。
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("myProcess");
- 这里要注意的重要一点是,这与调用
startProcessInstanceById
方法不同,此方法期望Activiti引擎在部署时生成的字符串id,可以通过调用processDefinition.getId()
方法进行检索。生成的id的格式为key:version,并且长度限制为64个字符。如果你收到一个ActivitiException声明生成的ID太长,请限制该流程的键字段中的文本。 -
name:此属性是可选的,并且映射到
ProcessDefinition
的name属性,引擎本身不使用此属性,因此,例如,它可用于在用户界面中显示更人性化的名称。
入门:10分钟的教程
在本节中,我们将介绍一个(非常简单的)业务流程,我们将使用它介绍一些基本的Activiti概念和Activiti API。
前提条件
本教程假定你正在运行Activiti演示安装程序,并且你正在使用独立的H2服务器,编辑db.properties
并设置jdbc.url=jdbc:h2:tcp://localhost/activiti
,然后根据H2的文档运行独立服务器。
目标
本教程的目的是学习Activiti和一些基本的BPMN 2.0概念,最终结果将是一个简单的Java SE程序,该程序将部署流程定义,并通过Activiti引擎API与该流程进行交互。还将介绍Activiti周围的一些工具,当然,在围绕业务流程构建自己的Web应用程序时,也可以使用本教程中学习的内容。
用例
用例很简单:我们有一家公司,我们称之为BPMCorp。在BPMCorp中,每月需要为公司股东编写财务报告,这是会计部门的责任,报告完成后,高层管理人员之一需要批准该文件,然后再将其发送给所有股东。
流程图
可以使用Activiti Designer以图形方式显示上述业务流程,但是,在本教程中,我们将自己键入XML,因为我们在这一点上学到的最多,我们的流程的图形化BPMN 2.0表示法如下所示:
我们看到的是一个none Start Event(左侧的圆圈),后面是两个用户任务:“Write monthly financial report”和“Verify monthly financial report”,以none end event(右侧带有粗边框的圆圈)结尾。
XML表示
该业务流程的XML版本(FinancialReportProcess.bpmn20.xml)如下所示:
- (none) start event告诉我们该流程的切入点是什么。
- 用户任务声明是我们流程中人工任务的表示,请注意,第一个任务分配给会计组,而第二个任务分配给管理组。
- 当到达none end event时,该流程结束。
- 元素通过顺序流相互连接,这些顺序流具有源和目标,它们定义了顺序流的方向。
<definitions id="definitions"
targetNamespace="http://activiti.org/bpmn20"
xmlns:activiti="http://activiti.org/bpmn"
xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL">
<process id="financialReport" name="Monthly financial report reminder process">
<startEvent id="theStart" />
<sequenceFlow id="flow1" sourceRef="theStart" targetRef="writeReportTask" />
<userTask id="writeReportTask" name="Write monthly financial report" >
<documentation>
Write monthly financial report for publication to shareholders.
</documentation>
<potentialOwner>
<resourceAssignmentExpression>
<formalExpression>accountancy</formalExpression>
</resourceAssignmentExpression>
</potentialOwner>
</userTask>
<sequenceFlow id="flow2" sourceRef="writeReportTask" targetRef="verifyReportTask" />
<userTask id="verifyReportTask" name="Verify monthly financial report" >
<documentation>
Verify monthly financial report composed by the accountancy department.
This financial report is going to be sent to all the company shareholders.
</documentation>
<potentialOwner>
<resourceAssignmentExpression>
<formalExpression>management</formalExpression>
</resourceAssignmentExpression>
</potentialOwner>
</userTask>
<sequenceFlow id="flow3" sourceRef="verifyReportTask" targetRef="theEnd" />
<endEvent id="theEnd" />
</process>
</definitions>
启动流程实例
现在,我们已经创建了业务流程的流程定义,通过这样的流程定义,我们可以创建流程实例,在这种情况下,一个流程实例将与特定月份的单个财务报告的创建和验证相匹配,所有流程实例共享相同的流程定义。
为了能够根据给定的流程定义创建流程实例,我们必须首先部署该流程定义,部署流程定义意味着两件事:
- 流程定义将存储在为你的Activiti引擎配置的持久性数据存储中,因此,通过部署业务流程,我们确保引擎重新启动后,引擎将找到流程定义。
- BPMN 2.0流程文件将解析为内存中的对象模型,可以通过Activiti API对其进行操作。
部署可以通过多种方式进行,一种方法是通过以下API,请注意,与Activiti引擎的所有交互都是通过其服务进行的。
Deployment deployment = repositoryService.createDeployment()
.addClasspathResource("FinancialReportProcess.bpmn20.xml")
.deploy();
现在,我们可以使用在流程定义中定义的id
(请参阅XML文件中的process元素)来启动新流程实例,请注意,Activiti术语中的此id
称为key。
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("financialReport");
这将创建一个流程实例,该实例将首先经历启动事件,启动事件之后,它遵循所有输出顺序流(在这种情况下只有一个),并且到达了第一个任务(写每月财务报告),Activiti引擎现在将在持久数据库中存储任务,此时,将解决任务中附加的用户或组分配,并将其存储在数据库中,重要的是要注意,Activiti引擎将继续执行流程,直到达到等待状态(例如用户任务)为止。在这种等待状态下,流程实例的当前状态存储在数据库中,它将保持该状态,直到用户决定完成其任务为止,届时,引擎将继续运行直到达到新的等待状态或流程结束,同时,如果引擎重新启动或崩溃,则该流程的状态是安全的,并且在数据库中也处于良好状态。
创建任务后,由于用户任务活动处于等待状态,因此startProcessInstanceByKey
方法将返回。在这种情况下,将任务分配给一个组,这意味着该组中的每个成员都是执行任务的候选人。
现在,我们可以将所有内容放在一起,并创建一个简单的Java程序,创建一个新的Eclipse项目,并将Activiti JAR和依赖项添加到其类路径(可以在Activiti发行版的libs文件夹中找到它们)。在调用Activiti服务之前,我们必须首先构造一个ProcessEngine
,使我们能够访问这些服务。在这里,我们使用“standalone”配置,该配置构造一个ProcessEngine
,该ProcessEngine
使用也是演示设置中使用的数据库。
public static void main(String[] args) {
// Create Activiti process engine
ProcessEngine processEngine = ProcessEngineConfiguration
.createStandaloneProcessEngineConfiguration()
.buildProcessEngine();
// Get Activiti services
RepositoryService repositoryService = processEngine.getRepositoryService();
RuntimeService runtimeService = processEngine.getRuntimeService();
// Deploy the process definition
repositoryService.createDeployment()
.addClasspathResource("FinancialReportProcess.bpmn20.xml")
.deploy();
// Start a process instance
runtimeService.startProcessInstanceByKey("financialReport");
}
任务列表
现在,我们可以通过添加以下逻辑通过TaskService
检索此任务:
List<Task> tasks = taskService.createTaskQuery().taskCandidateUser("kermit").list();
请注意,我们传递给此操作的用户必须是accountancy的成员,因为它是在流程定义中声明的:
<potentialOwner>
<resourceAssignmentExpression>
<formalExpression>accountancy</formalExpression>
</resourceAssignmentExpression>
</potentialOwner>
我们还可以使用任务查询API,通过组名获得相同的结果,现在,我们可以在代码中添加以下逻辑:
TaskService taskService = processEngine.getTaskService();
List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup("accountancy").list();
领取任务
会计现在需要申领任务,通过领取任务,特定用户将成为任务的受让人,并且该任务将从accountancy组其他成员的每个任务列表中消失,领取任务是通过编程完成的,如下所示:
taskService.claim(task.getId(), "fozzie");
现在,该任务已在领取该任务的个人任务列表中。
List<Task> tasks = taskService.createTaskQuery().taskAssignee("fozzie").list();
完成任务
会计现在可以开始处理财务报告了,报告完成后,他就可以完成任务,这意味着该任务的所有工作都已完成。
taskService.complete(task.getId());
对于Activiti引擎,这是一个外部信号,表明必须继续执行流程实例,任务本身已从运行时数据中删除,接下来是任务的单个传出过渡,将执行移至第二个任务(“verification of the report”)。现在将使用与针对第一个任务所述的相同机制来分配第二个任务,所不同的只是将任务分配给管理组。
结束流程
可以按照与以前完全相同的方式检索并领取任务,完成第二个任务会将流程执行移至结束事件,从而结束流程实例,流程实例和所有相关的运行时执行数据将从数据存储中删除。
通过编程,你还可以使用historyService
验证该流程是否已结束:
HistoryService historyService = processEngine.getHistoryService();
HistoricProcessInstance historicProcessInstance =
historyService.createHistoricProcessInstanceQuery().processInstanceId(procId).singleResult();
System.out.println("Process instance end time: " + historicProcessInstance.getEndTime());
代码概述
合并之前各节中的所有代码片段,你应该具有类似的内容:
public class TenMinuteTutorial {
public static void main(String[] args) {
// Create Activiti process engine
ProcessEngine processEngine = ProcessEngineConfiguration
.createStandaloneProcessEngineConfiguration()
.buildProcessEngine();
// Get Activiti services
RepositoryService repositoryService = processEngine.getRepositoryService();
RuntimeService runtimeService = processEngine.getRuntimeService();
// Deploy the process definition
repositoryService.createDeployment()
.addClasspathResource("FinancialReportProcess.bpmn20.xml")
.deploy();
// Start a process instance
String procId = runtimeService.startProcessInstanceByKey("financialReport").getId();
// Get the first task
TaskService taskService = processEngine.getTaskService();
List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup("accountancy").list();
for (Task task : tasks) {
System.out.println("Following task is available for accountancy group: " + task.getName());
// claim it
taskService.claim(task.getId(), "fozzie");
}
// Verify Fozzie can now retrieve the task
tasks = taskService.createTaskQuery().taskAssignee("fozzie").list();
for (Task task : tasks) {
System.out.println("Task for fozzie: " + task.getName());
// Complete the task
taskService.complete(task.getId());
}
System.out.println("Number of tasks for fozzie: "
+ taskService.createTaskQuery().taskAssignee("fozzie").count());
// Retrieve and claim the second task
tasks = taskService.createTaskQuery().taskCandidateGroup("management").list();
for (Task task : tasks) {
System.out.println("Following task is available for management group: " + task.getName());
taskService.claim(task.getId(), "kermit");
}
// Completing the second task ends the process
for (Task task : tasks) {
taskService.complete(task.getId());
}
// verify that the process is actually finished
HistoryService historyService = processEngine.getHistoryService();
HistoricProcessInstance historicProcessInstance =
historyService.createHistoricProcessInstanceQuery().processInstanceId(procId).singleResult();
System.out.println("Process instance end time: " + historicProcessInstance.getEndTime());
}
}
进一步增强
很容易看出,这个业务流程太简单了,无法在现实中使用。但是,当你遍历Activiti中可用的BPMN 2.0构造时,你将能够通过以下方式增强业务流程:
- 定义充当决策的网关,这样,经理可以拒绝财务报告,将为会计重新创建财务报告的任务。
- 声明和使用变量,以便我们可以存储或引用报告,以便可以在表单中将其可视化。
- 在流程结束时定义服务任务,将报告发送给每个股东。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。