Flowable 深度解析:现代企业级工作流引擎的核心与实践
- 开发
- 10天前
- 17热度
- 0评论
Flowable 深度解析:现代企业级工作流引擎的核心与实践
本文从架构原理、核心概念、实战代码三个维度,系统剖析 Flowable 工作流引擎,帮助开发者快速掌握流程建模、部署、执行与监控的完整链路。Flowable 是一个轻量级、高性能的开源业务流程管理(BPM)平台,支持 BPMN 2.0、CMMN 1.1 和 DMN 1.1 标准,适用于各种企业级应用。
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. 核心架构与模块
graph TD
A[Flowable 生态]
A --> B[flowable-engine (BPMN流程)]
A --> C[flowable-cmmn-engine (案例管理)]
A --> D[flowable-dmn-engine (决策表)]
A --> E[flowable-content (内容管理)]
A --> F[flowable-spring-boot-starter]
F --> G[数据库层 (MyBatis + 连接池)]
G --> H[H2 / MySQL / PostgreSQL / Oracle / MSSQL]七大核心 Service
| Service | 职责 |
|---|---|
| RepositoryService | 流程定义的部署、查询、删除 |
| RuntimeService | 流程实例的启动、查询、变量操作 |
| TaskService | 用户任务的查询、认领、完成 |
| HistoryService | 历史数据查询(已完成的流程/任务) |
| ManagementService | 数据库表管理、Job 管理 |
| IdentityService | 用户与组的管理 |
| FormService | 表单数据的读写(需开启表单模块) |
3. 核心概念详解
3.1 流程定义 vs 流程实例
流程定义 (ProcessDefinition)
├── 类比:Java Class(模板)
└── 对应:.bpmn20.xml 文件
流程实例 (ProcessInstance)
├── 类比:Java Object(运行时对象)
└── 每次启动流程 = 创建一个新实例3.2 执行流 (Execution)
Flowable 使用令牌(Token)模型驱动流程推进:
- 流程实例本身是根执行流
- 并行网关会产生多个子执行流
- 每个执行流独立推进,互不阻塞
3.3 历史级别
flowable:
history-level: full # none | activity | audit | full| 级别 | 说明 |
|---|---|
| none | 不记录历史,性能最高 |
| activity | 记录活动开始/结束 |
| audit | 记录变量变化(默认) |
| full | 记录表单字段、所有详情 |
4. 环境搭建与快速入门
4.1 Maven 依赖
<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 配置
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+ 张表,前缀分别为 ACTRE(仓库)、ACTRU(运行时)、ACTHI(历史)、ACTGE(通用)。
5. BPMN 2.0 建模实战
以员工请假审批流程为例,演示完整的 BPMN 建模:
<?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. 流程部署与启动
@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;
}
/**
* 启动流程实例
*/
public ProcessInstance startProcess(String initiator, int leaveDays) {
Map<String, Object> variables = new HashMap<>();
variables.put("initiator", initiator);
variables.put("leaveDays", leaveDays);
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("leaveApproval", variables);
log.info("流程实例启动成功,实例ID: {}", processInstance.getId());
return processInstance;
}
}代码解释
- 部署流程定义:使用 RepositoryService 的 createDeployment 方法部署流程定义文件。
- 启动流程实例:使用 RuntimeService 的 startProcessInstanceByKey 方法启动流程实例,并传递初始变量。
以上内容详细介绍了 Flowable 工作流引擎的基本概念、核心架构、环境搭建和实战代码。通过这些内容,开发者可以快速上手并应用 Flowable 到实际项目中。下一部分将继续深入探讨用户任务与候选人机制、网关与条件分支等内容。
7. 用户任务与候选人机制
在 Flowable 中,用户任务(User Task)是工作流中的重要组成部分,用于处理需要人工干预的任务。候选人机制则允许将任务分配给特定的用户或用户组,从而实现更灵活的任务管理。
@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();
}
}7.1 待办任务查询
通过 taskAssignee 方法可以查询分配给特定用户的任务,并按创建时间降序排列。这有助于用户快速查看和处理最新的任务。
7.2 候选任务查询
taskCandidateOrAssigned 方法用于查询用户所在组的待认领任务,结合 taskCandidateGroupIn 方法可以指定多个组。这使得任务分配更加灵活,适用于团队协作场景。
7.3 任务认领
claimTask 方法允许用户从候选人池中认领任务,确保任务不会被重复认领。认领后,任务将从候选人池中移除,分配给具体的用户。
7.4 任务完成
completeApproval 方法用于完成任务,并根据审批结果设置相应的流程变量。此外,还可以添加审批意见,便于后续审计和追踪。
7.5 任务转办
delegateTask 方法允许将任务转交给其他用户,适用于需要进一步处理或审核的情况。
7.6 任务详情查询
getTaskDetail 方法返回任务的详细信息,包括任务 ID、名称、分配人、创建时间和流程变量。这些信息对于任务管理和审计非常有用。
8. 网关与条件分支
Flowable 支持多种类型的网关,用于控制流程的分支和合并。合理使用网关可以实现复杂的业务逻辑。
8.1 排他网关(Exclusive Gateway / XOR)
排他网关根据条件表达式选择一个分支执行,其他分支将被忽略。
<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)
并行网关用于分叉和汇合,分叉时所有分支同时激活,汇合时等待所有分支完成。
<!-- 分叉:同时激活所有分支 -->
<parallelGateway id="forkGateway"/>
<!-- 汇合:等待所有分支完成 -->
<parallelGateway id="joinGateway"/>8.3 包容网关(Inclusive Gateway / OR)
包容网关根据条件表达式选择多个分支执行,至少激活一个分支。
<!-- 满足条件的分支都会激活,至少激活一个 -->
<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)
事件网关等待第一个到来的事件,然后选择对应的分支执行。
<!-- 等待第一个到来的事件,只走对应分支 -->
<eventBasedGateway id="eventGateway"/>
<sequenceFlow sourceRef="eventGateway" targetRef="timerCatch"/>
<sequenceFlow sourceRef="eventGateway" targetRef="messageCatch"/>9. 服务任务与 JavaDelegate
服务任务(Service Task)用于执行自动化操作,可以通过实现 JavaDelegate 接口来定义自定义任务。
9.1 实现 JavaDelegate
@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 表达式调用
服务任务可以通过多种方式调用 JavaDelegate 实现:
<!-- 方式一:delegateExpression(Spring Bean) -->
<serviceTask flowable:delegateExpression="${notificationService}"/>
<!-- 方式二:class(直接实例化) -->
<serviceTask flowable:class="com.example.delegate.NotificationDelegate"/>
<!-- 方式三:expression(调用方法) -->
<serviceTask flowable:expression="${myBean.sendNotification(execution)}"/>10. 事件监听与流程变量
事件监听器(Event Listener)可以捕获流程中的各种事件,用于日志记录、审计和业务逻辑处理。流程变量则用于在流程节点之间传递数据。
10.1 全局事件监听器
@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 注册监听器
@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 流程变量操作
// 设置变量
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,方便与其他系统集成。只需引入相应的依赖即可启用 REST 功能。
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-spring-boot-starter-rest</artifactId>
<version>6.8.0</version>
</dependency>常用 REST 接口
# 部署流程
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=leaveApproval12. 生产实践与最佳实践
12.1 性能优化
通过合理的配置,可以显著提升 Flowable 在生产环境中的性能。
flowable:
# 开启异步历史,避免历史写入阻塞主流程
async-history:
enabled: true
# 异步执行器配置
async-executor:
core-pool-size: 8
max-pool-size: 16
queue-capacity: 1000
# 锁定时间(防止节点宕机后任务死锁)
async-job-lock-time-in-millis: 30000012.2 多租户设计
多租户支持使得 Flowable 可以在一个实例中管理多个独立的业务流程。
// 部署时指定租户
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 事务管理
确保业务操作和流程操作在同一事务中,以保证数据的一致性。
@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 流程版本管理
合理管理流程版本,确保新旧实例的平滑迁移。
// 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