工作流引擎在现代企业应用中扮演着重要角色,它能够将业务流程从应用程序代码中抽离出来,实现业务流程的可视化设计和灵活变更。Activiti作为一款轻量级的工作流引擎,与SpringBoot的完美结合可以极大提升开发效率。本文将详细介绍如何在SpringBoot 3.x环境中集成Activiti 7,并实现完整的业务流程管理。
在开始集成前,我们需要准备以下开发环境:
选择这些版本组合主要基于以下考虑:
提示:如果使用其他IDE如Eclipse,需要安装对应的Activiti插件。本文以IntelliJ IDEA为例进行说明。
由于IDEA 2020+版本不再支持传统的actiBPM插件,我们需要安装替代方案:
File -> Settings -> Plugins安装完成后,我们可以通过以下方式验证插件是否正常工作:
使用IDEA的Spring Initializr创建项目:
File -> New -> Project在pom.xml中添加Activiti 7和其他必要依赖:
xml复制<dependencies>
<!-- SpringBoot基础依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.2.0</version>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- MyBatis Plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>3.5.7</version>
</dependency>
<!-- Activiti 7核心依赖 -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter</artifactId>
<version>7.1.0.M6</version>
</dependency>
</dependencies>
依赖选择说明:
activiti-spring-boot-starter而非单独的activiti依赖,可以简化配置创建数据库和表结构:
sql复制CREATE DATABASE `activiti` DEFAULT CHARACTER SET utf8mb4;
-- 用户表示例
CREATE TABLE `user` (
`ID` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
`NAME` varchar(100) DEFAULT NULL COMMENT '名称',
`AGE` varchar(100) DEFAULT NULL COMMENT '年龄',
PRIMARY KEY (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户信息表';
application.yml配置:
yaml复制spring:
datasource:
url: jdbc:mysql://localhost:3306/activiti?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
activiti:
database-schema-update: true
history-level: audit
db-history-used: true
关键配置项说明:
database-schema-update: true:启动时自动创建或更新Activiti表结构history-level: audit:记录完整的流程历史信息db-history-used: true:启用历史数据存储Activiti工作流的核心操作步骤如下:
创建一个简单的请假流程(leave.bpmn):
在IDEA中:
java复制@SpringBootTest
public class ProcessDeployTests {
@Autowired
private RepositoryService repositoryService;
@Test
public void testDeploy() {
Deployment deployment = repositoryService.createDeployment()
.addClasspathResource("process/leave.bpmn")
.addClasspathResource("process/leave.png")
.name("请假流程")
.deploy();
System.out.println("部署ID:" + deployment.getId());
System.out.println("部署名称:" + deployment.getName());
}
}
部署成功后,Activiti会自动创建以下表:
java复制@Autowired
private RuntimeService runtimeService;
@Test
public void startProcess() {
// 使用流程定义Key启动
ProcessInstance instance = runtimeService.startProcessInstanceByKey("leave");
System.out.println("流程实例ID:" + instance.getId());
System.out.println("流程定义ID:" + instance.getProcessDefinitionId());
}
java复制@Autowired
private TaskService taskService;
@Test
public void queryAndCompleteTask() {
// 查询李四的待办任务
List<Task> tasks = taskService.createTaskQuery()
.taskAssignee("李四")
.list();
// 完成任务
tasks.forEach(task -> {
System.out.println("处理任务:" + task.getName());
taskService.complete(task.getId());
});
}
java复制@Test
public void startProcessWithBusinessKey() {
// 将业务ID与流程关联
String businessKey = "leave_1001";
ProcessInstance instance = runtimeService.startProcessInstanceByKey(
"leave", businessKey);
System.out.println("业务关联ID:" + instance.getBusinessKey());
}
java复制@Autowired
private HistoryService historyService;
@Test
public void queryHistory() {
List<HistoricActivityInstance> activities = historyService
.createHistoricActivityInstanceQuery()
.processInstanceId("2501")
.finished()
.list();
activities.forEach(activity -> {
System.out.println(activity.getActivityName() + " : "
+ activity.getDurationInMillis() + "ms");
});
}
流程变量可以在流程执行过程中传递数据:
java复制@Test
public void useProcessVariables() {
// 启动时设置变量
Map<String, Object> variables = new HashMap<>();
variables.put("days", 3);
variables.put("reason", "病假");
ProcessInstance instance = runtimeService.startProcessInstanceByKey(
"leave", variables);
// 任务处理时获取变量
Task task = taskService.createTaskQuery()
.processInstanceId(instance.getId())
.singleResult();
Integer days = (Integer) taskService.getVariable(task.getId(), "days");
System.out.println("请假天数:" + days);
}
实际业务中,处理人可能需要动态确定:
java复制@Test
public void dynamicAssignee() {
// 通过变量指定处理人
Map<String, Object> variables = new HashMap<>();
variables.put("approver", "王五");
runtimeService.startProcessInstanceByKey("leave", variables);
// 查询任务时使用变量
Task task = taskService.createTaskQuery()
.taskAssignee("王五")
.singleResult();
if(task != null) {
taskService.complete(task.getId());
}
}
Activiti支持流程定义的版本管理:
java复制@Test
public void processVersionManagement() {
// 查询所有版本
List<ProcessDefinition> definitions = repositoryService
.createProcessDefinitionQuery()
.processDefinitionKey("leave")
.list();
// 挂起/激活流程定义
repositoryService.suspendProcessDefinitionById("leave:1:2501");
// repositoryService.activateProcessDefinitionById("leave:1:2501");
}
问题现象:启动应用后,Activiti表未自动创建
解决方案:
spring.activiti.database-schema-update=true问题现象:部署后流程图显示异常或无法查看
解决方案:
问题现象:任务应该存在但查询结果为空
排查步骤:
yaml复制spring:
activiti:
history-level: none # 可选none, activity, audit, full
合理的项目结构能提高代码可维护性:
code复制src/main/java
├── com.example.activiti
│ ├── config # 配置类
│ ├── controller # 控制器
│ ├── service # 服务层
│ │ ├── impl # 服务实现
│ ├── repository # 数据访问
│ ├── entity # 实体类
│ ├── util # 工具类
│ └── ActivitiApplication.java
src/main/resources
├── static
├── templates
└── process # 存放BPMN流程定义文件
在实际开发中,我通常会为Activiti相关操作创建单独的服务类,例如:
java复制@Service
public class WorkflowService {
@Autowired
private RuntimeService runtimeService;
@Autowired
private TaskService taskService;
public String startProcess(String processKey, String businessKey) {
ProcessInstance instance = runtimeService
.startProcessInstanceByKey(processKey, businessKey);
return instance.getId();
}
public List<Task> getUserTasks(String userId) {
return taskService.createTaskQuery()
.taskAssignee(userId)
.list();
}
}
这种封装方式使业务代码更简洁,也便于统一处理异常和日志。