1. 概述

简单来说,Activiti 是一个工作流和业务流程管理平台

我们可以通过创建 ProcessEngineConfiguration(通常基于配置文件)快速上手。由此可以获取 ProcessEngine,并通过它执行工作流和 BPM 操作。

该 API 提供了多种服务用于访问和管理流程,这些服务能帮我们获取:

  • 流程历史记录
  • 当前运行中的流程
  • 已部署但未运行的流程

这些服务还能用于定义流程结构并操作流程状态(如运行、挂起、取消等)。

⚠️ 如果你是 Activiti API 新手,建议先阅读我们的 **Java Activiti API 入门**。本文将重点介绍如何在 Spring Boot 应用中集成 Activiti。

2. Spring Boot 集成配置

2.1. 基础配置

添加 Maven 依赖即可快速集成:

<dependency>
    <groupId>org.activiti</groupId>
    <artifactId>activiti-spring-boot-starter</artifactId>
</dependency>

最新稳定版本可在 Maven 中央仓库 查询。

也可以通过 Spring Initializr 生成项目,直接选择 Activiti 依赖。

仅需添加依赖和 @EnableAutoConfiguration 注解,Spring Boot 就会自动完成:

  • ✅ 创建数据源(Activiti 需要数据库构建 ProcessEngine
  • ✅ 暴露 ProcessEngine Bean
  • ✅ 暴露 Activiti 服务 Bean
  • ✅ 创建 Spring 任务执行器

2.2. 创建并运行流程

我们通过一个示例演示流程创建与运行。首先需要定义 BPMN 流程文件。

将下载的 BPMN 文件放入 src/main/resources/processes 目录(Spring Boot 默认扫描路径)。我们创建一个包含用户任务的简单流程:

startEvent endEvent

用户任务指派给流程发起人,对应的 BPMN 定义如下:

 <process id="my-process" name="say-hello-process" isExecutable="true">
     <startEvent id="startEvent" name="startEvent">
     </startEvent>
     <sequenceFlow id="sequence-flow-1" sourceRef="startEvent" targetRef="A">
     </sequenceFlow>     
     <userTask id="A" name="A" activiti:assignee="$INITIATOR">
     </userTask>
     <sequenceFlow id="sequence-flow-2" sourceRef="A" targetRef="endEvent">
     </sequenceFlow>
     <endEvent id="endEvent" name="endEvent">
     </endEvent>
</process>

接下来创建 REST 控制器启动流程:

@Autowired
private RuntimeService runtimeService;

@GetMapping("/start-process")
public String startProcess() {
  
    runtimeService.startProcessInstanceByKey("my-process");
    return "Process started. Number of currently running"
      + "process instances = "
      + runtimeService.createProcessInstanceQuery().count();
}

核心逻辑说明:

  • runtimeService.startProcessInstanceByKey("my-process") 启动 key 为 "my-process" 的流程
  • runtimeService.createProcessInstanceQuery().count() 获取当前运行流程实例数

每次访问 /start-process 接口都会创建新流程实例,运行实例数递增。JUnit 测试验证了该行为:

@Test
public void givenProcess_whenStartProcess_thenIncreaseInProcessInstanceCount() 
  throws Exception {
 
    String responseBody = this.mockMvc
      .perform(MockMvcRequestBuilders.get("/start-process"))
      .andReturn().getResponse().getContentAsString();
 
    assertEquals("Process started. Number of currently running"
      + " process instances = 1", responseBody);
        
    responseBody = this.mockMvc
      .perform(MockMvcRequestBuilders.get("/start-process"))
      .andReturn().getResponse().getContentAsString();
 
    assertEquals("Process started. Number of currently running"
      + " process instances = 2", responseBody);
        
    responseBody = this.mockMvc
      .perform(MockMvcRequestBuilders.get("/start-process"))
      .andReturn().getResponse().getContentAsString();
 
    assertEquals("Process started. Number of currently running"
      + " process instances = 3", responseBody);
}

3. 流程操作实战

现在我们扩展示例,演示如何访问和操作流程。

3.1. 获取流程实例的任务列表

流程包含两个用户任务 A 和 B。启动后流程会等待任务 A 完成,再执行任务 B。创建接口查看指定流程实例的任务:

class TaskRepresentation {
    private String id;
    private String name;
    private String processInstanceId;

    // 标准构造器
}
@GetMapping("/get-tasks/{processInstanceId}")
public List<TaskRepresentation> getTasks(
  @PathVariable String processInstanceId) {
 
    List<Task> usertasks = taskService.createTaskQuery()
      .processInstanceId(processInstanceId)
      .list();

    return usertasks.stream()
      .map(task -> new TaskRepresentation(
        task.getId(), task.getName(), task.getProcessInstanceId()))
      .collect(Collectors.toList());
}

关键点:

  • taskService.createTaskQuery() 创建任务查询
  • .processInstanceId(processInstanceId) 按流程实例过滤
  • 转换为自定义 TaskRepresentation 对象返回

测试验证:启动流程后调用该接口,应返回任务 A:

@Test
public void givenProcess_whenProcessInstance_thenReceivedRunningTask() 
  throws Exception {
 
    this.mockMvc.perform(MockMvcRequestBuilders.get("/start-process"))
      .andReturn()
      .getResponse();
    ProcessInstance pi = runtimeService.createProcessInstanceQuery()
      .orderByProcessInstanceId()
      .desc()
      .list()
      .get(0);
    String responseBody = this.mockMvc
      .perform(MockMvcRequestBuilders.get("/get-tasks/" + pi.getId()))
      .andReturn()
      .getResponse()
      .getContentAsString();

    ObjectMapper mapper = new ObjectMapper();
    List<TaskRepresentation> tasks = Arrays.asList(mapper
      .readValue(responseBody, TaskRepresentation[].class));
 
    assertEquals(1, tasks.size());
    assertEquals("A", tasks.get(0).getName());
}

3.2. 完成任务

现在演示完成任务 A 的效果。创建接口完成指定流程实例的任务 A:

@GetMapping("/complete-task-A/{processInstanceId}")
public void completeTaskA(@PathVariable String processInstanceId) {
    Task task = taskService.createTaskQuery()
      .processInstanceId(processInstanceId)
      .singleResult();
    taskService.complete(task.getId());
}

操作步骤:

  1. 通过 taskService 查询流程实例的任务(即任务 A)
  2. 调用 taskService.complete(task.getId) 完成任务

完成任务后,流程到达终点,RuntimeService 中不再有流程实例。测试验证:

@Test
public void givenProcess_whenCompleteTaskA_thenNoProcessInstance() 
  throws Exception {

    this.mockMvc.perform(MockMvcRequestBuilders.get("/start-process"))
      .andReturn()
      .getResponse();
    ProcessInstance pi = runtimeService.createProcessInstanceQuery()
      .orderByProcessInstanceId()
      .desc()
      .list()
      .get(0);
    this.mockMvc.perform(MockMvcRequestBuilders.get("/complete-task-A/" + pi.getId()))
      .andReturn()
      .getResponse()
      .getContentAsString();
    List<ProcessInstance> list = runtimeService.createProcessInstanceQuery().list();
    assertEquals(0, list.size());
}

这就是使用 Activiti 服务操作流程的基本方式。

4. 总结

本文介绍了在 Spring Boot 中使用 Activiti API 的核心要点。更多细节可参考官方用户指南

Spring Boot 的自动配置极大简化了集成过程,我们无需手动:

  • 创建数据库
  • 部署流程定义
  • 构建 ProcessEngine

💡 完整示例代码可在 GitHub 获取。实际开发中建议结合业务需求扩展流程定义和操作逻辑。


原始标题:Introduction to Activiti with Spring

« 上一篇: Cucumber Java 8 支持