Skip to content

Flowable:BPMN 流程如何从模型走到运行时

Flowable 是一组可嵌入 Java 应用的流程引擎。BPMN 引擎负责可预先建模的业务流程,CMMN 引擎处理更依赖事件和人工判断的案例,DMN 引擎执行决策表;Event Registry、身份、任务、变量和作业服务提供跨引擎共享能力。应用既可以直接调用 Java API,也可以把引擎放在服务端,通过 REST 接入。

从源码看,BPMN 图不会被逐节点解释成一串递归方法调用。部署阶段先把 XML 转成模型,为节点挂接 ActivityBehavior;运行阶段再把一次 API 调用包装成 Command,在同一个 CommandContext 中不断消费 Agenda 队列里的 operation。流程走到 User Task、Receive Task 或异步节点时,内存调用停止,执行位置被写入数据库,下一次外部事件或作业线程再恢复执行。

这套结构同时解释了 Flowable 的优势和成本。服务 API、命令拦截器、持久化 Session、流程行为与作业服务彼此分层,扩展点很多;另一方面,完整仓库包含 90 个 flowable-* 模块,配置项、共享服务和兼容层也很多。表达式、脚本、Java Delegate、HTTP、Mail 等节点还可能执行带副作用的业务逻辑,生产环境必须把模型发布权、可调用 Bean、网络出口和凭据管理当作安全边界。

下面先画出全仓库的职责地图,再以 BPMN 主引擎为中心,沿 Spring Boot 启动、模型部署、流程启动、人工任务完成和异步作业五条链路深入。CMMN、DMN、Event Registry 和 Flowable 5 兼容模块只说明各自如何接入,不声称逐一覆盖全部模块。

GitHub 仓库信息

信息内容
项目标题Flowable
项目描述面向开发者、系统管理员和业务用户的轻量工作流与业务流程管理平台,提供 Java 编写的 BPMN 流程、CMMN 案例和 DMN 决策引擎。
GitHub 仓库flowable/flowable-engine
官网地址Flowable 官网
主要开发语言GitHub Languages API 的动态元数据快照(2026-09-04,不能绑定 source_commit):Java 94.3%、JavaScript 4.95%、SQLPL 0.21%
开源许可证Apache-2.0
最近代码更新2026-09-02(GitHub pushed_at,核对日期:2026-09-04)

仓库结构与模块职责

根 POM 默认聚合模型、共享服务和核心引擎,deploydistro 等 Maven Profile 再引入 Spring、REST、兼容层和集成模块。下面的目录树按语义裁剪,不等于完整模块清单。

text
flowable-engine/
├── modules/
│   ├── flowable-engine-common-api/      # 通用 Engine、Command、事件与异步接口
│   ├── flowable-engine-common/          # CommandContext、拦截器、Session、锁与 MyBatis 基础
│   ├── flowable-bpmn-model/             # BPMN 内存模型
│   ├── flowable-bpmn-converter/         # BPMN XML 与模型转换
│   ├── flowable-process-validation/     # BPMN 语义校验
│   ├── flowable-engine/                 # BPMN API、命令、Agenda、行为、实体与查询
│   ├── flowable-task-service[-api]/     # 跨引擎任务实体和持久化服务
│   ├── flowable-variable-service[-api]/ # 变量类型、实体和持久化服务
│   ├── flowable-job-service[-api]/      # 异步、定时、暂停与死信作业
│   ├── flowable-eventsubscription-*     # 消息、信号等事件订阅
│   ├── flowable-cmmn-*                  # CMMN 模型、转换、校验与案例引擎
│   ├── flowable-dmn-*                   # DMN 模型、XML 转换与决策引擎
│   ├── flowable-event-registry-*        # 外部事件模型、通道和引擎集成
│   ├── flowable-spring/                 # Spring 事务、生命周期与资源自动部署
│   ├── flowable-spring-boot/            # Starter、自动配置和样例
│   ├── flowable-rest/                   # BPMN REST 资源
│   └── flowable5-*                      # 历史流程定义兼容路径
├── docs/docusaurus/docs/                # 官方文档源码
├── distro/sql/                          # 多数据库建表与升级脚本
├── tooling/archetypes/                  # 测试工程脚手架
├── build-tools/                         # Checkstyle 等构建规则
└── pom.xml                              # 8.1.0-SNAPSHOT、JDK 17、模块与 Profile
语义层关键模块或文件职责与上下游
公开 APIRepositoryServiceRuntimeServiceTaskServiceManagementService接收部署、启动、任务和作业操作,大多数实现立即转交给 CommandExecutor
命令基础设施AbstractEngineConfigurationCommandContext组装日志、事务、上下文和自定义拦截器,统一 Session flush、关闭和异常传播
BPMN 部署flowable-bpmn-converterflowable-process-validationBpmnDeployer把 XML 解析为模型,执行 XSD/语义校验,持久化流程定义并刷新缓存和事件订阅
BPMN 运行时ProcessInstanceHelperFlowableEngineAgendaContinueProcessOperation建立执行树,计划 operation,执行节点行为并决定同步、等待或异步分流
共享状态服务flowable-task-serviceflowable-variable-serviceflowable-identitylink-service为 BPMN、CMMN 等引擎提供任务、变量和参与者关系的统一实体与持久化能力
后台作业flowable-job-service查询到期作业、写入锁 owner 和到期时间、提交线程池、执行 Handler、重试或转死信
宿主集成flowable-springflowable-spring-bootflowable-rest将引擎放入 Spring 生命周期,发现流程资源,暴露 Bean 或 HTTP 入口

