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"流程变量选择了离开排他网关的两条路径之一。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