Activiti 简介
1. 概述
Activiti API 是一个工作流和业务流程管理系统。我们可以在其中定义一个流程,执行它,并使用 API 提供的服务以不同的方式对其进行操作。它需要JDK 7+。
可以在任何 IDE 中使用 API 进行开发,但是要使用Activiti Designer ,我们需要 Eclipse。
我们可以使用 BPMN 2.0 标准在其中定义一个流程。还有另一种不太流行的方法——使用 Java 类,如StartEvent、EndEvent、UserTask、SequenceFlow等。
如果我们想运行一个流程或访问任何服务,我们需要创建一个ProcessEngineConfiguration。
在某些方面,我们可以使用ProcessEngineConfiguration获取ProcessEngine,我们将在本文中进一步讨论。通过ProcessEngine我们可以执行Workflow 和 BPMN 操作。
2.Maven依赖
要使用这个 API,我们需要包含 Activiti 依赖:
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-engine</artifactId>
</dependency>
3. 创建ProcessEngine
Activiti 中的ProcessEngine通常使用 XML 文件activiti.cfg.xml进行配置。此配置文件的一个示例是:
<beans xmlns="...">
<bean id="processEngineConfiguration" class=
"org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
<property name="jdbcUrl"
value="jdbc:h2:mem:activiti;DB_CLOSE_DELAY=1000" />
<property name="jdbcDriver" value="org.h2.Driver" />
<property name="jdbcUsername" value="root" />
<property name="jdbcPassword" value="" />
<property name="databaseSchemaUpdate" value="true" />
</bean>
</beans>
现在我们可以使用ProcessEngines类获取ProcessEngine:
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
该语句将查找activiti.cfg。在classpath中添加一个xml文件,并根据文件中的配置构造一个ProcessEngine。
配置文件的示例代码表明它只是一个基于 Spring 的配置。但是,这并不意味着我们只能在 Spring 环境中使用 Activiti。Spring 的功能只是在内部用于创建ProcessEngine。
让我们编写一个 JUnit 测试用例,它将使用上面显示的配置文件创建ProcessEngine:
@Test
public void givenXMLConfig_whenGetDefault_thenGotProcessEngine() {
ProcessEngine processEngine
= ProcessEngines.getDefaultProcessEngine();
assertNotNull(processEngine);
assertEquals("root", processEngine.getProcessEngineConfiguration()
.getJdbcUsername());
}
4. Activiti 流程引擎 API 和服务
与 API 交互的入口点是ProcessEngine。通过ProcessEngine,我们可以访问各种提供工作流/BPMN 方法的服务。ProcessEngine和所有服务对象都是线程安全的。
取自 https://www.activiti.org/userguide/images/api.services.png
ProcessEngines类将扫描activiti.cfg.xml和activiti-context.xml文件。如前所述,对于所有的activiti.cfg.xml文件,ProcessEngine将以典型的方式创建。
然而,对于所有activiti-context.xml文件,它将以 Spring 方式创建——我将创建 Spring Application Context 并从中获取ProcessEngine。在流程执行期间,将按照 BPMN 文件中定义的顺序访问所有步骤。
在流程执行期间,将按照 BPMN 文件中定义的顺序访问所有步骤。
4.1. 流程定义和相关术语
**ProcessDefinition代表一个业务流程。**它用于定义流程中不同步骤的结构和行为。部署流程定义意味着将流程定义加载到 Activiti 数据库中。
流程定义主要由 BPMN 2.0 标准定义。也可以使用 Java 代码定义它们。本节中定义的所有术语也可以作为 Java 类使用。
一旦我们开始运行一个流程定义,它就可以被称为一个流程
ProcessInstance是ProcessDefinition的一种执行。**
StartEvent与每个业务流程相关联。它表示进程的入口点。**类似地,有一个EndEvent指示进程的结束。我们可以定义这些事件的条件。
开始和结束之间的所有步骤(或元素)都称为任务。任务可以是各种类型。最常用的任务是UserTasks和ServiceTasks。
顾名思义, UserTasks需要用户手动执行。
另一方面,ServiceTasks是用一段代码配置的。每当执行到达他们时,他们的代码块就会被执行。
SequenceFlows连接Tasks。我们可以通过它们将连接的源元素和目标元素来定义SequenceFlows。同样,我们还可以在SequenceFlows上定义条件以在流程中创建条件路径。
4.2. 服务
我们将简要讨论 Activiti 提供的服务:
- RepositoryService帮助我们操纵流程定义的部署。该服务处理与流程定义相关的静态数据
- RuntimeService管理ProcessInstances(当前正在运行的进程)以及进程变量
- TaskService跟踪UserTasks。需要用户手动执行的Task是 Activiti API 的核心。我们可以使用此服务创建任务、声明并完成任务、操作任务的受让人等
- FormService是一项可选服务。API 可以在没有它的情况下使用,并且不会牺牲它的任何特性。它用于定义流程中的开始形式和任务形式。
- IdentityService管理User和Group
- HistoryService跟踪 Activiti Engine 的历史。我们还可以设置不同的历史级别。
- ManagementService与元数据相关,通常在创建应用程序时不需要
- DynamicBpmnService帮助我们更改流程中的任何内容,而无需重新部署它
5. 使用 Activiti 服务
为了了解我们如何使用不同的服务并运行一个流程,让我们以“员工休假请求”的流程为例:
该流程的 BPMN 2.0 文件VacationRequest.bpmn20.xml将开始事件定义为:
<startEvent id="startEvent" name="request"
activiti:initiator="employeeName">
<extensionElements>
<activiti:formProperty id="numberOfDays"
name="Number of days" type="long" required="true"/>
<activiti:formProperty id="startDate"
name="Vacation start date (MM-dd-yyyy)" type="date"
datePattern="MM-dd-yyyy hh:mm" required="true"/>
<activiti:formProperty id="reason" name="Reason for leave"
type="string"/>
</extensionElements>
</startEvent>
同样,分配给用户组“管理”的第一个用户任务将如下所示:
<userTask id="handle_vacation_request" name=
"Handle Request for Vacation">
<documentation>${employeeName} would like to take ${numberOfDays} day(s)
of vacation (Motivation: ${reason}).</documentation>
<extensionElements>
<activiti:formProperty id="vacationApproved" name="Do you approve
this vacation request?" type="enum" required="true"/>
<activiti:formProperty id="comments" name="Comments from Manager"
type="string"/>
</extensionElements>
<potentialOwner>
<resourceAssignmentExpression>
<formalExpression>management</formalExpression>
</resourceAssignmentExpression>
</potentialOwner>
</userTask>
使用ServiceTask,我们需要定义要执行的代码。我们将这段代码作为 Java 类:
<serviceTask id="send-email-confirmation" name="Send email confirmation"
activiti:class=
"com.example.activiti.servicetasks.SendEmailServiceTask.java">
</serviceTask>
条件流将通过在“sequenceFlow”中添加“conditionExpression”标签来显示:
<sequenceFlow id="flow3" name="approved"
sourceRef="sid-12A577AE-5227-4918-8DE1-DC077D70967C"
targetRef="send-email-confirmation">
<conditionExpression xsi:type="tFormalExpression">
<![CDATA[${vacationApproved == 'true'}]]>
</conditionExpression>
</sequenceFlow>
在这里,vacationApproved是上面显示的UserTask的formProperty。
正如我们在图中看到的,这是一个非常简单的过程。员工提出休假请求,提供休假天数和开始日期。请求发送给经理。他们可以批准/拒绝该请求。
如果获得批准,则会定义一个服务任务来发送确认电子邮件。如果未获批准,员工可以选择修改并重新发送请求,或者什么也不做。
为服务任务提供了一些要执行的代码(这里,作为 Java 类)。我们已经给出了SendEmailServiceTask.java类。
这些类型的类应该扩展JavaDelegate。另外,我们需要重写它的*execute()*方法,这个方法会在流程执行到这一步时执行。
5.1. 部署流程
为了让 Activiti 引擎知道我们的流程,我们需要部署流程。我们可以使用RepositoryService 以编程方式完成。让我们编写一个 JUnit 测试来展示这一点:
@Test
public void givenBPMN_whenDeployProcess_thenDeployed() {
ProcessEngine processEngine
= ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService
= processEngine.getRepositoryService();
repositoryService.createDeployment()
.addClasspathResource(
"org/activiti/test/vacationRequest.bpmn20.xml")
.deploy();
Long count=repositoryService.createProcessDefinitionQuery().count();
assertEquals("1", count.toString());
}
部署意味着引擎将解析 BPMN 文件并将其转换为可执行文件。此外,每次部署都会在存储库表中添加一条记录。 因此,之后我们可以查询RuntimeService服务以获取已部署的进程。
5.2. 启动ProcessInstance
将ProcessDefinition部署到 Activiti Engine 后,我们可以通过创建ProcessInstances来执行流程。ProcessDefinition是一个蓝图,ProcessInstance是它的运行时执行。
对于单个ProcessDefinition,可以有多个ProcessInstances。
可以通过RuntimeService访问与ProcessInstances相关的所有详细信息。
在我们的示例中,在开始事件中,我们需要传递休假天数、开始日期和原因。我们将使用流程变量,并在创建ProcessInstance 时传递它们。
让我们编写一个 JUnit 测试用例以获得更好的想法:
@Test
public void givenDeployedProcess_whenStartProcessInstance_thenRunning() {
//deploy the process definition
Map<String, Object> variables = new HashMap>();
variables.put("employeeName", "John");
variables.put("numberOfDays", 4);
variables.put("vacationMotivation", "I need a break!");
RuntimeService runtimeService = processEngine.getRuntimeService();
ProcessInstance processInstance = runtimeService
.startProcessInstanceByKey("vacationRequest", variables);
Long count=runtimeService.createProcessInstanceQuery().count();
assertEquals("1", count.toString());
}
单个流程定义的多个实例将因流程变量而异。
有多种方法可以启动流程实例。在这里,我们使用的是进程的密钥。启动流程实例后,我们可以通过查询RuntimeService来获取它的相关信息。
5.3. 完成任务
当我们的流程实例开始运行时,第一步是一个用户任务,分配给用户组“management”。
用户可能有一个收件箱,其中包含他们要完成的任务列表。现在,如果我们想继续流程执行,用户需要完成这个任务。对于 Activiti Engine,它被称为“完成任务”。
我们可以查询TaskService,获取任务对象,然后完成它。
我们需要为此编写的代码如下所示:
@Test
public void givenProcessInstance_whenCompleteTask_thenGotNextTask() {
// deploy process and start process instance
TaskService taskService = processEngine.getTaskService();
List<Task> tasks = taskService.createTaskQuery()
.taskCandidateGroup("management").list();
Task task = tasks.get(0);
Map<String, Object> taskVariables = new HashMap<>();
taskVariables.put("vacationApproved", "false");
taskVariables.put("comments", "We have a tight deadline!");
taskService.complete(task.getId(), taskVariables);
Task currentTask = taskService.createTaskQuery()
.taskName("Modify vacation request").singleResult();
assertNotNull(currentTask);
}
请注意,TaskService的*complete()*方法也接受所需的流程变量。我们传递了经理的回复。
在此之后,流程引擎将继续进行下一步。在这里,下一步询问员工是否要重新发送休假请求。
因此,我们的ProcessInstance现在正在等待这个名为“Modify vacation request”的UserTask。
5.4. 暂停和激活进程
我们可以暂停ProcessDefinition和ProcessInstance。如果我们挂起一个ProcessDefinition,我们就不能在它挂起时创建它的实例。我们可以使用RepositoryService 做到这一点:
@Test(expected = ActivitiException.class)
public void givenDeployedProcess_whenSuspend_thenNoProcessInstance() {
// deploy the process definition
repositoryService.suspendProcessDefinitionByKey("vacationRequest");
runtimeService.startProcessInstanceByKey("vacationRequest");
}
要再次激活它,我们只需要调用repositoryService.activateProcessDefinitionXXX方法之一。
同样,我们可以使用RuntimeService挂起ProcessInstance。