五个核心机制

API 最终汇入 Command

Command 是描述一次引擎操作及其参数的对象。RuntimeServiceImpl.startProcessInstanceByKey()RepositoryServiceImpl.deploy()TaskServiceImpl.complete() 的共同点,是把参数封装成命令后交给 CommandExecutor。默认拦截器链包含日志、数据库特定重试、事务、CommandContext 和最终命令执行器;前后还可以插入自定义 CommandInterceptor

CommandContext 是一次引擎调用的工作单元。CommandContext 保存本次命令打开的 Session、异常和关闭监听器:正常结束先通知监听器,再 flush Session;失败时触发失败监听器;无论成功与否都关闭 Session。Spring 版本用 SpringTransactionInterceptor 接入宿主事务,嵌套服务调用还可以复用当前上下文。这意味着一个 API 返回成功,通常也意味着同一命令中的持久化刷新已经完成;运行到异步边界后的业务结果则不在这一事务承诺里。

部署把模型节点变成可执行行为

BPMN XML 先由 BpmnXMLConverter 转为 BpmnModel,再经过 ProcessValidator 做语义校验。解析处理器随后把 User Task、Service Task、Gateway、Event 等模型元素绑定到具体 ActivityBehavior。运行期因此无需重新解释 XML,只需从流程定义缓存取得模型,并调用当前节点已经挂接的行为。

部署不只是保存一份文件。BpmnDeployer 还会计算定义版本和 ID,写入授权,更新定时器和事件订阅,生成可选流程图,刷新流程定义缓存。重复部署过滤、租户、派生部署和 Flowable 5 兼容会在同一链路中改变行为。

Agenda 如何推进执行

每次根命令都有一个 Agenda,也就是当前命令的待执行 operation 队列。命令本身可以把 ContinueProcessOperationTakeOutgoingSequenceFlowsOperationTriggerExecutionOperation 等 operation 放入队列;CommandInvoker 持续取出并执行,直到队列为空。一个 operation 又能计划后续 operation,所以网关分支、子流程、边界事件和结束处理都能在同一事务内继续推进。

流程图的连线描述控制流,真正推进这些连线的是 Agenda 队列;execution tree 则保存流程实例、子流程和并行分支的运行位置。排查“为什么没有走到下一节点”时,应同时看当前 ExecutionEntity、当前 FlowElement、对应行为以及 Agenda 计划了什么,单看 Java 调用栈不够。

等待状态把控制权交回数据库

User Task 创建 TaskEntity 后不会主动计划离开当前节点。当前 execution 保留在该活动上,任务、变量和参与者关系写入 ACT_RU_* 表,调用结束。稍后 TaskService.complete() 删除或归档任务,计划 TriggerExecutionOperation,再由 UserTaskActivityBehavior.trigger() 离开节点。

Receive Task、消息捕获、定时器和外部事件也遵循类似原则。等待状态把执行位置持久化到数据库,当前线程随命令结束;JVM 可以在等待期间重启,另一个应用节点也可以根据同一数据库恢复流程。

异步作业把事务切成两段

当 FlowNode 标记为异步时,ContinueProcessOperation 不执行节点行为,而是创建 JobEntity 并调度。当前 API 事务只承诺作业已落库;后台获取线程再查询到期作业、设置锁 owner 与锁过期时间、放入执行线程池,并在新事务中调用 JobHandler。

