1. SpringBoot集成Flowable工作流引擎实战指南
工作流引擎是现代企业应用开发中不可或缺的组件,它能够将复杂的业务流程可视化、标准化和自动化。本文将详细介绍如何在SpringBoot项目中集成Flowable工作流引擎,并通过一个完整的请假审批流程案例,带你从零开始掌握工作流开发的核心要点。
1.1 工作流引擎核心概念解析
工作流(Workflow)本质上是通过计算机对业务流程进行自动化管理。它建立在业务流程的基础上,主要解决在多个参与者之间按照预定义规则自动传递文档、信息或任务的问题,最终实现特定的业务目标。
典型工作流系统应用场景:
- 行政管理类:出差申请、加班申请、请假审批等
- 人事管理类:员工培训安排、绩效考评流程
- 财务相关类:付款请求、日常报销处理
- 客户服务类:客户投诉处理、售后服务管理
- 关键业务流程:订单处理、合同审核等
传统实现方式通常采用状态字段跟踪流程变化,这种方式耦合度高且难以维护。而专业的工作流引擎如Flowable提供了更灵活的流程定义和管理能力。
1.2 Flowable工作流引擎优势
Flowable是一个轻量级的Java业务流程引擎,支持BPMN 2.0标准。相比同类产品Activiti,Flowable具有以下显著优势:
-
功能增强:
- 支持加签、动态增加流程节点
- 支持CMMN(案例管理)和DMN(决策模型)规范
- 提供HTTP任务等新型节点
- 支持历史数据通过消息中间件发送
-
技术先进性:
- 支持Java 11+版本
- 支持MongoDB存储历史数据
- 优化了代码结构,移除了PVM(流程虚拟机)
-
稳定性提升:
- 修复了Activiti中的大量bug
- 改进了DMN设计器
- 提供了更完善的事务支持
1.3 Flowable表结构设计
Flowable在初始化时会创建五类表结构,每类表有特定的前缀和用途:
| 表前缀 | 含义 | 主要用途 |
|---|---|---|
| ACT_RE | Repository | 存储流程定义和静态资源(如图片、规则等) |
| ACT_RU | Runtime | 存储运行时的流程实例、任务、变量等数据 |
| ACT_HI | History | 存储历史流程实例、变量、任务等历史数据 |
| ACT_GE | General | 通用数据,用于不同场景下 |
| ACT_ID | Identity | 存储用户、用户组等身份认证和授权信息 |
运行时表(ACT_RU_)中的数据会在流程结束时删除,以保持表体积最小化,而历史表(ACT_HI_)则会持久化存储流程执行记录。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 项目环境搭建与配置
2.1 创建SpringBoot项目
首先创建一个标准的SpringBoot项目,添加必要的依赖:
xml复制<dependencies>
<!-- SpringBoot基础依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Flowable核心依赖 -->
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-spring-boot-starter</artifactId>
<version>6.4.1</version>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version>
</dependency>
</dependencies>
2.2 数据库配置
在application.yml中配置数据库连接信息:
yaml复制spring:
datasource:
username: root
password: 123456
url: jdbc:mysql://localhost:3306/flowable-test?useUnicode=true&characterEncoding=UTF-8
driver-class-name: com.mysql.jdbc.Driver
flowable:
database-schema-update: true # 自动创建/更新数据库表结构
async-executor-activate: false # 关闭异步执行器(开发环境建议关闭)
注意:MySQL 8.x及以上版本需要使用
com.mysql.cj.jdbc.Driver驱动类,并添加serverTimezone=Asia/Shanghai参数。
2.3 解决中文乱码问题
创建配置类解决流程图中文显示乱码问题:
java复制@Configuration
public class FlowableConfig implements EngineConfigurationConfigurer<SpringProcessEngineConfiguration> {
@Override
public void configure(SpringProcessEngineConfiguration configuration) {
configuration.setActivityFontName("宋体");
configuration.setLabelFontName("宋体");
configuration.setAnnotationFontName("宋体");
}
}
启动项目后,Flowable会自动创建所需的数据库表结构,共约60张表,分为前述的五类。
3. 流程设计与建模
3.1 BPMN 2.0基础元素
BPMN(业务流程模型和符号)是流程建模的标准语言,主要包含以下元素:
-
事件(Event):
- 开始事件:标识流程的开始
- 结束事件:标识流程的结束
-
网关(Gateway):
- 排他网关(Exclusive Gateway):类似if-else,只有一个出口会被执行
- 并行网关(Parallel Gateway):所有出口同时执行
- 包容性网关(Inclusive Gateway):满足条件的出口都会执行
-
任务(Task):
- 用户任务(User Task):需要人工处理的任务节点
- 服务任务(Service Task):自动执行的逻辑节点
- 接收任务(Receive Task):被动等待触发的节点
3.2 使用IDEA插件设计流程图
-
安装Flowable BPMN插件:
- 打开IDEA的Settings → Plugins
- 搜索"Flowable BPMN"并安装
-
创建流程文件:
- 在resources目录下创建processes文件夹
- 新建ask_for_leave.bpmn20.xml文件(后缀必须为.bpmn20.xml)
-
设计请假审批流程:
- 开始事件 → 员工请假任务 → 组长审批 → 排他网关 →
- 通过:经理审批 → 排他网关 →
- 通过:结束
- 拒绝:发送拒绝通知 → 结束
- 拒绝:发送拒绝通知 → 结束
- 通过:经理审批 → 排他网关 →
- 开始事件 → 员工请假任务 → 组长审批 → 排他网关 →
3.3 流程XML关键配置
xml复制<process id="ask_for_leave" name="ask_for_leave" isExecutable="true">
<!-- 员工请假任务,动态指定处理人 -->
<userTask id="leaveTask" name="请假" flowable:assignee="#{leaveUser}"/>
<!-- 组长审批任务 -->
<userTask id="zuZhangTask" name="组长审核" flowable:assignee="#{zuZhangUser}"/>
<!-- 排他网关和条件流转 -->
<exclusiveGateway id="zuZhangJudgeGateway"/>
<sequenceFlow id="zuZhangPass" sourceRef="zuZhangJudgeGateway" targetRef="managerTask" name="组长审核通过">
<conditionExpression xsi:type="tFormalExpression">
${var:equals(zuZhangCheckResult,"通过")}
</conditionExpression>
</sequenceFlow>
<!-- 拒绝时执行的服务任务 -->
<serviceTask id="sendFailMessage" flowable:class="com.example.service.LeaveFailService"/>
</process>
4. 流程实现与接口开发
4.1 核心服务注入
在Controller中注入Flowable的核心服务:
java复制@RestController
public class AskForLeaveController {
@Autowired
private RuntimeService runtimeService; // 运行时服务
@Autowired
private RepositoryService repositoryService; // 仓库服务
@Autowired
private TaskService taskService; // 任务服务
@Autowired
private ProcessEngine processEngine; // 流程引擎
}
4.2 流程操作接口实现
4.2.1 启动请假流程
java复制@GetMapping("/askForLeave")
public String askForLeave() {
Map<String, Object> variables = new HashMap<>();
variables.put("leaveUser", "张三"); // 请假申请人
variables.put("reason", "家庭旅行"); // 请假原因
variables.put("days", 5); // 请假天数
ProcessInstance instance = runtimeService.startProcessInstanceByKey(
"ask_for_leave", // 流程定义key
variables
);
return "流程已启动,ID:" + instance.getId();
}
4.2.2 查看流程图
java复制@GetMapping("/viewFlowChart")
public void viewFlowChart(HttpServletResponse resp, String processId) throws Exception {
ProcessInstance pi = runtimeService.createProcessInstanceQuery()
.processInstanceId(processId)
.singleResult();
if (pi == null) return;
// 获取当前活动节点
List<String> activityIds = runtimeService.getActiveActivityIds(pi.getId());
// 生成流程图
BpmnModel bpmnModel = repositoryService.getBpmnModel(pi.getProcessDefinitionId());
ProcessEngineConfiguration engconf = processEngine.getProcessEngineConfiguration();
ProcessDiagramGenerator diagramGenerator = engconf.getProcessDiagramGenerator();
try (InputStream in = diagramGenerator.generateDiagram(bpmnModel, "png", activityIds,
Collections.emptyList(), engconf.getActivityFontName(),
engconf.getLabelFontName(), engconf.getAnnotationFontName(),
engconf.getClassLoader(), 1.0, false);
OutputStream out = resp.getOutputStream()) {
byte[] buf = new byte[1024];
int length;
while ((length = in.read(buf)) != -1) {
out.write(buf, 0, length);
}
}
}
4.2.3 任务处理接口
java复制// 员工提交给组长
@GetMapping("/submitToZuZhang")
public String submitToZuZhang() {
Task task = taskService.createTaskQuery()
.taskAssignee("张三")
.orderByTaskCreateTime()
.desc()
.list()
.get(0);
Map<String, Object> variables = new HashMap<>();
variables.put("zuZhangUser", "李四"); // 指定组长审批人
taskService.complete(task.getId(), variables);
return "已提交给组长审批";
}
// 组长审批
@GetMapping("/zuZhangApprove")
public String zuZhangApprove(String result) {
Task task = taskService.createTaskQuery()
.taskAssignee("李四")
.taskName("组长审核")
.singleResult();
Map<String, Object> variables = new HashMap<>();
variables.put("zuZhangCheckResult", result);
variables.put("managerUser", "王五"); // 指定经理审批人
taskService.complete(task.getId(), variables);
return "组长审批完成,结果:" + result;
}
4.3 服务任务实现
当审批被拒绝时,流程会执行服务任务发送通知:
java复制@Component
public class LeaveFailService implements JavaDelegate {
@Override
public void execute(DelegateExecution execution) {
String processInstanceId = execution.getProcessInstanceId();
String reason = (String) execution.getVariable("reason");
System.out.println("审批不通过通知:流程ID " + processInstanceId);
System.out.println("拒绝原因:" + reason);
// 实际项目中可调用邮件/短信服务发送通知
}
}
5. 流程测试与问题排查
5.1 完整流程测试步骤
-
启动流程:
code复制GET /askForLeave返回:
流程已启动,ID:ef67cd51-af01-11ef-8fdb-005056c00001 -
查看流程图:
code复制GET /viewFlowChart?processId=ef67cd51-af01-11ef-8fdb-005056c00001将显示当前流程位置(员工请假任务高亮)
-
提交组长审批:
code复制GET /submitToZuZhang返回:
已提交给组长审批 -
组长审批通过:
code复制GET /zuZhangApprove?result=通过返回:
组长审批完成,结果:通过 -
经理最终审批:
code复制GET /managerApprove?result=通过返回:
经理审批完成,结果:通过
5.2 常见问题与解决方案
-
流程图中文乱码:
- 确保已配置FlowableConfig
- 检查服务器是否安装了中文字体
-
任务查询不到:
- 确认任务assignee是否正确
- 检查流程是否已结束(查询历史任务)
- 使用ProcessInstanceQuery检查流程状态
-
变量获取为null:
- 确保变量在流程启动或任务完成时正确设置
- 使用runtimeService.getVariables()检查所有变量
-
服务任务不执行:
- 确认类路径配置正确
- 检查类是否实现JavaDelegate接口
- 确保类被Spring管理(添加@Component)
5.3 性能优化建议
-
生产环境配置:
yaml复制flowable: async-executor-activate: true # 启用异步执行器 async-executor-thread-pool-size: 10 # 线程池大小 -
历史数据管理:
- 对于运行时间长的流程,考虑配置历史数据清理策略
- 可以使用MongoDB存储历史数据减轻关系型数据库压力
-
缓存配置:
java复制@Configuration public class FlowableCacheConfig { @Bean public FlowableCachingConfigurer flowableCachingConfigurer() { return new FlowableCachingConfigurer() { @Override public void configure(SpringProcessEngineConfiguration configuration) { configuration.setProcessDefinitionCache(new DefaultDeploymentCache<>(100)); configuration.setProcessDefinitionInfoCache(new DefaultDeploymentCache<>(100)); } }; } }
6. 流程扩展与高级功能
6.1 动态添加审批人
在实际应用中,审批人可能需要从数据库或外部系统获取:
java复制@GetMapping("/dynamicAssignee")
public String dynamicAssignee() {
Task task = taskService.createTaskQuery()
.taskAssignee("张三")
.singleResult();
// 从组织架构服务获取组长信息
String zuZhang = organizationService.getZuZhang("张三");
Map<String, Object> variables = new HashMap<>();
variables.put("zuZhangUser", zuZhang);
taskService.complete(task.getId(), variables);
return "动态指定审批人:" + zuZhang;
}
6.2 加签功能实现
Flowable支持在运行中添加新的审批节点:
java复制@GetMapping("/addSigner")
public String addSigner(String processId, String newApprover) {
RuntimeService runtimeService = processEngine.getRuntimeService();
// 获取当前执行实例
Execution execution = runtimeService.createExecutionQuery()
.processInstanceId(processId)
.active()
.singleResult();
// 动态添加用户任务
UserTask newTask = new UserTask();
newTask.setId("additionalReview_" + System.currentTimeMillis());
newTask.setName("补充审批");
newTask.setAssignee(newApprover);
// 添加到流程中
runtimeService.addUserIdentityLink(
execution.getId(),
newApprover,
IdentityLinkType.ASSIGNEE
);
return "已添加补充审批人:" + newApprover;
}
6.3 子流程调用
对于复杂流程,可以使用Call Activity调用子流程:
xml复制<callActivity id="callSubProcess" name="调用子流程"
calledElement="approvalSubProcess">
<extensionElements>
<flowable:in source="mainVar" target="subVar"/>
<flowable:out source="subResult" target="mainResult"/>
</extensionElements>
</callActivity>
6.4 业务数据关联
将业务数据与流程实例关联:
java复制// 启动流程时关联业务key
ProcessInstance instance = runtimeService.startProcessInstanceByKey(
"ask_for_leave",
"businessKey123", // 业务数据ID
variables
);
// 通过业务key查询流程
ProcessInstance instance = runtimeService.createProcessInstanceQuery()
.processInstanceBusinessKey("businessKey123")
.singleResult();
7. 生产环境最佳实践
7.1 流程版本管理
当修改流程定义时,Flowable会自动创建新版本:
java复制@GetMapping("/deployNewVersion")
public String deployNewVersion() {
Deployment deployment = repositoryService.createDeployment()
.addClasspathResource("processes/ask_for_leave_v2.bpmn20.xml")
.deploy();
return "新版本部署成功:" + deployment.getId();
}
// 查询所有版本
List<ProcessDefinition> definitions = repositoryService.createProcessDefinitionQuery()
.processDefinitionKey("ask_for_leave")
.orderByProcessDefinitionVersion()
.desc()
.list();
7.2 流程监控与管理
java复制// 查询运行中的流程
List<ProcessInstance> instances = runtimeService.createProcessInstanceQuery()
.active()
.list();
// 查询历史流程
List<HistoricProcessInstance> histories = historyService.createHistoricProcessInstanceQuery()
.finished()
.orderByProcessInstanceEndTime()
.desc()
.list();
7.3 异常处理与事务管理
Flowable默认与Spring事务集成,但需要注意:
java复制@Transactional
public void completeTaskWithTransaction(String taskId) {
try {
// 业务逻辑处理
businessService.processData();
// 完成任务
taskService.complete(taskId);
} catch (Exception e) {
// 异常时将标记事务回滚
throw new FlowableException("任务处理失败", e);
}
}
8. 流程设计经验分享
在实际项目中使用Flowable时,我总结了以下几点经验:
-
流程设计原则:
- 保持每个流程的职责单一
- 避免过于复杂的网关嵌套
- 为每个任务节点设置明确的命名
-
性能优化:
- 对于高频流程,考虑关闭历史记录
- 批量操作时使用异步执行
- 定期清理已完成流程的历史数据
-
异常处理:
- 为关键节点添加边界错误事件
- 实现自定义的异常处理服务任务
- 记录详细的流程执行日志
-
团队协作:
- 建立统一的流程设计规范
- 使用版本控制管理BPMN文件
- 开发流程测试工具验证设计
-
扩展性考虑:
- 预留动态审批人接口
- 设计可配置的流程跳转规则
- 实现流程模板功能
通过本文的实战指南,你应该已经掌握了SpringBoot集成Flowable的核心要点。实际项目中,还需要根据具体业务需求进行调整和优化。Flowable的强大功能远不止于此,建议进一步探索其决策表(DMN)、案例管理(CMMN)等高级特性。
