Flowable 深度解析:现代企业级工作流引擎的核心与实践

摘要:本文从架构原理、核心概念、实战代码三个维度,系统剖析 Flowable 工作流引擎,帮助开发者快速掌握流程建模、部署、执行与监控的完整链路。


目录

  1. 什么是 Flowable
  2. 核心架构与模块
  3. 核心概念详解
  4. 环境搭建与快速入门
  5. BPMN 2.0 建模实战
  6. 流程部署与启动
  7. 用户任务与候选人机制
  8. 网关与条件分支
  9. 服务任务与 JavaDelegate
  10. 事件监听与流程变量
  11. REST API 集成
  12. 生产实践与最佳实践
  13. 总结

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

相关推荐
NCIN EXPE2 小时前
SpringBoot Test详解
spring boot·后端·log4j
2601_949194262 小时前
springboot之集成Elasticsearch
spring boot·后端·elasticsearch
splage3 小时前
Spring Cloud Data Flow 简介
后端·spring·spring cloud
woniu_maggie3 小时前
SAP RESTful 接口服务发布教程
后端
用户279420831323 小时前
临时解决 Mac SSH 客户端与服务器算法不匹配问题
后端
小锋java12343 小时前
LangChain4j 来了,Java AI智能体开发再次起飞。。。
java·人工智能·后端
一点一一3 小时前
nestjs+langchain:Prompt Template
人工智能·后端
Oneslide3 小时前
低版本kubernetes节点卡死重置恢复方法
后端
spring2997923 小时前
Spring Boot 实战篇(四):实现用户登录与注册功能
java·spring boot·后端