异步节点把流程切成两个事务阶段:数据库作业先成为可靠交接点,线程池再作为后续消费者。集群节点通过锁字段和乐观锁竞争工作;失败作业在独立新事务中减少重试次数,进入定时作业等待下一次尝试,耗尽后进入死信表。

源码环境与运行边界

固定源码的根 POM 声明 8.1.0-SNAPSHOT 和 JDK 17。2026 年 9 月 4 日核对到的最新公开 Release 是 flowable-8.0.0,发布于 2026 年 2 月 27 日。Release 说明明确 Flowable 8 升级到 Spring Framework 7、Spring Boot 4 和 Jackson 3;快照源码仍保留 Jackson2JsonObjectRestVariableConverter 这条 Jackson 2 REST 兼容路径,不能把 8.1 快照行为直接等同于 8.0 Release。

本次完成了固定提交的静态阅读、根 POM 内容哈希比对和文档校验,没有运行目标仓库的 Maven 构建、JUnit、数据库、Spring Boot 应用、REST 服务或异步执行器。下面是复现路径,不是亲测记录。

获取同一份源码

前置条件是 Git、JDK 17 和可用的 Maven。选择空目录执行:

bash
git clone https://github.com/flowable/flowable-engine.git
cd flowable-engine
git checkout d6d39ce1c69ff244f2d9dc6af756a9b95e865586
java -version
mvn -version

git status --short --branch 应显示 detached HEAD 或该固定提交,并且没有意外改动。若只阅读 BPMN 主引擎,可先构建相关反应器模块:

bash
mvn -pl modules/flowable-engine -am -DskipTests package

预期成功信号是 Maven 以 BUILD SUCCESS 结束,并在相关模块生成 target/ 产物。此命令显式跳过测试;要验证测试,应另行运行测试目标并查看测试汇总:

bash
mvn -pl modules/flowable-engine -am test

核心 API 片段

官方入门文档使用独立 ProcessEngineConfiguration 配合 JDBC 数据库,也支持 Spring Boot Starter。Flowable 8 的 Spring 集成面向 Spring Boot 4.x。下面是围绕已配置好的三个 Service 的核心 API 片段,不是可单独编译的最小程序;数据库、引擎 Bean、BPMN 资源和 import 需要由宿主工程提供。最小验证不应停在应用启动,至少要完成部署、启动和人工任务续行:

java
Map<String, Object> variables = Map.of("days", 3);

Deployment deployment = repositoryService.createDeployment()
    .addClasspathResource("processes/holiday-request.bpmn20.xml")
    .deploy();

ProcessInstance instance = runtimeService
    .startProcessInstanceByKey("holidayRequest", variables);

Task task = taskService.createTaskQuery()
    .processInstanceId(instance.getId())
    .singleResult();

taskService.complete(task.getId(), Map.of("approved", true));

可观察结果应包括:部署查询能找到流程定义;启动后 ACT_RU_EXECUTION 有运行实例;走到 User Task 时任务查询返回一条任务;完成后任务消失并出现下一个等待状态或流程结束。若使用自动建表,开发环境可启用 schema update;生产环境更适合先审阅 distro/sql 或模块内数据库脚本并显式迁移。

整体架构地图

Mermaid 流程图
查看源码
flowchart TB
    Caller[Java API / REST / Spring Bean] --> Services[Repository / Runtime / Task / Management Services]
    Services --> Executor[CommandExecutor 拦截器链]
    Executor --> Context[CommandContext + 事务 + Session]
    Context --> Deploy[部署命令]
    Context --> Runtime[运行命令]
    Context --> TaskCmd[任务命令]
    subgraph Definition[定义阶段]
        XML[BPMN XML] --> Converter[BpmnXMLConverter]
        Converter --> Validator[ProcessValidator]
        Validator --> Parser[BpmnParse handlers]
        Parser --> Model[BpmnModel + ActivityBehavior]
        Model --> Cache[流程定义缓存]
    end
    Deploy --> Definition
    Runtime --> Agenda[FlowableEngineAgenda]
    TaskCmd --> Agenda
    Cache --> Agenda
    Agenda --> Behavior[ActivityBehavior]
    Behavior --> Sync[同步续行]
    Behavior --> Wait[User Task / Message / Timer 等等待状态]
    Behavior --> Async[JobEntity]
    Context --> DB[(ACT_RE / ACT_RU / ACT_HI)]
    Wait --> DB
    Async --> DB
    DB --> Acquire[异步与定时作业获取]
    Acquire --> Pool[执行线程池]
    Pool --> Handler[JobHandler]
    Handler --> Agenda
    CMMN[CMMN Engine] --> Shared[任务 / 变量 / 作业 / 事件订阅服务]
    DMN[DMN Engine] --> Shared
    Event[Event Registry] --> Shared
    Shared --> DB

