当业务系统里的审批环节从两三个涨到十几个,继续用状态字段加 if-else 硬编码的方式维护流程,代码会迅速变成一场灾难。流程改一次,代码改一片,测试回归一轮,这是很多团队都经历过的痛点。工作流引擎就是为解决这个问题而生的,Activiti 7 作为其中较为流行的开源方案,与 Spring Boot 的整合非常顺滑,本文将带你完整走一遍整合过程,并用一个请假审批流程把核心 API 串起来。

一、Activiti 7 核心概念与整合前的准备
在动手写代码之前,有必要先理清 Activiti 的几个核心概念。Activiti 基于 BPMN 2.0 规范,流程通过 BPMN 文件定义,引擎负责解析并驱动流程实例向前推进。它内置了独立的数据库表结构来存储流程定义、运行时任务和历史数据,所有操作都围绕这几组表展开。
Activiti 的常用服务接口有四个:RepositoryService 负责流程定义的部署与查询;RuntimeService 负责启动流程实例、操作流程变量;TaskService 负责查询和办理个人任务;HistoryService 则提供已结束流程的历史查询能力。理解了这四个服务的分工,后面的代码写起来思路会非常清晰。
整合前需要确认版本匹配关系。Activiti 7 要求 Spring Boot 2.x 版本,如果你用的是 Spring Boot 3.x,直接整合会因 javax 到 jakarta 的包名变更而失败,这一点务必提前确认。数据库方面,Activiti 默认支持 MySQL、PostgreSQL、Oracle 等主流数据库,本文以 MySQL 为例。
二、依赖配置与数据库初始化
首先创建一个 Spring Boot 项目,在 pom.xml 中引入相关依赖。除了 Activiti 的 starter 之外,还需要引入 spring-boot-starter-jdbc 或 mybatis 等数据访问依赖,因为 Activiti 需要一个数据源来创建自己的表。
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter</artifactId>
<version>7.1.0.M6</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
</dependencies>
接着在 application.yml 中配置数据源。需要注意的是 database-schema-update 这个配置项,它控制引擎启动时是否自动创建和更新表结构,开发阶段建议设置为 true,生产环境则应该改为 false 并通过脚本手动管理表结构,避免引擎擅自改表带来风险。
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/activiti_demo?useSSL=false&serverTimezone=UTC&nullCatalogMeansCurrent=true
username: root
password: root
activiti:
database-schema-update: true
db-history-used: true
history-level: full
check-process-definitions: true
这里有一个高频踩坑点必须提醒:连接串里的 nullCatalogMeansCurrent=true 参数在 MySQL 8 驱动下建议加上,否则 Activiti 在建表时可能因为跨库查询系统表而报错。另外建库时推荐使用 utf8mb4 字符集,避免流程名称包含中文时出现乱码。
配置完成后启动项目,引擎会自动在数据库中创建几十张以 ACT_ 开头的表。这些表分为三类:ACT_RE_ 前缀是流程定义的存储表,ACT_RU_ 前缀是运行时数据表,ACT_HI_ 前缀是历史数据表。看到这些表被创建出来,说明整合已经成功了一半。
三、设计并部署一个请假审批流程
流程定义文件采用 BPMN 2.0 的 XML 格式,放在项目的 resources/processes 目录下,Activiti 启动时会自动扫描并部署该目录下的所有流程文件。下面定义一个简单的请假流程:员工发起申请,主管审批,通过后人事备案,不通过则直接结束。
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:activiti="http://activiti.org/bpmn"
targetNamespace="http://activiti.org/demo">
<process id="leaveProcess" name="请假审批流程" isExecutable="true">
<startEvent id="start" name="开始"/>
<userTask id="applyTask" name="填写申请" activiti:assignee="${applyUser}"/>
<userTask id="managerTask" name="主管审批" activiti:assignee="${manager}"/>
<exclusiveGateway id="gateway" name="是否通过"/>
<userTask id="hrTask" name="人事备案" activiti:candidateGroups="hrGroup"/>
<endEvent id="end" name="结束"/>
<endEvent id="rejectEnd" name="拒绝结束"/>
<sequenceFlow id="flow1" sourceRef="start" targetRef="applyTask"/>
<sequenceFlow id="flow2" sourceRef="applyTask" targetRef="managerTask"/>
<sequenceFlow id="flow3" sourceRef="managerTask" targetRef="gateway"/>
<sequenceFlow id="flow4" sourceRef="gateway" targetRef="hrTask">
<conditionExpression>${approved}</conditionExpression>
</sequenceFlow>
<sequenceFlow id="flow5" sourceRef="gateway" targetRef="rejectEnd">
<conditionExpression>${!approved}</conditionExpression>
</sequenceFlow>
<sequenceFlow id="flow6" sourceRef="hrTask" targetRef="end"/>
</process>
</definitions>
流程文件中有几个关键点值得说明。userTask 通过 activiti:assignee 指定办理人,值可以是固定字符串,也可以是表达式,表达式会在流程启动或任务创建时从流程变量中取值,这样同一个流程定义就能服务不同的发起人。排他网关后面的两条连线上通过 conditionExpression 定义了条件表达式,引擎会根据流程变量 approved 的值决定流向。实际项目中建议使用流程设计器插件来画图,再用表达式补充业务细节,纯手写 XML 容易出错。
四、用核心 API 完成流程的发起与办理
流程部署好之后,就可以编写业务代码了。下面的示例覆盖了发起流程、查询待办、完成任务三个最常用的操作,这也是绝大多数审批业务的骨架。
@Service
public class LeaveService {
@Autowired
private RuntimeService runtimeService;
@Autowired
private TaskService taskService;
/** 发起请假流程 */
public String startLeave(String applyUser, int days) {
Map<String, Object> vars = new HashMap<>();
vars.put("applyUser", applyUser);
vars.put("manager", "zhangsan");
vars.put("days", days);
ProcessInstance instance = runtimeService
.startProcessInstanceByKey("leaveProcess", vars);
return instance.getId();
}
/** 查询某人的待办任务 */
public List<Task> listTasks(String assignee) {
return taskService.createTaskQuery()
.taskAssignee(assignee)
.orderByTaskCreateTime().desc()
.list();
}
/** 主管审批:同意或拒绝 */
public void approve(String taskId, boolean approved) {
Map<String, Object> vars = new HashMap<>();
vars.put("approved", approved);
taskService.complete(taskId, vars);
}
}
代码逻辑不难,但有几个细节容易被忽略。第一,启动流程时传入的变量 Map 会成为流程变量,在整个流程实例生命周期内可见,网关条件和任务办理人表达式都从这里取值。第二,调用 taskService.complete 时再次传入了 approved 变量,这个变量会被网关的条件表达式读取,从而决定流程走向。第三,完成任务后引擎会自动推进到下一个节点并创建新任务,不需要手动干预。
还有一个常见需求是查询流程走到哪一步了。可以通过 runtimeService.createExecutionQuery() 查询当前活动节点,或者借助 HistoryService 查询完整的流转记录。如果需要绘制流程进度图,Activiti 还提供了生成流程图像的 API,可以将当前节点高亮标记后返回给前端展示,用户体验会好很多。
五、常见问题与生产环境建议
整合过程中有几个高频问题值得提前了解。首先是安全框架冲突,Activiti 7 的 starter 默认依赖 Spring Security,如果项目本身有自己的认证体系,可能启动时被强制跳转到登录页。解决办法是引入 security 自动配置的排除项,或者干脆自己实现一个配置类放行相关路径。
其次是事务问题。Activiti 与业务代码共享 Spring 的事务管理器,这意味着业务操作和流程推进可以在同一个事务中完成,这是整合的一大优势。但要注意流程操作和业务表操作应使用同一个数据源,跨库场景下事务无法保证一致,需要额外设计补偿机制。
最后是历史数据的管理。流程实例结束后运行时表的数据会被删除并归档到历史表,长期运行下来历史表数据量会很大,建议定期归档清理,或者根据查询需求调整 history-level 配置,不必所有环境都开 full 级别。把这几个问题处理好,Activiti 整合方案就可以稳定地支撑业务流程自动化了。
Spring Boot整合Activiti 7工作流引擎业务流程自动化修改时间:2026-09-06 13:30:50