摘要:本文从架构原理、核心概念、实战代码三个维度,系统剖析 Flowable 工作流引擎,帮助开发者快速掌握流程建模、部署、执行与监控的完整链路。
目录
- 什么是 Flowable
- 核心架构与模块
- 核心概念详解
- 环境搭建与快速入门
- BPMN 2.0 建模实战
- 流程部署与启动
- 用户任务与候选人机制
- 网关与条件分支
- 服务任务与 JavaDelegate
- 事件监听与流程变量
- REST API 集成
- 生产实践与最佳实践
- 总结
1. 什么是 Flowable
Flowable 是一个轻量级、高性能的开源业务流程管理(BPM)平台,脱胎于 Activiti,由原 Activiti 核心团队于 2016 年分叉维护,经过多年演进已形成完整的企业级工作流生态。
与同类产品对比
| 特性 | Flowable | Activiti | Camunda |
|---|---|---|---|
| BPMN 2.0 支持 | ✅ 完整 | ✅ 完整 | ✅ 完整 |
| CMMN 支持 | ✅ | ❌ | ✅ |
| DMN 支持 | ✅ | ❌ | ✅ |
| Spring Boot 集成 | ✅ 原生 | ✅ | ✅ |
| 多租户 | ✅ | ❌ | 企业版 |
| 历史数据异步 | ✅ | ❌ | 企业版 |
| 社区活跃度 | 高 | 中 | 高 |
核心优势
- 标准遵从:完整支持 BPMN 2.0、CMMN 1.1、DMN 1.1 三大标准
- 轻量嵌入:可作为库嵌入任意 Java 应用,无需独立服务器
- Spring Boot Starter:开箱即用,零配置启动
- 多数据库支持:H2、MySQL、PostgreSQL、Oracle、MSSQL
- 历史异步:支持异步历史写入,大幅提升吞吐量
2. 核心架构与模块
scss
┌─────────────────────────────────────────────────────────┐
│ Flowable 生态 │
├──────────────┬──────────────┬──────────────┬────────────┤
│ flowable- │ flowable- │ flowable- │ flowable- │
│ engine │ cmmn-engine │ dmn-engine │ content │
│ (BPMN流程) │ (案例管理) │ (决策表) │ (内容管理)│
├──────────────┴──────────────┴──────────────┴────────────┤
│ flowable-spring-boot-starter │
├─────────────────────────────────────────────────────────┤
│ 数据库层 (MyBatis + 连接池) │
│ H2 / MySQL / PostgreSQL / Oracle / MSSQL │
└─────────────────────────────────────────────────────────┘
七大核心 Service
| Service | 职责 |
|---|---|
RepositoryService |
流程定义的部署、查询、删除 |
RuntimeService |
流程实例的启动、查询、变量操作 |
TaskService |
用户任务的查询、认领、完成 |
HistoryService |
历史数据查询(已完成的流程/任务) |
ManagementService |
数据库表管理、Job 管理 |
IdentityService |
用户与组的管理 |
FormService |
表单数据的读写(需开启表单模块) |
3. 核心概念详解
3.1 流程定义 vs 流程实例
markdown
流程定义 (ProcessDefinition)
├── 类比:Java Class(模板)
└── 对应:.bpmn20.xml 文件
流程实例 (ProcessInstance)
├── 类比:Java Object(运行时对象)
└── 每次启动流程 = 创建一个新实例
3.2 执行流 (Execution)
Flowable 使用**令牌(Token)**模型驱动流程推进:
- 流程实例本身是根执行流
- 并行网关会产生多个子执行流
- 每个执行流独立推进,互不阻塞
3.3 历史级别
sql
flowable:
history-level: full # none | activity | audit | full
| 级别 | 说明 |
|---|---|
none |
不记录历史,性能最高 |
activity |
记录活动开始/结束 |
audit |
记录变量变化(默认) |
full |
记录表单字段、所有详情 |
4. 环境搭建与快速入门
4.1 Maven 依赖
xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.18</version>
</parent>
<dependencies>
<!-- Flowable Spring Boot Starter -->
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-spring-boot-starter</artifactId>
<version>6.8.0</version>
</dependency>
<!-- 数据库驱动(以 MySQL 为例) -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
<!-- 连接池 -->
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
</dependency>
</dependencies>
4.2 application.yml 配置
yaml
spring:
datasource:
url: jdbc:mysql://localhost:3306/flowable?useSSL=false&characterEncoding=UTF-8
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
flowable:
# 自动建表策略:true=自动创建/更新,false=不操作
database-schema-update: true
# 历史记录级别
history-level: full
# 异步历史(高并发推荐开启)
async-history:
enabled: true
# 流程定义文件位置(默认 classpath*:/processes/*.bpmn20.xml)
process:
resource-pattern: classpath*:/processes/**/*.bpmn20.xml
数据库初始化 :首次启动,Flowable 会自动创建约 70+ 张表 ,前缀分别为
ACT_RE_(仓库)、ACT_RU_(运行时)、ACT_HI_(历史)、ACT_GE_(通用)。
5. BPMN 2.0 建模实战
以员工请假审批流程为例,演示完整的 BPMN 建模:
xml
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:flowable="http://flowable.org/bpmn"
targetNamespace="http://flowable.org/bpmn">
<process id="leaveApproval" name="员工请假审批" isExecutable="true">
<!-- 开始事件 -->
<startEvent id="startEvent" name="提交申请"/>
<sequenceFlow id="flow1" sourceRef="startEvent" targetRef="applyTask"/>
<!-- 申请人填写请假单(用户任务) -->
<userTask id="applyTask" name="填写请假申请"
flowable:assignee="${initiator}">
<documentation>员工填写请假类型、开始/结束日期及请假原因</documentation>
</userTask>
<sequenceFlow id="flow2" sourceRef="applyTask" targetRef="managerGateway"/>
<!-- 排他网关:判断请假天数 -->
<exclusiveGateway id="managerGateway" name="天数判断"/>
<!-- 3天以内 → 直属经理审批 -->
<sequenceFlow id="flow3" sourceRef="managerGateway" targetRef="managerApprove">
<conditionExpression>${leaveDays <= 3}</conditionExpression>
</sequenceFlow>
<!-- 超过3天 → HR审批 -->
<sequenceFlow id="flow4" sourceRef="managerGateway" targetRef="hrApprove">
<conditionExpression>${leaveDays > 3}</conditionExpression>
</sequenceFlow>
<!-- 经理审批任务 -->
<userTask id="managerApprove" name="经理审批"
flowable:candidateGroups="managers">
<extensionElements>
<flowable:taskListener event="complete"
class="com.example.listener.ApprovalNotifyListener"/>
</extensionElements>
</userTask>
<!-- HR 审批任务 -->
<userTask id="hrApprove" name="HR审批"
flowable:candidateGroups="hr">
</userTask>
<sequenceFlow id="flow5" sourceRef="managerApprove" targetRef="approvalGateway"/>
<sequenceFlow id="flow6" sourceRef="hrApprove" targetRef="approvalGateway"/>
<!-- 排他网关:审批结果判断 -->
<exclusiveGateway id="approvalGateway" name="审批结果"/>
<sequenceFlow id="flow7" sourceRef="approvalGateway" targetRef="sendNotifyTask">
<conditionExpression>${approved == true}</conditionExpression>
</sequenceFlow>
<sequenceFlow id="flow8" sourceRef="approvalGateway" targetRef="rejectTask">
<conditionExpression>${approved == false}</conditionExpression>
</sequenceFlow>
<!-- 审批通过:发送通知(服务任务) -->
<serviceTask id="sendNotifyTask" name="发送审批通知"
flowable:delegateExpression="${notificationService}"/>
<!-- 审批拒绝:退回申请 -->
<userTask id="rejectTask" name="驳回通知"
flowable:assignee="${initiator}"/>
<sequenceFlow id="flow9" sourceRef="sendNotifyTask" targetRef="endEvent"/>
<sequenceFlow id="flow10" sourceRef="rejectTask" targetRef="endEvent"/>
<!-- 结束事件 -->
<endEvent id="endEvent" name="流程结束"/>
</process>
</definitions>
6. 流程部署与启动
typescript
@Service
@Slf4j
public class LeaveProcessService {
@Autowired
private RepositoryService repositoryService;
@Autowired
private RuntimeService runtimeService;
/**
* 部署流程定义
*/
public Deployment deployProcess() {
Deployment deployment = repositoryService.createDeployment()
.addClasspathResource("processes/leave-approval.bpmn20.xml")
.name("员工请假审批流程")
.category("HR")
.tenantId("company-a") // 多租户支持
.deploy();
log.info("流程部署成功,部署ID: {}", deployment.getId());
return deployment;
}
/**
* 启动流程实例
*
* @param employeeId 员工ID
* @param leaveDays 请假天数
* @param reason 请假原因
*/
public ProcessInstance startLeaveProcess(String employeeId,
int leaveDays,
String reason) {
Map<String, Object> variables = new HashMap<>();
variables.put("initiator", employeeId);
variables.put("leaveDays", leaveDays);
variables.put("reason", reason);
variables.put("applyTime", LocalDateTime.now().toString());
// 通过流程定义Key启动(自动选用最新版本)
ProcessInstance instance = runtimeService
.startProcessInstanceByKey("leaveApproval", variables);
log.info("流程实例已启动,实例ID: {}, 流程定义ID: {}",
instance.getId(), instance.getProcessDefinitionId());
return instance;
}
/**
* 查询流程定义列表
*/
public List<ProcessDefinition> queryProcessDefinitions() {
return repositoryService.createProcessDefinitionQuery()
.processDefinitionKey("leaveApproval")
.orderByProcessDefinitionVersion()
.desc()
.list();
}
}
7. 用户任务与候选人机制
scss
@Service
@Slf4j
public class TaskProcessService {
@Autowired
private TaskService taskService;
/**
* 查询我的待办任务(被指派给我)
*/
public List<Task> queryMyTasks(String userId) {
return taskService.createTaskQuery()
.taskAssignee(userId)
.orderByTaskCreateTime()
.desc()
.list();
}
/**
* 查询候选任务(我所在组的待认领任务)
*/
public List<Task> queryCandidateTasks(String userId, List<String> groups) {
return taskService.createTaskQuery()
.taskCandidateOrAssigned(userId)
.taskCandidateGroupIn(groups)
.orderByTaskCreateTime()
.desc()
.list();
}
/**
* 认领任务(从候选人池中抢占)
*/
public void claimTask(String taskId, String userId) {
taskService.claim(taskId, userId);
log.info("用户 {} 认领了任务 {}", userId, taskId);
}
/**
* 完成任务(审批通过)
*/
public void completeApproval(String taskId, boolean approved, String comment) {
// 添加审批意见
taskService.addComment(taskId, null, approved ? "同意" : "拒绝", comment);
Map<String, Object> variables = new HashMap<>();
variables.put("approved", approved);
taskService.complete(taskId, variables);
log.info("任务 {} 已完成,审批结果: {}", taskId, approved ? "通过" : "拒绝");
}
/**
* 任务转办
*/
public void delegateTask(String taskId, String targetUserId) {
taskService.delegateTask(taskId, targetUserId);
}
/**
* 查询任务详情(含流程变量)
*/
public TaskDetail getTaskDetail(String taskId) {
Task task = taskService.createTaskQuery()
.taskId(taskId)
.includeProcessVariables()
.singleResult();
if (task == null) {
throw new RuntimeException("任务不存在: " + taskId);
}
return TaskDetail.builder()
.taskId(task.getId())
.taskName(task.getName())
.assignee(task.getAssignee())
.createTime(task.getCreateTime())
.processVariables(task.getProcessVariables())
.build();
}
}
8. 网关与条件分支
Flowable 支持四种网关类型:
8.1 排他网关(Exclusive Gateway / XOR)
xml
<exclusiveGateway id="xorGateway"/>
<!-- 只走第一个满足条件的分支 -->
<sequenceFlow sourceRef="xorGateway" targetRef="taskA">
<conditionExpression>${amount < 1000}</conditionExpression>
</sequenceFlow>
<sequenceFlow sourceRef="xorGateway" targetRef="taskB">
<conditionExpression>${amount >= 1000}</conditionExpression>
</sequenceFlow>
8.2 并行网关(Parallel Gateway / AND)
xml
<!-- 分叉:同时激活所有分支 -->
<parallelGateway id="forkGateway"/>
<!-- 汇合:等待所有分支完成 -->
<parallelGateway id="joinGateway"/>
8.3 包容网关(Inclusive Gateway / OR)
xml
<!-- 满足条件的分支都会激活,至少激活一个 -->
<inclusiveGateway id="orGateway"/>
<sequenceFlow sourceRef="orGateway" targetRef="taskA">
<conditionExpression>${needManagerApprove}</conditionExpression>
</sequenceFlow>
<sequenceFlow sourceRef="orGateway" targetRef="taskB">
<conditionExpression>${needLegalApprove}</conditionExpression>
</sequenceFlow>
8.4 事件网关(Event Gateway)
xml
<!-- 等待第一个到来的事件,只走对应分支 -->
<eventBasedGateway id="eventGateway"/>
<sequenceFlow sourceRef="eventGateway" targetRef="timerCatch"/>
<sequenceFlow sourceRef="eventGateway" targetRef="messageCatch"/>
9. 服务任务与 JavaDelegate
9.1 实现 JavaDelegate
typescript
@Component("notificationService")
@Slf4j
public class NotificationServiceDelegate implements JavaDelegate {
@Autowired
private EmailService emailService;
@Override
public void execute(DelegateExecution execution) {
// 获取流程变量
String initiator = (String) execution.getVariable("initiator");
Integer leaveDays = (Integer) execution.getVariable("leaveDays");
Boolean approved = (Boolean) execution.getVariable("approved");
String subject = approved ? "请假申请已批准" : "请假申请被拒绝";
String content = String.format(
"您的 %d 天请假申请已%s。", leaveDays, approved ? "批准" : "拒绝");
emailService.send(initiator + "@company.com", subject, content);
// 设置输出变量(供后续节点使用)
execution.setVariable("notifyTime", LocalDateTime.now().toString());
execution.setVariable("notifyStatus", "SENT");
log.info("通知邮件已发送至员工: {}", initiator);
}
}
9.2 表达式调用
xml
<!-- 方式一:delegateExpression(Spring Bean) -->
<serviceTask flowable:delegateExpression="${notificationService}"/>
<!-- 方式二:class(直接实例化) -->
<serviceTask flowable:class="com.example.delegate.NotificationDelegate"/>
<!-- 方式三:expression(调用方法) -->
<serviceTask flowable:expression="${myBean.sendNotification(execution)}"/>
10. 事件监听与流程变量
10.1 全局事件监听器
typescript
@Component
public class FlowableEventListener implements FlowableEventListener {
@Override
public void onEvent(FlowableEvent event) {
if (event instanceof FlowableProcessStartedEvent startedEvent) {
String processInstanceId = startedEvent.getProcessInstanceId();
log.info("流程实例启动: {}", processInstanceId);
// 可在此记录审计日志、初始化业务数据等
}
if (event instanceof FlowableActivityEvent activityEvent) {
if (FlowableEngineEventType.TASK_COMPLETED
.equals(activityEvent.getType())) {
log.info("任务完成: {}", activityEvent.getActivityId());
}
}
}
@Override
public boolean isFailOnException() {
return false; // 监听器异常不影响主流程
}
@Override
public boolean isFireOnTransactionLifecycleEvent() {
return false;
}
@Override
public String getOnTransaction() {
return null;
}
}
10.2 注册监听器
arduino
@Configuration
public class FlowableConfig {
@Autowired
private FlowableEventListener eventListener;
@Bean
public EngineConfigurationConfigurer<SpringProcessEngineConfiguration> configurer() {
return config -> {
config.setEventListeners(List.of(eventListener));
// 异步历史配置
config.setAsyncHistoryEnabled(true);
config.setAsyncHistoryExecutorActivate(true);
};
}
}
10.3 流程变量操作
ini
// 设置变量
runtimeService.setVariable(processInstanceId, "key", value);
runtimeService.setVariables(processInstanceId, variableMap);
// 获取变量
Object value = runtimeService.getVariable(processInstanceId, "key");
Map<String, Object> allVars = runtimeService.getVariables(processInstanceId);
// 局部变量(仅在当前执行流可见)
runtimeService.setVariableLocal(executionId, "localKey", value);
11. REST API 集成
Flowable 提供开箱即用的 REST API,引入依赖即可启用:
xml
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-spring-boot-starter-rest</artifactId>
<version>6.8.0</version>
</dependency>
常用 REST 接口
bash
# 部署流程
POST /process-api/repository/deployments
Content-Type: multipart/form-data
# 查询流程定义
GET /process-api/repository/process-definitions?key=leaveApproval
# 启动流程实例
POST /process-api/runtime/process-instances
{
"processDefinitionKey": "leaveApproval",
"variables": [
{"name": "initiator", "value": "user001"},
{"name": "leaveDays", "value": 3}
]
}
# 查询我的任务
GET /process-api/runtime/tasks?assignee=user001
# 完成任务
POST /process-api/runtime/tasks/{taskId}
{
"action": "complete",
"variables": [
{"name": "approved", "value": true}
]
}
# 查询历史流程实例
GET /process-api/history/historic-process-instances?processDefinitionKey=leaveApproval
12. 生产实践与最佳实践
12.1 性能优化
yaml
flowable:
# 开启异步历史,避免历史写入阻塞主流程
async-history:
enabled: true
# 异步执行器配置
async-executor:
core-pool-size: 8
max-pool-size: 16
queue-capacity: 1000
# 锁定时间(防止节点宕机后任务死锁)
async-job-lock-time-in-millis: 300000
12.2 多租户设计
scss
// 部署时指定租户
repositoryService.createDeployment()
.tenantId("tenant-001")
.addClasspathResource("processes/flow.bpmn20.xml")
.deploy();
// 启动时指定租户
runtimeService.startProcessInstanceByKeyAndTenantId(
"processKey", variables, "tenant-001");
// 查询时过滤租户
taskService.createTaskQuery()
.taskTenantId("tenant-001")
.list();
12.3 事务管理
less
@Service
@Transactional
public class BusinessFlowService {
/**
* 业务操作与流程操作在同一事务中
* 任意一方失败,全部回滚
*/
public void submitLeaveApplication(LeaveApplication application) {
// 1. 保存业务数据
leaveApplicationRepo.save(application);
// 2. 启动流程(同一事务)
Map<String, Object> vars = new HashMap<>();
vars.put("applicationId", application.getId());
vars.put("initiator", application.getEmployeeId());
vars.put("leaveDays", application.getDays());
runtimeService.startProcessInstanceByKey("leaveApproval", vars);
// 若此处抛出异常,业务数据和流程都会回滚
}
}
12.4 流程版本管理
scss
// Flowable 自动管理版本,每次部署同一 key 的流程,版本号自动递增
// 建议:生产环境使用 category + 语义版本 进行管理
repositoryService.createDeployment()
.addClasspathResource("processes/leave-v2.bpmn20.xml")
.name("请假流程 v2.0")
.category("HR-LEAVE")
.deploy();
// 迁移旧实例到新版本
ProcessInstanceMigrationBuilder migrationBuilder =
processMigrationService.createProcessInstanceMigrationBuilder()
.migrateToProcessDefinition("leaveApproval", 2)
.addActivityMigrationMapping(
ActivityMigrationMapping.createMappingFor("oldTaskId", "newTaskId"));
migrationBuilder.migrate(processInstanceId);
12.5 常见坑点汇总
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 流程无法推进 | 条件表达式变量不存在 | 启动时传入所有必要变量,或使用 ${leaveDays != null && leaveDays > 3} |
| 任务被重复认领 | 未做幂等检查 | 认领前校验 task.getAssignee() == null |
| 历史数据丢失 | historyLevel 设置过低 | 根据查询需求选择合适的历史级别 |
| 定时任务不触发 | 异步执行器未启动 | 确认 asyncExecutorActivate=true |
| 并行分支无法汇合 | 子执行流变量不一致 | 使用 execution.setVariable 而非局部变量 |
| 大量 Job 堆积 | 线程池配置不当 | 增大线程池,检查 ServiceTask 是否存在长耗时操作 |
13. 总结
Flowable 凭借其对 BPMN 2.0 标准的完整实现、与 Spring 生态的无缝集成,以及丰富的企业级特性(多租户、异步历史、DMN 决策表),成为 Java 生态中工作流引擎的首选之一。
核心要点回顾:
- 通过 RepositoryService 管理流程定义的生命周期
- 通过 RuntimeService 控制流程实例的运行时状态
- 通过 TaskService 处理人工节点的任务流转
- 合理配置历史级别 和异步执行以平衡功能与性能
- 将业务事务 与流程操作纳入同一事务保证数据一致性
💡 推荐阅读 :Flowable 官方文档 · Flowable GitHub
更新时间:2026-04 | 基于 Flowable 6.8.x