为了避免把 BPMN 深挖误读成“所有引擎都已逐链路展开”,各引擎在本文中的源码覆盖边界如下:

引擎或入口固定源码入口本文覆盖未展开部分
BPMNProcessEngineConfigurationImpl深入启动、部署、执行、人工任务和异步作业全部 90 个语义模块的逐文件审计
CMMNCmmnEngineConfiguration引擎装配和共享任务、变量、作业服务边界plan item 生命周期和完整案例链路
DMNDmnEngineConfiguration决策引擎角色及与 BPMN 的集成位置决策表解析和逐规则求值
Event RegistryEventRegistryEngineConfiguration外部事件通道和共享服务边界入站、出站通道的完整消息链路
RESTProcessInstanceCollectionResource作为 Java API 之上的传输适配器REST 资源逐接口分析、认证部署和权限配置

这张矩阵说明了一个重要边界:共享服务的存在可以从固定源码确认,但 CMMN、DMN 和 Event Registry 的完整业务语义仍需要各自的专门走读。

想理解的能力阅读入口继续向下读
引擎如何创建AbstractBuildableEngineConfiguration.buildEngine()ProcessEngineConfigurationImpl.init()ProcessEngineImpl
Spring Boot 如何装配ProcessEngineAutoConfigurationProcessEngineServicesAutoConfigurationProcessEngineFactoryBeanSpringProcessEngineConfiguration
API 的事务边界AbstractEngineConfiguration.initCommandInterceptors()CommandContextInterceptorTransactionContextInterceptorCommandContext.close()
BPMN 如何部署RepositoryServiceImpl.createDeployment()DeployCmdBpmnDeployerParsedDeploymentBuilderBpmnParse
流程如何启动RuntimeServiceImpl.startProcessInstanceByKey()StartProcessInstanceCmdProcessInstanceHelperCommandInvoker
人工任务如何恢复UserTaskActivityBehaviorCompleteTaskCmdTaskHelperTriggerExecutionOperation
异步作业如何集群执行AcquireAsyncJobsDueRunnableAcquireJobsCmdExecuteAsyncRunnableJobRetryCmd
动态跳转如何实现RuntimeService.createChangeActivityStateBuilder()ChangeActivityStateBuilderImplChangeActivityStateCmdDefaultDynamicStateManager

引擎启动链路

独立引擎

Mermaid 流程图
查看源码
sequenceDiagram
    participant App as 应用
    participant Config as ProcessEngineConfigurationImpl
    participant Common as AbstractBuildableEngineConfiguration
    participant Engine as ProcessEngineImpl
    participant DB as 数据库
    participant Async as AsyncExecutor
    App->>Common: buildEngine()
    Common->>Config: init()
    Config->>Config: 初始化命令、服务、解析器、缓存、实体管理器、作业服务
    Common->>Engine: createEngine()
    Engine->>DB: 执行 schema management command
    Engine->>Engine: 注册 ProcessEngine
    Engine->>Async: 构造阶段按配置启动已激活执行器
    Common->>Config: postEngineBuildConsumer.accept(engine)

buildEngine() 只做四步:初始化配置、准备构建后回调、创建引擎、执行回调。真正复杂的是 ProcessEngineConfigurationImpl.init()ProcessEngineConfigurationImpl.init() 按依赖顺序创建命令设施、表达式、数据源、服务、解析器、缓存、实体管理器、部署器、事件分发器和异步执行器。

ProcessEngineImpl 构造函数取得各项 Service,在命令事务中执行 schema management,注册引擎,并按配置启动已激活的异步执行器;buildEngine() 随后执行 post-build consumer。关闭时则先注销引擎、停止两个异步执行器,再关闭配置和通知生命周期监听器。

Spring Boot 引擎

Spring Boot 路线增加了两层宿主控制。ProcessEngineAutoConfiguration 创建 SpringProcessEngineConfiguration,注入 DataSource、事务管理器、异步执行器和自动部署资源;ProcessEngineServicesAutoConfiguration 再创建 ProcessEngineFactoryBean。FactoryBean 调用 buildProcessEngine(),并在销毁时关闭引擎。

