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

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

本文从架构原理、核心概念、实战代码三个维度,系统剖析 Flowable 工作流引擎,帮助开发者快速掌握流程建模、部署、执行与监控的完整链路。Flowable 是一个轻量级、高性能的开源业务流程管理(BPM)平台,支持 BPMN 2.0、CMMN 1.1 和 DMN 1.1 标准,适用于各种企业级应用。

1. 什么是 Flowable

Flowable 是一个轻量级、高性能的开源业务流程管理(BPM)平台,脱胎于 Activiti,由原 Activiti 核心团队于 2016 年分叉维护,经过多年演进已形成完整的企业级工作流生态。

与同类产品对比

特性FlowableActivitiCamunda
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=leaveApproval

12. 生产实践与最佳实践

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: 300000

12.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