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 默认扫描路径)。我们创建一个包含用户任务的简单流程:
用户任务指派给流程发起人,对应的 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());
}
操作步骤:
- 通过
taskService
查询流程实例的任务(即任务 A) - 调用
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 获取。实际开发中建议结合业务需求扩展流程定义和操作逻辑。