SpringProcessEngineConfiguration 关闭了“引擎构造后立即启动执行器”的默认行为。Spring 生命周期进入 start() 后,start() 才遍历已构建引擎,调用 startExecutors() 并按 deployment mode 自动部署 classpath 中发现的流程资源;自动部署位置、后缀和检查开关由 FlowableProperties 暴露。这个顺序避免后台线程在 Spring 容器尚未完全就绪时抢先工作,也让 Flowable 的事务交给 PlatformTransactionManager 管理。

BPMN 部署链路

Mermaid 流程图
查看源码
sequenceDiagram
    participant App as 应用
    participant Repo as RepositoryServiceImpl
    participant Cmd as DeployCmd
    participant Manager as DeploymentManager
    participant Deployer as BpmnDeployer
    participant Parse as BpmnParse
    participant DB as ACT_RE_* / 作业与事件表
    participant Cache as 定义缓存
    App->>Repo: createDeployment().add...().deploy()
    Repo->>Cmd: CommandExecutor.execute(DeployCmd)
    Cmd->>DB: 插入 DeploymentEntity
    Cmd->>Manager: deploy(deployment, settings)
    Manager->>Deployer: deploy()
    Deployer->>Parse: XML 转模型、XSD/语义校验、绑定行为
    Parse-->>Deployer: ParsedDeployment
    Deployer->>DB: 定义版本、授权、定时器、事件订阅
    Deployer->>Cache: 更新模型和流程定义缓存

DeploymentBuilderImpl.deploy() 回到 RepositoryServiceImpl.deploy(),后者执行 DeployCmd。命令先处理 Flowable 5 兼容和重复部署过滤,再保存 DeploymentEntity,最后交给 DeploymentManager 中注册的 deployer。

ParsedDeploymentBuilder 只挑选 BPMN 资源。BpmnParse.execute() 依次执行 XML 转换、可选 XSD 校验、语义校验、parse handler 和图形信息处理;错误会中断整个命令。随后 BpmnDeployer.deploy() 检查 definition key 冲突,为新版本计算 ID,持久化定义和授权,更新定时器与事件,再刷新缓存。

部署阶段把“业务图”转换为“可执行定义”。若部署失败,运行时不会得到半成品模型,因为部署与定义写入处在同一个命令事务中。

流程实例启动链路

RuntimeServiceImpl.startProcessInstanceByKey() 创建 StartProcessInstanceCmd。命令按 key、tenant 和 fallback 规则解析流程定义,校验启动表单后交给 ProcessInstanceHelper。后者拒绝已挂起定义,从缓存取得 Process 和起始元素,创建根 execution、初始子 execution、变量、身份关系、历史记录与事件订阅,然后计划 ContinueProcessOperation

Mermaid 流程图
查看源码
flowchart TD
    API[startProcessInstanceByKey] --> Cmd[StartProcessInstanceCmd]
    Cmd --> Definition{找到可用定义?}
    Definition -- 否 --> Error[抛出未找到或挂起异常]
    Definition -- 是 --> Helper[ProcessInstanceHelper]
    Helper --> Root[创建 process instance execution]
    Root --> Child[创建指向 StartEvent 的子 execution]
    Child --> State[变量 / 历史 / 身份关系 / 事件订阅]
    State --> Plan[planContinueProcessOperation]
    Plan --> Invoker[CommandInvoker 消费 Agenda]
    Invoker --> Continue[ContinueProcessOperation]
    Continue --> Behavior[ActivityBehavior.execute]
    Behavior --> Next[计划后续 operation 或进入等待状态]

CommandInvokerwhile (!agenda.isEmpty()) 是同步推进的核心。ContinueProcessOperation 读取 execution 当前元素:遇到 Sequence Flow 就处理监听器和目标节点;遇到 FlowNode 就创建边界事件,选择同步、多实例或异步路径,再调用已经绑定的行为。

同步 Service Task 可能在一次 startProcessInstanceByKey() 中直接执行。若 Delegate 抛出普通运行时异常,当前命令事务回滚;若模型在前面设置了 async="true",启动事务只创建作业,Delegate 的异常由后台作业重试链处理。这是建模层直接改变事务边界的例子。

人工任务链路

创建任务并停在等待状态

