1、添加flowable引擎依赖和数据库依赖
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-engine</artifactId>
<version>...</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId
<version>....</version>
</dependency>
2、实例化一个ProcessEngine实例
默认场景:引入flowable-spring-boot-starter 依赖+ 配置文件 -> 直接注入服务
高级场景:通过自定义配置类来创建bean对象:
@Configuration
public class FlowableConfig {
@Bean
public ProcessEngineConfiguration processEngineConfiguration(DataSource dataSource, PlatformTransactionManager transactionManager) {
SpringProcessEngineConfiguration config = new SpringProcessEngineConfiguration();
config.setDataSource(dataSource);
config.setTransactionManager(transactionManager);
config.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
config.setAsyncExecutorActivate(true); // 启用异步执行器
return config;
}
@Bean
public ProcessEngine processEngine(ProcessEngineConfiguration config) {
return config.buildProcessEngine();
}
// 其他服务(如RuntimeService)会自动从ProcessEngine中暴露为Bean
}
3、部署流程定义:
这里构建一个休假流程,flowable引擎使用BPMN2.0格式定义流程。从流程定义中,可以启动许多流程实例。将流程定义视为许多流程执行的蓝图。在这个特定的例子中,流程定义定义了请假所涉及的不同步骤,而一个流程实例匹配一个特定员工的休假请求。
BPMN 2.0 以 XML 格式存储,但它也有可视化部分:它以标准方式定义了如何表示每种不同的步骤类型(人工任务、自动服务调用等)以及如何将这些不同的步骤相互连接。
使用的流程定义如下:
我们假设流程是通过提供一些信息来启动的,比如员工姓名、请假天数和描述。
下面是与上图对应的 BPMN 2.0 XML。将以下 XML 保存在 src/main/resources 文件夹中名为 holiday-request.bpmn20.xml 的文件中。
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC"
xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI"
xmlns:flowable="http://flowable.org/bpmn"
typeLanguage="http://www.w3.org/2001/XMLSchema"
expressionLanguage="http://www.w3.org/1999/XPath"
targetNamespace="http://www.flowable.org/processdef">
<process id="holidayRequest" name="Holiday Request" isExecutable="true">
<startEvent id="startEvent"/>
<sequenceFlow sourceRef="startEvent" targetRef="approveTask"/>
<userTask id="approveTask" name="Approve or reject request"/>
<sequenceFlow sourceRef="approveTask" targetRef="decision"/>
<exclusiveGateway id="decision"/>
<sequenceFlow sourceRef="decision" targetRef="externalSystemCall">
<conditionExpression xsi:type="tFormalExpression">
<![CDATA[
${approved}
]]>
</conditionExpression>
</sequenceFlow>
<sequenceFlow sourceRef="decision" targetRef="sendRejectionMail">
<conditionExpression xsi:type="tFormalExpression">
<![CDATA[
${!approved}
]]>
</conditionExpression>
</sequenceFlow>
<serviceTask id="externalSystemCall" name="Enter holidays in external system"
flowable:class="org.flowable.CallExternalSystemDelegate"/>
<sequenceFlow sourceRef="externalSystemCall" targetRef="holidayApprovedTask"/>
<userTask id="holidayApprovedTask" name="Holiday approved"/>
<sequenceFlow sourceRef="holidayApprovedTask" targetRef="approveEnd"/>
<serviceTask id="sendRejectionMail" name="Send out rejection email"
flowable:class="org.flowable.SendRejectionMail"/>
<sequenceFlow sourceRef="sendRejectionMail" targetRef="rejectEnd"/>
<endEvent id="approveEnd"/>
<endEvent id="rejectEnd"/>
</process>
</definitions>
第 2-11 行在几乎每个流程定义中看到的内容相同。
每个步骤(在 BPMN 2.0 术语中称为"活动")都有一个 id 属性,为其在 XML 文件中提供唯一标识符。所有活动也可以有一个可选的名称,这当然会增加可视化图表的可读性。
活动通过序列流连接,在可视化图表中是一个有向箭头。在执行流程实例时,执行将从开始事件流向下一个活动,遵循序列流。
离开排他网关(带有 X 的菱形)的序列流明显很特殊:它们都有一个以表达式形式定义的条件(见第 25 和 32 行)。当流程实例执行到达这个网关时,将评估这些条件,并采用第一个解析为true的条件。这就是这里排他的含义:只选择一个。如果需要不同的路由行为,当然可以使用其他类型的网关。
这里写为表达式的条件形式为 ${approved},这是 ${approved == true} 的简写。变量 'approved' 被称为流程变量。流程变量是与流程实例一起存储的持久数据,可以在流程实例的生命周期内使用。在这种情况下,这确实意味着我们将必须在流程实例中的某个时刻(当经理用户任务被提交时,或者用 Flowable 术语说,完成时)设置这个流程变量,因为这不是流程实例启动时可用的数据。
接下来将BPMN2.0xml文件部署到引擎上:
这样一来流程引擎将xml文件存储在数据库中,以便在需要时就可以检索它;流程定义被解析为内部的、可执行的对象模型,以便可以从中启动流程实例。
要将流程定义部署到 Flowable 引擎,使用 RepositoryService,它可以从 ProcessEngine 对象获取(如果注入的依赖是flowable-spring-boot-starter,那么可以不用从ProcessEngine中获取,直接注入即可)。使用 RepositoryService,通过传递 XML 文件的位置并调用 deploy() 方法来实际执行它,从而创建新的 Deployment:
RepositoryService repositoryService = processEngine.getRepositoryService();
Deployment deployment = repositoryService.createDeployment()
.addClasspathResource("holiday-request.bpmn20.xml")
.deploy();
同时我们也可以通过API查询来验证流程定义是否被引擎知晓,通过 RepositoryService 创建新的 ProcessDefinitionQuery 对象来完成
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
.deploymentId(deployment.getId())
启动流程实例:
因为我们在定义流程时已经使用了需要流程变量才能触发的方式,所以这里我们就需要提供一些初始的流程变量来模拟触发:
Scanner scanner= new Scanner(System.in);
System.out.println("Who are you?");
String employee = scanner.nextLine();
System.out.println("How many holidays do you want to request?");
Integer nrOfHolidays = Integer.valueOf(scanner.nextLine());
System.out.println("Why do you need them?");
String description = scanner.nextLine();
这里我们可以使用RuntimeService来启动一个实例流程。使用HashMap来收集数据作为实例传递,使用键启动流程实例。这个键匹配在 BPMN 2.0 XML 文件中设置的 id 属性,在本例中是 holidayRequest。(稍后会了解到启动流程实例有很多方法,不仅仅是使用键)
RuntimeService runtimeService = processEngine.getRuntimeService();
Map<String, Object> variables = new HashMap<String, Object>();
variables.put("employee", employee);
variables.put("nrOfHolidays", nrOfHolidays);
variables.put("description", description);
ProcessInstance processInstance =
runtimeService.startProcessInstanceByKey("holidayRequest", variables);
这里创建RuntimeService实例是使用processEngine.getRuntimeService()方法得到的。如果注入的依赖是flowable-spring-boot-starter,可以直接注入得到
当流程实例启动时,将创建一个执行并放入开始事件中。从那里,这个执行沿着序列流到达经理审批的用户任务,并执行用户任务行为。这个行为将在数据库中创建一个任务,稍后可以通过查询找到。用户任务是一个等待状态,引擎将停止执行任何进一步的操作,返回 API 调用。
查询和完成任务:
在更现实的应用程序中,会有一个用户界面,员工和经理可以登录并查看他们的任务列表。通过这些列表,他们可以检查存储为流程变量的流程实例数据,并决定他们想对任务做什么。在这个例子中,我们将通过执行 API 调用来模拟任务列表,这些 API 调用通常会位于驱动 UI 的服务调用背后。
我们将第一个任务分配给"managers"组,在第一个任务中添加 candidateGroups 属性:
<userTask id="approveTask" name="Approve or reject request" flowable:candidateGroups="managers"/>
第二个用户任务分配给原始的假期申请人,并在第二个任务中添加 assignee 属性。这里没有使用像上面"managers"那样的静态值,而是使用基于流程变量的动态分配,这个流程变量是我们在启动流程实例时传递的
<userTask id="holidayApprovedTask" name="Holiday approved" flowable:assignee="${employee}"/>
要获取实际的任务列表,我们通过 TaskService 创建一个 TaskQuery,并配置查询仅返回"managers"组的任务:
TaskService taskService = processEngine.getTaskService();
List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup("managers").list();
经理现在可以完成任务了。在现实中,这通常意味着用户提交了一个表单。然后表单中的数据作为流程变量传递。在这里,我们将通过在完成任务时传递一个带有"approved"变量的 map 来模拟这一点(名称很重要,因为它稍后会在序列流的条件中使用!):
boolean approved = scanner.nextLine().toLowerCase().equals("y");
variables = new HashMap<String, Object>();
variables.put("approved", approved);
taskService.complete(task.getId(), variables);
任务现在已完成,基于"approved"流程变量选择了离开排他网关的两条路径之一。