UserTaskActivityBehavior.execute() 创建任务,解析名称、到期时间、优先级、表单、assignee、owner、候选用户和候选组。任务插入后才建立人员关系,因为 identity link 需要 task ID。创建监听器与 TASK_CREATED 事件在同一命令内触发。

如果 skip expression 判定跳过,任务会被立即删除并继续离开节点;普通 User Task 不调用 leave(),所以 Agenda 耗尽后事务结束,execution 留在当前节点。这正是可持久化等待状态。

完成任务并恢复流程

Mermaid 流程图
查看源码
sequenceDiagram
    participant Client as 调用方
    participant Task as TaskServiceImpl
    participant Cmd as CompleteTaskCmd
    participant Helper as TaskHelper
    participant Agenda as FlowableEngineAgenda
    participant Trigger as TriggerExecutionOperation
    participant Behavior as UserTaskActivityBehavior
    Client->>Task: complete(taskId, variables)
    Task->>Cmd: CommandExecutor.execute()
    Cmd->>Helper: completeTask()
    Helper->>Helper: 写变量、执行 complete listener、记录历史、删除任务
    Helper->>Agenda: planTriggerExecutionOperation(execution)
    Agenda->>Trigger: run()
    Trigger->>Behavior: trigger(execution)
    Behavior->>Behavior: 确认活动任务已删除
    Behavior->>Agenda: leave() 并计划 outgoing sequence flows

CompleteTaskCmd 先拒绝用 BPMN API 完成 CMMN 创建的任务,并保留 Flowable 5 兼容分流。TaskHelper.completeTask() 写入流程变量和本地变量,触发 complete listener 与事件,记录完成人和历史,再完成任务实体。若监听器抛出 BPMN Business Error,错误会按模型传播,不再执行普通续行;否则计划 TriggerExecutionOperation

TriggerExecutionOperation 要求当前行为实现 TriggerableActivityBehaviorUserTaskActivityBehavior.trigger() 会检查同一 execution 下是否还有未删除任务,确认后才 leave()。这能防止绕过任务完成直接 signal execution。

异步作业链路

Mermaid 流程图
查看源码
flowchart LR
    AsyncNode[异步 FlowNode] --> Create[创建 JobEntity]
    Create --> JobTable[(ACT_RU_JOB)]
    JobTable --> Acquire[AcquireAsyncJobsDueRunnable]
    Acquire --> Lock[AcquireJobsCmd 设置 lock owner / expiration]
    Lock --> Pool[AsyncExecutor 线程池]
    Pool --> ReFetch[ExecuteAsyncRunnableJobCmd 重新查询作业]
    ReFetch --> Handler[JobManager -> JobHandler]
    Handler -- 成功 --> Continue[恢复 Agenda 并删除作业]
    Handler -- 失败 --> Listener[FailedJobListener]
    Listener --> NewTx[REQUIRES_NEW JobRetryCmd]
    NewTx --> Timer[(ACT_RU_TIMER_JOB)]
    NewTx --> Dead[(ACT_RU_DEADLETTER_JOB)]
    Timer --> Acquire

AcquireAsyncJobsDueRunnable 先看线程池剩余容量,再执行 AcquireJobsCmd。命令查询可执行作业,把本节点标识和锁到期时间写入每个作业。多个节点同时获取时,乐观锁异常被视为集群中的正常竞争;还可以选择全局获取锁,让节点轮流填充队列。

ExecuteAsyncRunnable 对 exclusive job 额外锁定流程实例,避免同一实例的并发作业交错修改执行树。事务型作业通过 ExecuteAsyncRunnableJobCmd 重新查询数据库,因为排队期间作业可能已被边界事件或另一分支删除;确认仍存在后才交给 JobManager 和具体 JobHandler。

失败路径不会在已回滚事务里直接修改重试次数。FailedJobListener.closeFailure() 使用 transactionRequiresNew() 创建新命令,BPMN 的 JobRetryCmd 读取模型上的 retry time cycle。仍有次数时,作业移入 timer job 并计算下一次到期时间;次数耗尽或异常不可恢复时,作业进入 dead letter。异常消息和堆栈也随新作业实体保存。

分支、失败与扩展

分流条件源码行为排障入口
流程定义不存在或已挂起启动命令在创建 execution 前抛错StartProcessInstanceCmdProcessInstanceHelper
BPMN 校验有 errorBpmnParse 汇总错误并抛出 FlowableException;warning 只记录日志部署异常、资源名、XSD 和 ProcessValidator 输出
节点同步执行行为与后续 operation 留在当前命令事务API 异常、事务回滚、执行监听器与 Delegate
节点 async="true"当前事务创建作业,后台事务执行行为ACT_RU_JOB、锁字段、Async Executor 日志
User Task skip expression 为真任务插入后立即删除,不触发普通任务事件并直接续行模型表达式、变量值、SkipExpressionUtil
complete listener 抛 BPMN Error错误按模型传播,普通 TriggerExecutionOperation 不再计划Task listener、边界错误事件、execution 当前节点
作业排队时已被删除执行命令重新查询后直接跳过ExecuteAsyncRunnableJobCmd 的调试日志
作业执行失败新事务减少重试并转 timer job;耗尽后转 dead lettertimer job、dead letter、异常堆栈
集群竞争同一作业lock owner、lock expiration 与乐观锁决定归属获取线程日志、数据库时钟、锁过期配置
Flowable 5 流程定义部署、启动、任务和作业命令进入兼容 Handlerflowable5-* classpath 与兼容开关
扩展需求合适入口代价与边界
给所有 API 增加审计、租户或重试自定义 CommandInterceptor位于事务主路径,错误会影响所有命令
在引擎初始化前后替换组件EngineConfigurator.beforeInit() / configure()依赖初始化顺序和 priority,升级时需复核内部对象
实现业务 Service TaskJavaDelegate、delegate expression能访问流程变量和 Spring Bean,也可能执行任意副作用
实现新的 BPMN 节点行为ActivityBehaviorFactory / ActivityBehavior属于内部 SPI,需要理解 Agenda、execution 和历史记录
监听任务、流程和作业事件BPMN listener、FlowableEventListener同步 listener 可能延长事务;异常语义取决于监听器类型
运行中跳转、撤回或加签ChangeActivityStateBuilder会重写执行树和关联任务,应覆盖并发、多实例、子流程与历史测试

安全上最容易被低估的是模型内容。ProcessExpressionManager 接收可调用的 Bean 映射;DefaultActivityBehaviorFactory 为 Class Delegate、Delegate Expression、Script、Shell、HTTP 和 Mail 等模型行为创建执行对象。流程定义应像可执行代码一样经过版本控制、评审和发布授权,REST 暴露也应另行配置认证、租户隔离和网络策略。

数据表如何对应运行状态

前缀典型表含义
ACT_RE_*ACT_RE_DEPLOYMENTACT_RE_PROCDEFACT_RE_MODELRepository,部署资源和流程定义
ACT_RU_*ACT_RU_EXECUTIONACT_RU_TASKACT_RU_VARIABLEACT_RU_JOBRuntime,当前执行树、任务、变量和作业
ACT_HI_*历史流程、活动、任务和变量表History,已经发生的审计与查询数据

当流程停在 User Task 时,重点关联 ACT_RU_EXECUTIONACT_RU_TASK 和 identity link;异步节点则增加 ACT_RU_JOB。执行结束后,EndExecutionOperation 会清理运行时执行数据;是否还能查询完整轨迹取决于历史级别和异步历史配置。直接改表会绕过实体缓存、版本字段、事件和历史管理,不适合作为常规修复手段。

设计取舍

命令统一事务边界。 对外 Service 不自行拼接事务和持久化细节,拦截器链让日志、重试、上下文与扩展按统一顺序工作。代价是自定义拦截器位于所有 API 的主路径,错误影响面很大。

模型与行为分离。 部署期完成解析和行为绑定,运行期围绕 execution 与 behavior 工作,既保留 BPMN 语义,也允许替换行为工厂。内部 SPI 与初始化顺序较复杂,跨大版本升级需要重新核对。

Agenda 显式表达控制流。 operation 队列为网关、边界事件、子流程和调试监听提供统一执行面。理解问题时需要同时掌握 execution tree、模型节点和 Agenda,学习成本高于普通状态机。

等待状态和作业都持久化。 长流程不占用线程,异步交接、集群竞争、重试和死信也有数据库状态可查。数据库因此同时承担业务状态和协调压力,索引、清理、锁等待与时钟一致性都进入运维范围。

多引擎共享基础服务。 BPMN 与 CMMN 复用任务、变量、作业和身份关系服务,但保留各自语义。共享降低重复,也让配置器、schema 和生命周期之间的关系更难从单一模块看全。

适用场景与同类选择

Flowable 适合需要 BPMN 2.0 可视化流程、人工任务、长期等待、审计历史、定时器和 Java/Spring 深度嵌入的系统,例如审批、订单履约、客户服务和跨系统编排。若流程主要由开发者以代码定义、没有 BPMN 建模与人工任务需求,代码优先的持久执行框架可能更直接;若只是简单状态枚举,完整流程引擎会引入过多概念和表。

方案建模与运行形态更适合的侧重点采用时应核对
FlowableJava 可嵌入或 REST;BPMN、CMMN、DMN 与共享服务Spring 应用中的流程、案例、决策和人工任务统一引擎Flowable 8 基础栈、数据库容量、模型发布治理
Camunda 7Java 流程引擎与 BPMN/DMN 生态已有 Camunda 7 模型、插件和运维体系产品生命周期、版本支持与后续迁移路线
Temporal代码定义的持久工作流,不以 BPMN 图为核心开发者主导的分布式长事务、重试和补偿与业务建模人员的协作方式、SDK 与服务端运维

真正有区分度的问题是:谁维护流程,状态要保留多久,是否需要人工任务,业务代码能否接受引擎侵入,数据库或独立服务由谁运维,以及升级是否允许改变 Spring/Jackson 基线。

构建与调试问题

下面的排障入口来自固定提交的静态源码阅读和 Flowable 官方配置文档;这些内容给出检查顺序与可观察状态,不代表本次任务已经在目标仓库运行验证。

启动时报数据库表不存在

先确认 schema update 配置与环境目标,参见 Flowable 数据库配置文档。开发环境可让引擎创建表;生产环境应使用对应数据库和版本的 SQL 脚本。验证时不要只看一张 ACT_RU_* 表,还要检查被启用的 common、identity、task、variable、job 和 history schema。

Spring Boot 启动了但流程没有自动部署

检查 FlowableProperties 中的 process-definition-location-prefix、文件后缀、check-process-definitions 和 deployment mode。自动配置先发现 Resource,再由 SpringProcessEngineConfiguration.start() 调用部署策略。日志中的 “No deployment resources were found for autodeployment” 表明没有发现资源,不等同于引擎创建失败。

完成任务后流程没有继续

先确认任务属于 BPMN 还是 CMMN;再检查 TaskHelper.completeTask() 是否完成任务、complete listener 是否传播了 BPMN Error、execution 当前节点是否仍为 User Task。普通路径会计划 TriggerExecutionOperation

异步节点长期不执行

AcquireAsyncJobsDueRunnable 的状态顺序排查:ACT_RU_JOB 是否有记录,due date 是否到期,lock owner 是否长期不释放,执行器是否 active,线程池是否满,作业是否已转到 timer、suspended 或 dead letter 表。集群中偶发乐观锁日志可能只是正常竞争,持续锁超时则要检查节点存活、数据库时钟和锁时长。

Service Task 重复调用外部系统

Flowable 的作业重试保证流程继续尝试,不自动保证外部副作用只发生一次。Delegate 调用支付、库存、通知等系统时应传递业务幂等键,并把“外部成功但本地事务回滚”的情况纳入对账或补偿设计。

升级到 Flowable 8 后依赖冲突

先核对 Flowable 8.0.0 Release 所说明的 Spring Boot 4、Spring Framework 7、Jackson 3 和 JUnit 5 基础栈。若现有应用仍固定在 Spring Boot 3,不应只替换 Flowable 版本号。Jackson 2 兼容层也不代表所有第三方扩展都已适配 Jackson 3。

后续阅读路径

准备扩展业务节点时,从 JavaDelegate、Service Task 解析处理器和 ActivityBehaviorFactory 开始;排查同步流程,从 Service API 进入 Command,再看 Agenda 和 execution;排查异步失败,则按 job、timer job、dead letter 的状态迁移阅读 AcquireAsyncJobsDueRunnableExecuteAsyncRunnableJobRetryCmd

若要理解 Flowable 全套平台,下一步再分别进入 CMMN 的 plan item 生命周期、DMN 的 decision execution、Event Registry 的入站与出站通道,以及这些引擎如何通过 EngineConfigurator 挂到 App Engine。阅读顺序仍应以一次真实业务链为主,不必按 90 个模块逐个展开。

阅读 Flowable 时可以沿四层定位问题:部署阶段把标准模型编译成带行为的内存定义;运行阶段用 Command 组织事务边界,用 Agenda 推进执行树;数据库保存等待位置,作业服务负责恢复异步工作。这样可以把复杂模块映射回一次具体业务链。

参考资料

全部公开文章由同一个站点构建和发布。