Article

工作流 Flowable

更新于:2026-07-15

第一章:Flowable 概述与入门

1.1 工作流与 BPMN 2.0 基础概念

概念名称说明注意事项
工作流(Workflow)指业务流程的自动化执行路径,用于协调人员、系统和任务之间的协作。在软件中通常表现为一系列按规则流转的任务。工作流不等于完整业务系统,需与其他模块(如用户管理、数据存储)集成。
业务流程管理(BPM)一种管理企业业务流程的方法论,旨在优化流程效率、透明度和可控性。Flowable 是一个轻量级 BPM 引擎。BPM 不仅是技术工具,也包含流程建模、分析、执行、监控等管理活动。
BPMN 2.0(Business Process Model and Notation)由 OMG 组织制定的标准流程建模语言,使用图形化符号描述业务流程。支持流程定义、协作、事件、网关等语义。BPMN 2.0 是 XML 格式的,可被引擎解析执行,不仅是绘图标准。
流程定义(Process Definition)描述一个业务流程的模板,通常以 .bpmn20.xml 文件形式存在,包含流程结构、任务、网关、事件等元素。同一 key 的流程定义每次部署会生成新版本,版本号递增。
流程实例(Process Instance)流程定义的一次具体执行,代表一个实际运行中的业务流程(如”张三的请假申请”)。一个流程定义可启动多个流程实例。
执行流(Execution)流程实例内部的执行路径,用于表示当前运行到哪个节点,支持并行分支等复杂路径。在并行网关中,一个流程实例可能对应多个执行流。
任务(Task)流程中需要人工或系统完成的工作单元,如”审批请假”、“发送邮件”等。用户任务(User Task)需人工处理,服务任务(Service Task)自动执行。
事件(Event)表示流程中发生的特定时刻,如开始、结束、消息到达、定时触发等。事件驱动流程行为,如边界事件可中断或附加到任务上。
网关(Gateway)控制流程分支与汇聚的元素,如排他网关(决策)、并行网关(分叉/汇聚)等。网关不执行任何操作,仅控制流程走向。

1.2 Flowable 简介与核心组件

概念名称说明注意事项
Flowable 项目一个开源的轻量级业务流程引擎,支持 BPMN 2.0、CMMN、DMN 标准,可用于自动化和管理业务流程。由 Activiti 分支发展而来,社区活跃,API 简洁,适合嵌入 Spring Boot 应用。
ProcessEngineFlowable 的核心执行引擎,负责管理流程的部署、启动、执行和监控。所有操作都通过其提供的服务接口进行。一个 JVM 中可存在多个 ProcessEngine,但通常只创建一个。
RepositoryService用于管理流程资源的服务,如部署流程定义、查询流程定义、获取流程图资源等。部署后流程定义不可修改,需重新部署新版本。
RuntimeService用于启动流程实例、操作流程变量、查询运行时执行流等。启动流程实例时可传入变量,用于流程决策或任务分配。
TaskService管理用户任务的服务,包括查询任务、办理任务、设置任务负责人等。仅对用户任务(User Task)有效,不能操作服务任务。
HistoryService提供对历史数据的查询功能,如已完成的流程实例、任务、变量等。历史数据保留依赖于历史级别(historyLevel)配置。
IdentityService简单的身份管理服务,用于管理用户、组及权限关系。功能较基础,生产环境常与 LDAP、数据库或 Spring Security 集成。
FormService管理流程表单的服务,支持动态表单定义与渲染。可与 Flowable Modeler 配合使用,实现无代码表单设计。
DynamicBpmnService允许在不重新部署的情况下动态修改流程定义的行为(如任务名称、监听器等)。仅支持部分属性的动态修改,不能改变流程结构。

1.3 Flowable 与其他工作流引擎对比(Activiti、Camunda)

对比项FlowableActivitiCamunda
起源从 Activiti 6 分支独立出来,由原核心团队开发Alfresco 公司发起,早期由 jBPM 团队主导由 Activiti 核心成员创建,专注于企业级流程引擎
社区与活跃度活跃,持续更新,GitHub 星标高Activiti 7 后转向云原生,社区分裂,活跃度下降非常活跃,文档完善,企业支持强
架构设计轻量级,模块化,易于嵌入 Spring Boot模块较多,7.x 版本重构较大模块清晰,支持微服务架构(Camunda Platform 8 为 Zeebe + Operate)
标准支持支持 BPMN 2.0、CMMN、DMN支持 BPMN 2.0,CMMN 和 DMN 支持有限完全支持 BPMN 2.0、CMMN、DMN
API 易用性API 简洁,与 Spring 集成良好API 较复杂,7.x 版本变化大API 清晰,文档丰富,学习曲线平缓
UI 工具提供 Flowable UI(Modeler、Task、Admin)提供 Activiti App,但功能较弱提供 Camunda Modeler(桌面)、Cockpit(Web 控制台)
企业支持有商业支持,但不如 Camunda 成熟Alfresco 提供企业版支持提供完善的企业版支持(监控、高可用、安全)
性能表现性能优秀,适合中小规模系统性能良好,但 7.x 版本稳定性曾受质疑性能优异,尤其在高并发场景下表现突出
扩展性支持插件、事件总线、自定义行为扩展机制较弱扩展性强,支持事件监听、外部任务等
适用场景中小型项目、Spring Boot 快速集成已有 Activiti 项目迁移大型企业、高可用、复杂流程场景

注意事项:

  • Flowable 和 Camunda 都源于 Activiti,但发展路径不同。
  • 若追求稳定性、企业级支持,Camunda 更优;若追求轻量、快速集成,Flowable 是良好选择。
  • Activiti 6 及之前版本稳定,但 7.x 重构后争议较大,社区推荐迁移到 Flowable 或 Camunda。

1.4 开发环境搭建与快速入门示例

本小节为实践引导,暂不涉及具体 API 方法,以下为环境准备与流程说明。

准备项说明注意事项
JDK 版本推荐使用 JDK 8 或以上版本Flowable 6.x 支持 JDK 8+,7.x 支持 JDK 11+
构建工具Maven 或 Gradle建议使用 Maven 管理依赖
数据库支持 H2(测试)、MySQL、PostgreSQL、Oracle 等需提前创建数据库,Flowable 自动建表
Spring Boot(推荐)使用 flowable-spring-boot-starter 快速集成可自动配置 ProcessEngine 和数据源
Flowable 依赖(Maven)org.flowable:flowable-spring-boot-starter:6.8.0版本需与 Spring Boot 兼容,建议使用最新稳定版
数据库建表策略Flowable 自动创建 ACT_ 开头的数据表第一次启动时自动建表,后续通过 databaseSchemaUpdate 控制

快速入门流程步骤:

  1. 创建 Spring Boot 项目
  2. 添加 Flowable 依赖
  3. 配置数据源
  4. resources/processes/ 下放置 .bpmn20.xml 文件
  5. 启动应用,自动部署流程
  6. 使用 RuntimeService 启动流程实例

.bpmn20.xml 文件名需与流程 id 一致或放在正确目录。

默认自动部署: 将 BPMN 文件放入 resources/processes/ 目录,Spring Boot 启动时自动部署。可通过 flowable.deployment-mode 配置部署行为。

核心表说明:

表前缀用途
ACT_RE_*资源存储(流程定义)
ACT_RU_*运行时数据
ACT_HI_*历史数据
ACT_ID_*身份数据

不要手动修改数据库表,应通过 API 操作。


第二章:Flowable 核心服务与 API 基础

2.1 ProcessEngine 与 Configuration 配置

方法/属性名称语法用途代码示例注意事项
ProcessEngines.init()ProcessEngines.init();初始化所有配置的 ProcessEngine 实例(基于 flowable.cfg.xmlProcessEngines.init(); ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();通常在应用启动时调用一次;Spring 环境下由容器自动管理,无需手动调用。
ProcessEngines.getProcessEngine(String name)ProcessEngines.getProcessEngine("engineName");获取指定名称的 ProcessEngine 实例ProcessEngine engine = ProcessEngines.getProcessEngine("default");名称需与配置文件中 <process-engine-name> 一致。
ProcessEngines.destroy()ProcessEngines.destroy();销毁所有已初始化的 ProcessEngine,释放资源ProcessEngines.destroy();应用关闭时调用,避免资源泄漏。
ProcessEngineConfiguration.createProcessEngineConfigurationFromResourceDefault()ProcessEngineConfiguration cfg = ProcessEngineConfiguration.createProcessEngineConfigurationFromResourceDefault();从默认配置文件 flowable.cfg.xml 创建配置对象ProcessEngineConfiguration cfg = ProcessEngineConfiguration.createProcessEngineConfigurationFromResourceDefault(); ProcessEngine engine = cfg.buildProcessEngine();配置文件必须位于 classpath:flowable.cfg.xml
ProcessEngineConfiguration.createStandaloneProcessEngineConfiguration()ProcessEngineConfiguration.createStandaloneProcessEngineConfiguration();创建独立运行的配置对象,适用于非 Spring 环境ProcessEngineConfiguration cfg = ProcessEngineConfiguration.createStandaloneProcessEngineConfiguration(); cfg.setJdbcUrl("jdbc:h2:mem:flowable"); cfg.setJdbcUsername("sa"); cfg.setJdbcPassword(""); cfg.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE); cfg.setAsyncExecutorActivate(false); ProcessEngine engine = cfg.buildProcessEngine();需手动设置数据源、事务管理器等。
cfg.setDatabaseSchemaUpdate(...)cfg.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);控制数据库表结构更新行为参见上例可选值:false(不更新)、true(启动时自动建表/更新)、create-drop(启动建表,关闭删表,测试用)。
cfg.setAsyncExecutorActivate(...)cfg.setAsyncExecutorActivate(true);是否启用异步执行器(用于定时任务、异步服务任务)参见上例生产环境建议开启,提高性能。
cfg.setHistoryLevel(...)cfg.setHistoryLevel(HistoryLevel.FULL);设置历史数据记录级别cfg.setHistoryLevel(HistoryLevel.AUDIT);可选值:NONEACTIVITYAUDITFULL;级别越高,存储越多,性能影响越大。
cfg.buildProcessEngine()ProcessEngine engine = cfg.buildProcessEngine();构建并返回 ProcessEngine 实例参见上例调用后会连接数据库并初始化表结构(若配置允许)。

说明: ProcessEngine 是 Flowable 的核心,所有服务(如 RepositoryService)都通过它获取。

2.2 核心服务接口概述(RepositoryService、RuntimeService 等)

服务接口获取方式用途注意事项
RepositoryServiceprocessEngine.getRepositoryService()管理流程资源:部署流程、查询流程定义、挂起/激活流程等是流程定义的”仓库”,不涉及运行时数据。
RuntimeServiceprocessEngine.getRuntimeService()管理运行时流程实例:启动实例、操作流程变量、查询执行流等用于流程实例的生命周期控制。
TaskServiceprocessEngine.getTaskService()管理用户任务:查询、办理、委派、设置负责人等仅对 userTask 有效,不能操作服务任务。
HistoryServiceprocessEngine.getHistoryService()查询历史数据:已完成的流程实例、任务、变量等依赖 historyLevel 配置,否则无数据。
ManagementServiceprocessEngine.getManagementService()引擎管理和数据库操作:获取表元数据、执行原生 SQL、作业管理等用于运维和监控,慎用原生 SQL。
IdentityServiceprocessEngine.getIdentityService()管理用户、组、权限关系内置功能简单,生产环境建议与外部系统集成。
FormServiceprocessEngine.getFormService()管理流程表单:获取表单数据、提交表单等支持动态表单,可与 Flowable UI 配合使用。
DynamicBpmnServiceprocessEngine.getDynamicBpmnService()动态修改流程定义行为(如任务名称、表达式),无需重新部署仅支持部分属性,不能修改流程结构(如增删节点)。

说明: 这些服务是线程安全的,可在多线程环境中共享使用。

2.3 流程部署与资源管理

方法名称语法用途代码示例注意事项
repositoryService.createDeployment()repositoryService.createDeployment()创建一个部署构建器Deployment deployment = repositoryService.createDeployment().addClasspathResource("processes/simple-process.bpmn20.xml").name("请假流程").deploy();每次调用 deploy() 会创建一次新部署。
.addClasspathResource(String resource).addClasspathResource("processes/demo.bpmn")添加类路径下的资源文件同上支持 BPMN、PNG、FORM 等文件。
.addInputStream(String resourceName, InputStream inputStream).addInputStream("dynamic.bpmn", inputStream)从输入流添加资源try (InputStream is = new FileInputStream("path/to/process.bpmn")) { repositoryService.createDeployment().addInputStream("dynamic.bpmn", is).deploy(); }适用于动态生成的流程文件。
.setDeploymentName(String name).setDeploymentName("报销流程V1")设置部署名称同上便于在历史记录中识别。
.setCategory(String category).setCategory("finance")设置部署分类repositoryService.createDeployment().addClasspathResource("expense.bpmn20.xml").category("finance").deploy();可用于流程定义分类查询。
.deploy().deploy()执行部署操作,返回 Deployment 对象同上部署后流程定义即生效,可启动实例。
repositoryService.deleteDeployment(String deploymentId)repositoryService.deleteDeployment("dep1000");删除指定部署// 删除部署,若有运行中的实例会抛异常 repositoryService.deleteDeployment("dep1000"); // 强制删除,级联删除相关流程实例 repositoryService.deleteDeployment("dep1000", true);第二个参数 cascade=true 表示级联删除所有相关流程实例和历史数据。
repositoryService.getDeploymentResourceNames(String deploymentId)List<String> names = repositoryService.getDeploymentResourceNames("dep1000");获取某次部署包含的所有资源文件名List<String> resources = repositoryService.getDeploymentResourceNames("dep1000"); for (String name : resources) { System.out.println(name); }可用于判断是否包含 PNG 图或 FORM 文件。
repositoryService.getResourceAsStream(String deploymentId, String resourceName)InputStream is = repositoryService.getResourceAsStream("dep1000", "simple-process.bpmn20.xml");获取部署资源的输入流try (InputStream is = repositoryService.getResourceAsStream("dep1000", "diagram.png")) { // 处理图片流 }常用于返回流程图给前端展示。

2.4 流程定义查询与管理

方法名称语法用途代码示例注意事项
repositoryService.createProcessDefinitionQuery()repositoryService.createProcessDefinitionQuery()创建流程定义查询对象List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery().processDefinitionKey("leave").latestVersion().list();支持链式调用添加查询条件。
.processDefinitionKey(String key).processDefinitionKey("leave")按流程定义的 id(即 key)查询同上对应 BPMN 文件中 <process id="leave">
.processDefinitionName(String name).processDefinitionName("请假流程")按流程定义名称查询ProcessDefinition def = repositoryService.createProcessDefinitionQuery().processDefinitionName("请假流程").singleResult();名称可重复,建议结合 key 使用。
.latestVersion().latestVersion()只查询每个 key 的最新版本同上非常常用,避免查到旧版本。
.version(int version).version(2)按具体版本号查询ProcessDefinition def = repositoryService.createProcessDefinitionQuery().processDefinitionKey("leave").version(2).singleResult();用于精确查询某版本。
.deploymentId(String id).deploymentId("dep1000")按部署 ID 查询所属流程定义List<ProcessDefinition> defs = repositoryService.createProcessDefinitionQuery().deploymentId("dep1000").list();一次部署可包含多个流程定义。
.suspended() / .active().suspended()查询已挂起或激活状态的流程定义List<ProcessDefinition> suspendedDefs = repositoryService.createProcessDefinitionQuery().suspended().list();挂起后无法启动新实例。
.count().count()返回查询结果数量long count = repositoryService.createProcessDefinitionQuery().active().count();用于分页统计。
.list() / .listPage(int first, int max).list() / .listPage(0, 10)返回结果列表或分页结果List<ProcessDefinition> page = repositoryService.createProcessDefinitionQuery().orderByProcessDefinitionVersion().asc().listPage(0, 10);分页避免内存溢出。
.singleResult().singleResult()返回唯一结果,若无或多个则返回 null 或抛异常ProcessDefinition def = repositoryService.createProcessDefinitionQuery().processDefinitionKey("leave").latestVersion().singleResult();适用于精确查询场景。
repositoryService.suspendProcessDefinitionById(String id)repositoryService.suspendProcessDefinitionById("leave:2:123")挂起指定流程定义repositoryService.suspendProcessDefinitionById("leave:2:123");挂起后不能启动新实例,但已有实例可继续执行。
repositoryService.activateProcessDefinitionById(String id)repositoryService.activateProcessDefinitionById("leave:2:123")激活已挂起的流程定义repositoryService.activateProcessDefinitionById("leave:2:123");激活后可重新启动新实例。
repositoryService.deleteProcessDefinition(String id)repositoryService.deleteProcessDefinition("leave:2:123")删除指定流程定义不允许直接删除,需通过删除部署实现流程定义不可单独删除,必须删除其所属部署。

说明: 流程定义的 id 格式为 key:version:generated-id,例如 leave:3:12345


第三章:流程定义与 BPMN 2.0 元素

3.1 流程定义结构(process、id、name、version)

概念名称说明注意事项
<process> 元素BPMN 2.0 文件中的根元素之一,定义一个完整的业务流程。包含任务、网关、事件等节点和连接线。必须位于 .bpmn20.xml 文件中,且文件根为 <definitions>
id 属性流程的唯一标识符(Key),在代码中用于启动流程、查询定义等。对应 Java 中的 key。只能包含字母、数字、连字符和下划线;一旦设定不建议修改;同一 id 多次部署会生成不同版本。
name 属性流程的可读名称,用于展示给用户,无技术约束。可包含中文、空格等,便于业务人员理解。
isExecutable 属性布尔值,表示该流程是否可执行。true 表示可被引擎解析运行,false 仅用于建模或文档。非可执行流程不会被部署到运行时。
version流程定义的版本号,由引擎自动生成。每次部署相同 id 的流程时版本递增。版本号从 1 开始;可通过 latestVersion() 查询最新版本。
key 与 id 的关系Flowable 中 process id 即为流程定义的 key,用于标识一类流程。不要与数据库主键混淆;key + version 才能唯一确定一个流程定义。
流程定义 ID(Full ID)格式为 key:version:generated-id,例如 leave:2:12345,是数据库中的唯一标识。在 API 中常作为参数使用,如挂起、查询等操作。

3.2 开始事件与结束事件

概念名称说明注意事项
开始事件(Start Event)流程的入口,表示流程实例的触发点。必须有且仅有一个(不包括事件子流程)。类型包括:空开始事件、消息开始事件、定时开始事件等。
空开始事件(None Start Event)最常见的开始事件,无特定触发条件,调用 startProcessInstanceByKey() 即可启动。适用于人工触发的流程,如”提交请假申请”。
消息开始事件(Message Start Event)通过接收指定名称的消息来启动流程。需调用 startProcessInstanceByMessage() 触发;可用于外部系统集成。
定时开始事件(Timer Start Event)按照设定的时间规则(如 cron 表达式)自动启动流程。仅支持顶级流程(非子流程);需启用异步执行器。
结束事件(End Event)表示流程或子流程的终止点。到达结束事件后,流程实例或执行流结束。可有多个结束事件,表示不同终止路径。
空结束事件(None End Event)默认结束事件,表示正常结束,不抛出异常。到达后流程实例状态变为”已完成”。
错误结束事件(Error End Event)抛出一个错误,可被边界错误事件或错误事件子流程捕获。必须设置 errorRef 属性;用于异常处理流程。
终止结束事件(Terminate End Event)立即终止整个流程实例,包括所有并行分支。使用需谨慎,可能导致未完成任务被丢弃。

3.3 用户任务(User Task)与任务分配

概念名称说明注意事项
用户任务(User Task)需要人工参与完成的任务,如审批、填写表单等。在任务列表中可见。对应 TaskService 操作。
assignee 属性指定任务的办理人,格式为用户 ID。任务直接分配给该用户。语法:assignee="zhangsan";该用户可在个人任务列表中看到任务。
candidateUsers 属性指定任务的候选人列表,任务出现在这些用户的”可领取任务”列表中。语法:candidateUsers="zhangsan,lisi";需先 claim 才能办理。
candidateGroups 属性指定任务的候选组,组内所有用户均可领取任务。语法:candidateGroups="managers";依赖身份服务中的组信息。
formKey 属性关联一个表单模板,用于前端渲染任务表单。语法:formKey="leaveForm";可通过 FormService 获取表单数据。
dueDate 属性设置任务的截止日期,可用于提醒或超时处理。支持表达式,如 ${dateUtils.addDays(now, 3)}
priority 属性设置任务优先级(0-100),数字越大优先级越高。默认为 50;可用于任务排序。
任务分配表达式使用 ${} 表达式动态设置 assignee 或 candidateUsers。示例:assignee="${initiator}"candidateUsers="${approverList}";变量需在启动流程时传入。

3.4 排他网关(Exclusive Gateway)与流程分支

概念名称说明注意事项
排他网关(Exclusive Gateway)也称”决策网关”,根据条件表达式选择一条 outgoing 路径继续执行。图标为菱形内部带 ‘X’;只选择一条路径。
条件表达式(Condition Expression)设置在顺序流(Sequence Flow)上的表达式,决定是否选择该路径。语法:${variable == 'value'};使用 Spring EL 表达式。
默认流(Default Flow)当所有条件都不满足时,执行的默认路径。通过 default 属性指定。语法:在网关上设置 default="flow2",指向某条 sequenceFlow。
流程分支排他网关后接多条顺序流,形成”if-else”结构。所有 outgoing 流必须有唯一条件,或设置默认流。
条件求值顺序引擎按顺序评估每条路径的条件,选择第一个为 true 的路径。建议将最可能满足的条件放在前面。
无默认流的风险若无默认流且所有条件为 false,流程将阻塞,无路径可执行。必须确保至少有一条路径可执行,否则流程卡住。

3.5 并行网关(Parallel Gateway)与流程汇聚

概念名称说明注意事项
并行网关(Parallel Gateway)用于分叉(Fork)或汇聚(Join)并行执行流。图标为菱形内部带 ’+‘。同一个网关可同时作为分叉和汇聚点。
并行分叉(Parallel Fork)将一个执行流拆分为多个并行执行流,所有 outgoing 路径同时执行。无需条件表达式;所有路径都会被执行。
并行汇聚(Parallel Join)等待所有 incoming 执行流完成,然后合并为一个执行流继续。必须等待所有分支完成,否则阻塞。
汇聚行为所有来自分叉的执行流必须全部到达汇聚网关,才能继续向下执行。若某一分支未完成,其他分支即使完成也需等待。
使用场景适用于可并行处理的任务,如”财务审批”和”人事审批”同时进行。不能用于条件分支的汇聚,应使用”包容网关”(Inclusive Gateway)。
错误使用示例在汇聚网关的 incoming 流上设置条件表达式。并行网关的 incoming 流不允许有条件,否则行为未定义。

3.6 服务任务(Service Task)与 JavaDelegate

概念名称说明注意事项
服务任务(Service Task)自动执行的任务,由系统完成,无需人工干预。如调用外部 API、发送邮件等。图标为圆角矩形底部带齿轮。
class 属性指定实现 JavaDelegate 接口的 Java 类。语法:class="com.example.SendEmailDelegate";类必须有无参构造函数。
expression 属性使用表达式调用 Spring Bean 的方法。语法:expression="${emailService.sendMail(execution)}";需在 Spring 中注册 Bean。
delegateExpression 属性表达式返回一个 JavaDelegate 实例。语法:delegateExpression="${myDelegate}";更灵活,支持运行时决定。
resultVariable 属性将服务任务的执行结果存储到指定变量中。语法:resultVariable="sendResult";默认覆盖 result 变量。
JavaDelegate 接口自定义逻辑的核心接口,实现 execute(DelegateExecution execution) 方法。方法内可通过 execution 获取流程变量、设置变量等。
异步执行(async)设置 async="true" 可将服务任务放入异步队列执行。需启用异步执行器;提高吞吐量,但增加复杂性。
排他执行(exclusive)设置 exclusive="true"(默认)表示同一流程实例的异步任务串行执行。避免数据竞争;可设为 false 允许并行异步执行。

3.7 脚本任务(Script Task)与表达式

概念名称说明注意事项
脚本任务(Script Task)执行一段脚本代码的任务,如 Groovy、JavaScript、Python 等。图标为圆角矩形底部带文档和代码符号。
scriptFormat 属性指定脚本语言类型,如 groovyjavascript语法:scriptFormat="groovy";需 JVM 支持该语言引擎。
script 内容在 BPMN 文件中直接编写脚本代码。示例:<script>execution.setVariable("x", 10);</script>
autoStoreVariables 属性是否自动将脚本中定义的变量保存到流程变量中。true 时,Groovy 脚本中 x=1 会自动存为流程变量。
表达式(Expression)在条件、赋值等场景使用 ${} 语法,基于 Spring EL。示例:${order.amount > 1000} 用于网关条件判断。
变量访问脚本中可通过 execution 对象访问流程变量和 API。Groovy 示例:def user = execution.getVariable("assignee")
性能与安全脚本任务性能低于 JavaDelegate,且存在安全风险。不建议在生产环境使用复杂脚本或动态代码。

3.8 接收任务(Receive Task)与外部信号

概念名称说明注意事项
接收任务(Receive Task)等待外部系统显式触发才能继续的等待任务。用于集成异步回调。图标为圆角矩形底部带信封。
waitStateType内部状态类型为 receive-task,表示处于等待状态。任务不会自动完成。
完成方式必须通过 API 调用 signalExecutionById()trigger() 来继续流程。示例:runtimeService.trigger(executionId);
使用场景适用于”等待第三方支付结果”、“等待人工确认”等场景。避免长时间阻塞,建议结合超时边界事件。
与用户任务区别接收任务无办理人概念,不出现于任务列表,纯系统级等待。用户任务需人工领取和完成。
异步特性接收任务天然异步,流程执行流在此暂停,释放线程。适合高并发场景下的资源优化。

3.9 子流程与调用活动(Call Activity)

概念名称说明注意事项
内嵌子流程(Sub-Process)在主流程内部定义的子流程,可包含自己的任务、网关等。与主流程共享变量和执行流。
折叠与展开子流程可折叠显示为一个节点,点击展开查看详情。用于简化复杂流程图。
调用活动(Call Activity)调用另一个独立的流程定义(被调用流程),实现流程复用。图标为圆角矩形底部带加号。
calledElement 属性指定被调用流程的 key。语法:calledElement="auditProcess";被调用流程必须已部署。
变量传递启动被调用流程时,可传递变量;结束时可返回变量。通过 inout 映射变量。
in 映射将主流程变量传递给被调用流程。语法:<extensionElements><in source="x" target="y"/></extensionElements>
out 映射将被调用流程的变量返回给主流程。语法:<out source="result" target="finalResult"/>
独立流程实例被调用流程会创建独立的流程实例,但生命周期受主流程控制。主流程实例未完成时,被调用流程实例状态为”运行中”。
错误传播被调用流程抛出错误可被主流程的边界事件捕获。支持跨流程异常处理。

第四章:流程实例与运行时管理

4.1 启动流程实例(RuntimeService)

方法名称语法用途代码示例注意事项
startProcessInstanceByKeyruntimeService.startProcessInstanceByKey(String processDefinitionKey)使用流程定义的 key 启动最新版本的流程实例ProcessInstance instance = runtimeService.startProcessInstanceByKey("leave");最常用方法;自动选择最新版本。
startProcessInstanceByIdruntimeService.startProcessInstanceById(String processDefinitionId)使用流程定义的完整 ID(含版本)启动特定版本的流程实例ProcessInstance instance = runtimeService.startProcessInstanceById("leave:2:12345");适用于需要精确控制版本的场景。
启动时传入变量方法重载支持 Map<String, Object> variables 参数在启动流程时设置初始流程变量Map<String, Object> vars = new HashMap<>(); vars.put("applicant", "zhangsan"); vars.put("days", 3); ProcessInstance instance = runtimeService.startProcessInstanceByKey("leave", vars);变量可用于后续任务分配、条件判断等。
设置业务键(businessKey)startProcessInstanceByKey(String key, String businessKey)为流程实例关联一个业务标识,如订单号、请假单号ProcessInstance instance = runtimeService.startProcessInstanceByKey("leave", "REQ-2025-001");业务键应唯一标识业务实体,便于查询和关联。
使用消息启动startProcessInstanceByMessage(String messageName)通过消息事件启动流程(需定义消息开始事件)ProcessInstance instance = runtimeService.startProcessInstanceByMessage("startLeave");适用于异步触发或外部系统集成。
返回值:ProcessInstance方法返回 ProcessInstance 对象包含流程实例的 ID、流程定义 ID、业务键等信息String procId = instance.getId(); String procDefId = instance.getProcessDefinitionId();可用于后续操作,如查询变量、挂起实例等。

4.2 流程变量(Variables)的使用与作用域

方法名称语法用途代码示例注意事项
setVariableruntimeService.setVariable(String executionId, String variableName, Object value)在指定执行流上设置变量runtimeService.setVariable(executionId, "approved", true);变量存储在 ACT_RU_VARIABLE 表中。
setVariablesruntimeService.setVariables(String executionId, Map<String, Object> variables)批量设置多个变量Map<String, Object> vars = Map.of("user", "lisi", "role", "manager"); runtimeService.setVariables(executionId, vars);提高效率,减少数据库操作次数。
getVariableruntimeService.getVariable(String executionId, String variableName)获取指定执行流上的变量值Boolean approved = (Boolean) runtimeService.getVariable(executionId, "approved");若变量不存在,返回 null。
getVariablesruntimeService.getVariables(String executionId)获取指定执行流上的所有变量Map<String, Object> allVars = runtimeService.getVariables(executionId);返回 Map<String, Object>
getVariableLocalruntimeService.getVariableLocal(String executionId, String variableName)获取执行流本地的变量(不从父级继承)String localVal = (String) runtimeService.getVariableLocal(executionId, "temp");用于隔离变量作用域。
setVariableLocalruntimeService.setVariableLocal(String executionId, String variableName, Object value)设置仅在当前执行流有效的本地变量runtimeService.setVariableLocal(executionId, "tempData", "tmp");子执行流无法访问,但父级也无法直接访问。

变量作用域与类型说明:

特性说明
变量作用域变量可存在于不同执行流中,支持继承:子执行流可访问父执行流的变量。在并行分支中,各分支有独立的变量副本,修改本地变量不影响其他分支。
变量类型支持基本类型、String、Serializable 对象、POJO(需序列化)。复杂对象会序列化存储。对于大对象,建议存储 ID 而非完整对象,避免序列化性能开销和数据库存储压力。
变量可见性在表达式、条件、脚本中可通过 ${varName} 访问,例如网关条件 ${days > 5}。确保变量在作用域内可访问。

4.3 查询运行中的流程实例

方法名称语法用途代码示例注意事项
createProcessInstanceQueryruntimeService.createProcessInstanceQuery()创建流程实例查询对象ProcessInstanceQuery query = runtimeService.createProcessInstanceQuery();支持链式调用添加查询条件。
.processInstanceId.processInstanceId(String procInstId)按流程实例 ID 精确查询ProcessInstance instance = runtimeService.createProcessInstanceQuery().processInstanceId("5001").singleResult();适用于已知 ID 的精确查询。
.processDefinitionKey.processDefinitionKey(String key)按流程定义的 key 查询List instances = runtimeService.createProcessInstanceQuery().processDefinitionKey("leave").list();可查询该 key 下所有版本的运行中实例。
.processDefinitionId.processDefinitionId(String procDefId)按流程定义的完整 ID 查询List instances = runtimeService.createProcessInstanceQuery().processDefinitionId("leave:2:12345").list();精确到特定版本。
.businessKey.businessKey(String businessKey)按业务键查询ProcessInstance instance = runtimeService.createProcessInstanceQuery().businessKey("REQ-2025-001").singleResult();便于与业务系统关联。
.variableValueEquals.variableValueEquals(String varName, Object varValue)查询变量等于指定值的流程实例List instances = runtimeService.createProcessInstanceQuery().variableValueEquals("applicant", "zhangsan").list();支持基本类型和 String。
.active.active()只查询处于活动状态的流程实例List activeInstances = runtimeService.createProcessInstanceQuery().active().list();排除已挂起的实例。
.suspended.suspended()查询已挂起的流程实例List suspended = runtimeService.createProcessInstanceQuery().suspended().list();挂起的实例不能继续执行。
.list / .listPage.list() / .listPage(int first, int max)返回查询结果列表或分页结果List page = runtimeService.createProcessInstanceQuery().listPage(0, 10);分页避免内存溢出,适用于大数据量。
.count.count()返回满足条件的流程实例数量long count = runtimeService.createProcessInstanceQuery().processDefinitionKey("leave").count();用于分页统计总数。
.singleResult.singleResult()返回唯一结果,若无或多个则返回 null 或抛异常ProcessInstance instance = runtimeService.createProcessInstanceQuery().processInstanceId("5001").singleResult();适用于精确查询场景。

4.4 挂起与激活流程定义

方法名称语法用途代码示例注意事项
suspendProcessDefinitionByIdrepositoryService.suspendProcessDefinitionById(String processDefinitionId)挂起指定 ID 的流程定义repositoryService.suspendProcessDefinitionById("leave:2:12345");挂起后无法启动新实例。
activateProcessDefinitionByIdrepositoryService.activateProcessDefinitionById(String processDefinitionId)激活已挂起的流程定义repositoryService.activateProcessDefinitionById("leave:2:12345");激活后可重新启动新实例。
suspendProcessDefinitionByKeyrepositoryService.suspendProcessDefinitionByKey(String processDefinitionKey)挂起指定 key 的所有版本流程定义repositoryService.suspendProcessDefinitionByKey("leave");批量操作,影响所有版本。
activateProcessDefinitionByKeyrepositoryService.activateProcessDefinitionByKey(String processDefinitionKey)激活指定 key 的所有版本流程定义repositoryService.activateProcessDefinitionByKey("leave");恢复所有版本的可用性。
挂起时启动实例尝试启动被挂起的流程定义会抛出 FlowableException// 抛异常 runtimeService.startProcessInstanceByKey("suspendedProcess");必须先激活才能启动。
已有实例的影响挂起流程定义不影响已启动的流程实例已运行的实例可继续执行至完成repositoryService.suspendProcessDefinitionByKey("leave"); // 正在运行的实例不受影响挂起是”冷启动”控制。
状态查询查询流程定义状态使用 ProcessDefinitionQuery.suspended().active()ProcessDefinition def = repositoryService.createProcessDefinitionQuery().processDefinitionKey("leave").latestVersion().singleResult(); boolean suspended = def.isSuspended();isSuspended() 返回布尔值。

4.5 删除与终止流程实例

方法名称语法用途代码示例注意事项
deleteProcessInstanceruntimeService.deleteProcessInstance(String processInstanceId, String deleteReason)删除指定的流程实例runtimeService.deleteProcessInstance("5001", "流程错误,需要重新提交");删除后实例进入历史表,状态为”已删除”。
删除原因第二个参数为删除原因记录在历史数据中,用于审计原因可为任意字符串建议填写有意义的原因。
强制删除运行中实例直接调用 deleteProcessInstance可删除正在运行的实例runtimeService.deleteProcessInstance("5001", "cancel");实例关联的任务也会被删除。
终止流程实例Flowable 无直接”终止”方法,通常通过删除实现彻底结束一个流程实例同上”终止”和”删除”在 Flowable 中常视为同义。
删除与完成的区别delete 是强制移除,complete 是正常结束正常完成的实例状态为”已完成”调用 taskService.complete(taskId) 完成任务,最终到达结束事件删除不会触发结束事件。
级联删除删除部署时可级联删除相关实例通过 repositoryService.deleteDeployment(deploymentId, true)repositoryService.deleteDeployment("dep1000", true);true 表示级联删除所有相关流程实例和历史数据。
历史数据保留删除实例后,历史数据默认仍保留可通过 HistoryService 查询HistoricProcessInstance historic = historyService.createHistoricProcessInstanceQuery().processInstanceId("5001").singleResult();若需彻底清理,需手动清理历史表或配置自动清理策略。
无法部分删除不能删除流程实例的某个分支或任务只能删除整个流程实例无对应 API需通过流程设计避免。

第五章:用户任务与任务管理

5.1 任务的创建与分配(Assignee、Candidate Users/Groups)

方法/属性名称语法用途代码示例注意事项
assignee 属性<userTask id="task1" flowable:assignee="zhangsan" />直接指定任务办理人在 BPMN 文件中设置 flowable:assignee="${approver}"任务将出现在该用户的个人任务列表中,无需领取。
candidateUsers 属性<userTask id="task1" flowable:candidateUsers="zhangsan,lisi" />指定任务候选人列表支持表达式:flowable:candidateUsers="${userList}"候选人需先”领取”任务(claim)才能办理。
candidateGroups 属性<userTask id="task1" flowable:candidateGroups="managers" />指定任务候选组支持表达式:flowable:candidateGroups="${dept}.reviewers"组内所有用户均可领取任务,依赖 IdentityService 中的组信息。
setAssignee 方法taskService.setAssignee(String taskId, String userId)运行时动态指定任务负责人taskService.setAssignee("task5001", "lisi");可将任务从候选状态直接分配给某人,或变更负责人。
addCandidateUser 方法taskService.addCandidateUser(String taskId, String userId)为任务动态添加候选人taskService.addCandidateUser("task5001", "wangwu");可在流程运行中扩展任务可办范围。
addCandidateGroup 方法taskService.addCandidateGroup(String taskId, String groupId)为任务动态添加候选组taskService.addCandidateGroup("task5001", "hr");动态扩展组权限。
deleteCandidateUser 方法taskService.deleteCandidateUser(String taskId, String userId)移除任务的候选人taskService.deleteCandidateUser("task5001", "zhangsan");用于权限调整。
deleteCandidateGroup 方法taskService.deleteCandidateGroup(String taskId, String groupId)移除任务的候选组taskService.deleteCandidateGroup("task5001", "interns");与上类似。
任务分配表达式${expression}在 BPMN 中使用表达式动态计算分配对象flowable:assignee="${initiator}"表达式在任务创建时求值,变量需在流程启动时传入。

5.2 任务查询(TaskQuery)与过滤

方法名称语法用途代码示例注意事项
createTaskQuerytaskService.createTaskQuery()创建任务查询对象TaskQuery query = taskService.createTaskQuery();支持链式调用添加条件。
.taskId.taskId(String taskId)按任务 ID 精确查询Task task = taskService.createTaskQuery().taskId("task5001").singleResult();适用于已知 ID 的查询。
.taskAssignee.taskAssignee(String assignee)查询指定办理人的个人任务List tasks = taskService.createTaskQuery().taskAssignee("zhangsan").list();仅查已领取的任务。
.taskCandidateUser.taskCandidateUser(String userId)查询某用户可领取的候选任务List candidates = taskService.createTaskQuery().taskCandidateUser("lisi").list();结合 candidateUsers 或 candidateGroups 生效。
.taskCandidateGroup.taskCandidateGroup(String groupId)查询某组可领取的任务List groupTasks = taskService.createTaskQuery().taskCandidateGroup("managers").list();需用户属于该组。
.taskInvolvedUser.taskInvolvedUser(String userId)查询与某用户相关的所有任务(包括办理、候选、参与)List allTasks = taskService.createTaskQuery().taskInvolvedUser("zhangsan").list();范围最广的个人任务查询。
.processInstanceId.processInstanceId(String procInstId)查询属于某流程实例的任务List tasks = taskService.createTaskQuery().processInstanceId("5001").list();用于流程调试或监控。
.processDefinitionKey.processDefinitionKey(String procDefKey)查询属于某类流程的任务List leaveTasks = taskService.createTaskQuery().processDefinitionKey("leave").list();可跨多个流程实例查询。
.taskDefinitionKey.taskDefinitionKey(String taskKey)按任务在 BPMN 中的 ID 查询Task task = taskService.createTaskQuery().taskDefinitionKey("approveTask").singleResult();适用于查找特定节点任务。
.taskName / .taskNameLike.taskName("审批") / .taskNameLike("%审批%")按任务名称精确或模糊查询List tasks = taskService.createTaskQuery().taskNameLike("请假%").list();名称支持国际化。
.taskCreatedAfter / .taskCreatedBefore.taskCreatedAfter(date)按创建时间范围查询Calendar cal = Calendar.getInstance(); cal.add(Calendar.DAY_OF_MONTH, -7); List recent = taskService.createTaskQuery().taskCreatedAfter(cal.getTime()).list();用于查询近期任务。
.active / .suspended.active()查询活动或挂起状态的任务List active = taskService.createTaskQuery().active().list();挂起任务不能办理。
.orderByTaskCreateTime.orderByTaskCreateTime().desc()按创建时间排序List sorted = taskService.createTaskQuery().orderByTaskCreateTime().desc().list();常用排序方式,结合分页使用。
.list / .listPage / .count.list() / .listPage(0, 10) / .count()返回结果列表、分页结果或数量long total = taskService.createTaskQuery().taskAssignee("zhangsan").count();分页避免性能问题。

5.3 任务办理(claim、complete)与委派

方法名称语法用途代码示例注意事项
claimtaskService.claim(String taskId, String userId)用户领取候选任务taskService.claim("task5001", "zhangsan");领取后任务变为该用户的个人任务。
unclaimtaskService.unclaim(String taskId)取消领取,将任务变回候选状态taskService.unclaim("task5001");需当前办理人为 null 或 ""。
completetaskService.complete(String taskId)完成任务,流程继续执行taskService.complete("task5001");任务必须有办理人,否则抛异常。
complete 带变量taskService.complete(String taskId, Map<String, Object> variables)完成任务并设置流程变量Map<String, Object> vars = Map.of("approved", true, "comment", "同意"); taskService.complete("task5001", vars);变量可用于后续流程判断。
delegatetaskService.delegateTask(String taskId, String userId)将任务委派给他人办理taskService.delegateTask("task5001", "lisi");原办理人仍为负责人,受委派人需完成任务后归还。
resolvetaskService.resolveTask(String taskId)完成委派任务,将任务归还给原负责人taskService.resolveTask("task5001");原负责人可继续处理或再次委派。
setOwnertaskService.setOwner(String taskId, String userId)设置任务所有者(负责人)taskService.setOwner("task5001", "manager");所有者可查看任务,但不能直接办理。
办理前检查查询任务状态确保任务存在且未被办理Task task = taskService.createTaskQuery().taskId("task5001").singleResult(); if (task != null && "zhangsan".equals(task.getAssignee())) { taskService.complete("task5001"); }避免并发操作异常。

5.4 任务附件与评论

方法名称语法用途代码示例注意事项
createAttachmenttaskService.createAttachment(String attachmentType, String taskId, String processInstanceId, String fileName, String description, InputStream content)创建任务附件try (InputStream is = new FileInputStream("contract.pdf")) { taskService.createAttachment("document", "task5001", "5001", "合同.pdf", "签署的合同", is); }attachmentType 常用 document 或 image。
getAttachmenttaskService.getAttachment(String attachmentId)获取附件元数据Attachment attachment = taskService.getAttachment("att1001");包含文件名、大小、类型等信息。
getAttachmentContenttaskService.getAttachmentContent(String attachmentId)获取附件内容流InputStream content = taskService.getAttachmentContent("att1001");需手动关闭流。
getProcessInstanceAttachmentstaskService.getProcessInstanceAttachments(String processInstanceId)获取某流程实例的所有附件List attachments = taskService.getProcessInstanceAttachments("5001");跨任务聚合附件。
deleteAttachmenttaskService.deleteAttachment(String attachmentId)删除附件taskService.deleteAttachment("att1001");同时删除元数据和内容。
addCommenttaskService.addComment(String taskId, String processInstanceId, String message)添加评论taskService.addComment("task5001", "5001", "请尽快处理。");评论与任务和流程实例关联。
getProcessInstanceCommentstaskService.getProcessInstanceComments(String processInstanceId)获取流程实例的所有评论List按时间顺序返回。
getTaskCommentstaskService.getTaskComments(String taskId)获取某任务的所有评论List仅限该任务的评论。
deleteCommenttaskService.deleteComment(String commentId, String processInstanceId)删除评论taskService.deleteComment("com1001", "5001");需提供流程实例 ID。

5.5 任务监听器(TaskListener)与事件类型

概念名称语法用途代码示例注意事项
taskListener 元素<taskListener event="create" class="com.example.TaskCreateListener" />在 BPMN 中定义任务监听器支持 class、expression、delegateExpression<extensionElements> 中配置。
event 属性event="create"指定监听的事件类型可选值:create, assignment, complete, delete不同事件对应任务生命周期的不同阶段。
create 事件event="create"任务创建时触发(在分配前)常用于初始化任务变量、发送通知此时 assignee 可能为空。
assignment 事件event="assignment"任务被分配(setAssignee 或 claim)时触发可用于发送待办提醒邮件事件触发时 assignee 已更新。
complete 事件event="complete"任务完成前触发(在 complete 方法执行后,流程继续前)可用于记录日志、更新外部系统可通过 delegateTask.getVariable() 获取变量。
delete 事件event="delete"任务被删除时触发(如流程实例被删除)用于清理关联资源不常见。
Java 实现 TaskListenerpublic class MyTaskListener implements TaskListener自定义监听逻辑public void notify(DelegateTask delegateTask) { String eventName = delegateTask.getEventName(); if ("create".equals(eventName)) { delegateTask.setVariable("createTime", new Date()); } }实现 notify 方法处理事件。
表达式监听器expression="${mailService.sendNotification(task)}"使用表达式调用服务需在 Spring 中注册 mailService Bean更灵活,无需编写 Java 类。
委托表达式delegateExpression="${myTaskListener}"表达式返回一个 TaskListener 实例支持运行时动态决定监听器推荐用于复杂场景。
监听器执行上下文DelegateTask 对象提供任务和流程的上下文信息可获取任务 ID、名称、办理人、流程变量等是监听器编程的核心对象。
异常处理监听器中抛出异常会中断流程执行try { ... } catch (Exception e) { log.error("监听器执行失败", e); }建议在监听器内捕获并处理异常。

第六章:流程控制与高级行为

6.1 执行流控制(Execution)与异步执行

概念名称说明语法/方法代码示例注意事项
Execution 对象表示流程中的一个执行路径,可对应主流程、并行分支或子流程。runtimeService.createExecutionQuery()Execution execution = runtimeService.createExecutionQuery().processInstanceId(procInstId).activityId("userTask1").singleResult();一个流程实例可有多个执行流(如并行网关后)。
查询执行流根据流程实例 ID、活动 ID 等条件查询执行流createExecutionQuery() 支持 .processInstanceId(), .activityId(), .parentId()List executions = runtimeService.createExecutionQuery().processInstanceId("5001").list();用于定位特定执行路径,支持流程跳转。
异步执行(async)将任务或流程节点放入异步执行器队列,由后台线程处理,不阻塞主线程。在 BPMN 中设置 flowable:async="true"<serviceTask id="sendEmail" flowable:class="SendEmailDelegate" flowable:async="true" />需启用异步执行器(asyncExecutor)。
排他执行(exclusive)控制异步任务的执行顺序。exclusive="true"(默认)表示同一流程实例的异步任务串行执行。flowable:exclusive="true"可避免同一实例的数据竞争。设为 false 可允许并行执行,提高吞吐量但需自行处理并发。
异步执行器配置异步任务由独立线程池处理,可配置线程数、队列等。flowable.cfg.xml 或 Spring 配置中设置<property name="asyncExecutorActivate" value="true"/>生产环境建议启用并合理配置线程池。
异步任务状态异步任务在 ACT_RU_JOB 表中创建为 job,等待执行器触发。可通过 ManagementService 查询和管理 job。
异步与性能异步执行可显著提高流程启动和任务处理的吞吐量。适用于耗时操作(如发送邮件、调用外部 API)。

6.2 事件监听器(ExecutionListener)

概念名称说明语法/方法代码示例注意事项
executionListener 元素用于监听流程执行过程中的事件,如流程启动、活动开始/结束等。<process><sequenceFlow> 等元素的 <extensionElements> 中定义<extensionElements><flowable:executionListener event="start" class="com.example.ProcessStartListener" /></extensionElements>可在流程级、活动级或连线级配置。
event 属性指定监听的事件类型可选值:start, end, take(仅用于连线)event="end" 表示活动或流程结束时触发start 在活动创建后触发,end 在活动完成后触发。
class 属性指定实现 ExecutionListener 接口的 Java 类class="com.example.MyExecutionListener"public class MyExecutionListener implements ExecutionListener { public void notify(DelegateExecution execution) { if ("start".equals(execution.getEventName())) { execution.setVariable("startTime", new Date()); } } }类必须有无参构造函数。
expression 属性使用表达式调用 Spring Bean 方法expression="${auditService.logStart(execution)}"需在 Spring 中注册 auditService Bean更灵活,无需实现接口。
delegateExpression 属性表达式返回一个 ExecutionListener 实例delegateExpression="${myListener}"支持运行时动态决定监听器推荐用于复杂场景。
流程级监听器监听整个流程的 start 和 end 事件<process> 元素上配置适用于流程生命周期的日志记录、资源初始化等一个流程可配置多个监听器。
活动级监听器监听特定任务或网关的 start 和 end<userTask>, <serviceTask> 等元素上配置适用于特定节点的前置/后置处理如在用户任务 start 时发送通知。
连线监听器(take)在顺序流被选择时触发event="take"适用于记录流程走向或动态调整变量只能在 <sequenceFlow> 上配置。
执行上下文DelegateExecution 对象提供流程实例、变量、执行流等信息在 notify 方法中使用可通过 execution.getProcessInstanceId(), execution.getVariable("x") 等方法获取数据是监听器编程的核心。

6.3 流程跳转与动态流程(Dynamic Process Changes)

概念名称说明语法/方法代码示例注意事项
createChangeActivityIdBuilder创建动态流程变更构建器,用于修改当前执行流的目标活动。runtimeService.createChangeActivityIdBuilder()RuntimeService runtimeService = processEngine.getRuntimeService(); runtimeService.createChangeActivityIdBuilder().processInstanceId("5001").moveExecutionToActivityId("executionId", "newTask").change();需要执行流 ID 和目标活动 ID。
流程跳转(Jump)在运行时强制将执行流从当前节点跳转到另一个节点,绕过中间流程。使用 ChangeActivityIdBuilder可用于”加签”、“跳过审批”等特殊场景。
moveExecutionToActivityId指定将某个执行流移动到目标活动.moveExecutionToActivityId(executionId, targetActivityId)多个执行流可同时跳转适用于并行流程的动态调整。
change执行变更操作.change()调用后立即生效,流程继续执行。
使用场景应急处理、流程纠错、特殊审批路径如领导特批,跳过部门经理审批需谨慎使用,避免破坏流程完整性。
限制条件目标活动必须是可达的,且不能违反流程结构不能跳转到已结束的分支或不兼容的节点引擎会进行基本校验,但仍需业务逻辑保证合理性。
历史记录跳转操作会被记录在历史表中可通过 HistoricActivityInstance 查询跳过的节点状态为 CANCELLED便于审计和追溯。
与变量结合跳转前后可设置变量控制流程逻辑在跳转前设置 skipReason 变量增强流程的可解释性。

6.4 流程超时与定时边界事件(Boundary Timer Event)

概念名称说明语法/方法代码示例注意事项
定时边界事件(Boundary Timer Event)附加在任务上的定时器,当时间到时触发中断或非中断流程。图形:附着在任务边缘的钟表图标<boundaryEvent id="timer1" attachedToRef="userTask1"><timerEventDefinition><timeDuration>PT2H</timeDuration></timerEventDefinition></boundaryEvent>用于实现任务超时。
timeDuration设置相对时间,如”2小时后”<timeDuration>PT2H</timeDuration>PT2H = 2小时,P1D = 1天常用于 SLA 超时提醒。
timeDate设置绝对时间,如”2025-10-01T12:00:00”<timeDate>2025-10-01T12:00:00</timeDate>适用于固定时间触发需确保系统时间准确。
cron 表达式设置周期性或复杂时间规则<timeCycle>0 0 9 * * ?</timeCycle>每天上午9点触发功能强大,但需熟悉 cron 语法。
中断性(cancelActivity)默认 true,定时器触发后中断原任务;false 则不中断,形成并行分支。cancelActivity="true"中断性常用于超时升级;非中断性用于提醒。根据业务需求选择。
超时处理流程定时器触发后,流程沿边界事件的 outgoing 流继续执行。可连接服务任务发送通知,或用户任务升级审批实现”2小时未处理,通知主管”等逻辑。
与异步执行器定时事件由异步执行器(Job Executor)管理需启用 asyncExecutor若未启用,定时器不会触发。
查询定时任务通过 ManagementService 查询待处理的 jobList jobs = managementService.createJobQuery().list();可用于监控和调试job 类型为 timer。
删除流程实例时流程实例被删除时,关联的定时器 job 也会被自动删除无需手动清理。

6.5 补偿事件与补偿处理

概念名称说明语法/方法代码示例注意事项
补偿事件(Compensation Event)用于撤销或补偿已执行的操作,通常在事务失败后触发。图形:内部带两条曲线的圆圈需配合 subProcess 的 isForCompensation="true" 使用实现”回滚”机制。
补偿边界事件(Compensation Boundary Event)附加在活动上,当补偿被触发时执行补偿逻辑。attachedToRef="serviceTask1"<boundaryEvent id="compensate1" attachedToRef="reserveStock"><compensateEventDefinition /></boundaryEvent>用于补偿特定服务任务。
compensateEventDefinition定义补偿事件,可指向需要补偿的活动<compensateEventDefinition activityRef="reserveStock" />activityRef 指定被补偿的任务若无 activityRef,则补偿所有可补偿的活动。
可补偿活动(Compensatable Activity)标记为可补偿的活动(如 serviceTask),其补偿处理器会在补偿事件触发时执行。设置 isForCompensation="true"<serviceTask id="cancelOrder" flowable:class="CancelOrderDelegate" isForCompensation="true" />通常是一个反向操作。
错误事件子流程(Error Event Sub-Process)常用于捕获错误并触发补偿顶级子流程,triggeredByEvent="true"捕获错误后,抛出 compensate 事件实现”下单失败,取消库存预留”流程。
补偿执行顺序补偿按与原执行顺序相反的顺序进行先执行的活动后补偿符合事务回滚的逻辑。
补偿范围可补偿整个子流程或特定任务通过 activityRef 控制范围设计时需明确补偿边界。
使用场景分布式事务、预留资源的释放、支付退款等如”支付失败,释放库存”是实现最终一致性的重要机制。
限制补偿事件不能跨流程实例只能在当前流程实例内触发和执行不能用于调用活动的被调用流程。

第七章:表达式与脚本支持

7.1 Spring EL 表达式在 Flowable 中的应用

概念名称说明语法/方法代码示例注意事项
Spring EL(Spring Expression Language)Flowable 默认的表达式语言,功能强大,支持方法调用、属性访问、运算符等。${expression}${days > 5}${user.department == 'IT'}需在 Spring 环境中使用以获得完整支持。
变量访问直接通过变量名访问流程变量${variableName}${applicant}${totalAmount}变量需在流程作用域内存在。
属性访问访问对象的属性(getter 方法)${object.property}${order.customer.name}支持链式访问。
方法调用调用对象的方法${object.method()}${userService.getManager(applicant)}方法必须是 public 且可被 Spring 管理。
运算符支持算术、关系、逻辑、三元等运算符+ - * / %> < >= <= == !=,`&&!?:`
集合操作支持 List、Map 的访问和操作list[0]map['key']${approvers[0]}${config['timeout']}可用于动态数据处理。
类型转换EL 自动进行类型转换${days}(String 变 int)需确保变量值可转换,否则抛异常。
空值处理支持 null 安全访问?. 操作符${user?.profile?.email}避免 NullPointerException。
Spring Bean 访问调用 Spring 容器中的 Bean 方法${beanName.method()}${notificationService.send(applicant, '任务已创建')}Bean 需在 Spring 上下文中注册。
表达式限制避免执行耗时或危险操作(如修改数据库)不推荐在表达式中调用 userService.deleteUser()表达式应为”纯查询”性质。

7.2 使用 Groovy 或 JavaScript 脚本

脚本类型说明语法/方法代码示例注意事项
flowable:script 元素在 BPMN 中定义脚本任务或执行监听器脚本<scriptTask flowable:scriptFormat="groovy"><scriptTask id="script1" name="计算费用" flowable:scriptFormat="groovy"><script>def total = days * 200; execution.setVariable('cost', total);</script></scriptTask>scriptFormat 指定脚本语言。
Groovy 脚本功能强大,语法类似 Java,支持闭包,与 JVM 无缝集成。flowable:scriptFormat="groovy"def manager = userService.getManager(execution.getVariable('applicant')); execution.setVariable('approver', manager);推荐使用,性能好,功能全。
JavaScript 脚本使用 Nashorn 引擎(Java 8+),语法灵活。flowable:scriptFormat="javascript"var total = Number(days) * 200; execution.setVariable('cost', total);Java 15+ 需额外引入 Nashorn。
脚本任务(Script Task)用于执行脚本逻辑,不需人工干预。<scriptTask>常用于数据计算、变量初始化。
执行上下文脚本中可通过 execution 访问 DelegateExecution 对象execution.getVariable(), execution.setVariable()是脚本与流程交互的主要方式。
变量操作在脚本中读取和设置流程变量execution.setVariable("var", value)def days = execution.getVariable('days'); if (days > 3) { execution.setVariable('level', 'senior'); }变量变更对后续流程可见。
调用 Spring Bean在脚本中访问 Spring Beandef bean = execution.getProcessEngineServices().getProcessEngineConfiguration().getBean('beanName')def service = execution.getProcessEngineServices().getProcessEngineConfiguration().getBean('approvalService'); def result = service.check(applicant);需通过配置获取 Bean。
脚本安全性脚本拥有较高权限,可能访问系统资源避免执行 System.exit()、文件 I/O 等危险操作生产环境需严格审查脚本内容。
性能考量脚本解析和执行有开销避免在循环中执行复杂脚本对性能敏感的场景建议使用 Java Delegate。
错误处理脚本中抛出异常会中断流程throw new RuntimeException("计算失败")建议在脚本中添加 try-catch。

7.3 表达式在条件、监听器、任务分配中的使用

应用场景说明语法/方法代码示例注意事项
排他网关(Exclusive Gateway)条件根据表达式结果决定流程走向conditionExpression="${variable > 10}"<conditionExpression xsi:type="tFormalExpression">${totalAmount > 10000}</conditionExpression>返回 true 或 false,第一个为 true 的连线被选中。
并行网关(Inclusive Gateway)条件控制并行分支的激活同上可同时激活多个满足条件的分支与排他网关不同,并行网关不互斥。
任务分配(Assignee)动态指定任务办理人flowable:assignee="${manager}"<userTask id="task1" flowable:assignee="${userService.getManager(applicant)}" />表达式在任务创建时求值。
任务候选人(Candidate Users/Groups)动态指定候选人flowable:candidateUsers="${approvers}"<userTask id="task2" flowable:candidateUsers="${department}Leaders" />approvers 可为 List<String> 或逗号分隔的字符串。
执行监听器(ExecutionListener)使用表达式调用服务方法expression="${service.method(execution)}"<flowable:executionListener event="start" expression="${auditService.logStart(execution)}" />避免实现 ExecutionListener 接口。
任务监听器(TaskListener)在任务事件中调用表达式expression="${mailService.notify(task)}"<taskListener event="create" expression="${notificationService.sendTaskCreated(task)}" />常用于发送邮件或消息通知。
条件启动事件(Conditional Start Event)当满足条件时启动流程需配置全局条件${order.status == 'APPROVED'}需启用条件启动事件功能。
定时器表达式(Timer)使用表达式动态计算定时时间timeDuration="${timeoutDuration}"<timeDuration>${processService.getTimeout()}PT1H</timeDuration>表达式需返回符合格式的字符串。
默认流程(Default Flow)当无连线条件为 true 时使用的默认路径在网关上设置 default 属性<exclusiveGateway id="decision" default="flow2">不依赖表达式,但作为兜底逻辑。
表达式缓存Flowable 会缓存表达式编译结果以提高性能首次执行稍慢,后续更快。
表达式调试表达式错误会导致流程中断检查日志中的 FlowableException确保变量存在且类型正确建议在测试环境充分验证表达式逻辑。

第八章:历史数据与流程审计

8.1 历史服务(HistoryService)与历史级别配置

配置项/方法语法用途代码示例注意事项
history 配置processEngineConfiguration.setHistory("full");设置历史级别,控制历史数据的详细程度可在 flowable.cfg.xml 或 Spring 配置中设置历史级别影响性能和存储空间。
nonehistory="none"不记录任何历史数据适用于纯运行时流程历史表为空,无法查询历史。
activityhistory="activity"记录流程实例、执行流和任务的开始/结束时间最低级别,记录活动实例不记录变量和详细信息。
audithistory="audit"记录任务、变量、作业等详细信息,推荐用于审计默认级别包含大多数审计所需数据。
fullhistory="full"记录所有数据,包括详细变更日志最详细,用于深度分析性能开销最大,存储占用高。
HistoryService 接口historyService.createHistoricProcessInstanceQuery()提供查询历史数据的方法HistoryService historyService = processEngine.getHistoryService();与 RuntimeService 对应,用于历史查询。
asyncHistoryExecutorasyncHistoryExecutorActivate="true"启用异步历史记录,提升流程性能将历史数据写入操作放入队列异步处理生产环境推荐启用,避免阻塞主流程。
历史表命名ACT_HI_*历史数据存储在以 ACT_HI_ 开头的表中ACT_HI_PROCINST, ACT_HI_TASKINST, ACT_HI_VARINST与运行时表 ACT_RU_* 区分。
历史清理策略定期清理过期历史数据可通过自定义作业或外部脚本实现避免数据库无限增长。
历史级别变更修改 history 配置后重启引擎影响新启动的流程实例已运行的流程仍按原级别记录建议在系统设计初期确定级别。

8.2 查询历史流程实例与任务

方法名称语法用途代码示例注意事项
createHistoricProcessInstanceQueryhistoryService.createHistoricProcessInstanceQuery()创建历史流程实例查询对象HistoricProcessInstanceQuery query = historyService.createHistoricProcessInstanceQuery();支持链式调用。
.processInstanceId.processInstanceId(String procInstId)按流程实例 ID 查询HistoricProcessInstance instance = historyService.createHistoricProcessInstanceQuery().processInstanceId("5001").singleResult();精确匹配。
.processDefinitionKey.processDefinitionKey(String procDefKey)查询某类流程的历史实例List instances = historyService.createHistoricProcessInstanceQuery().processDefinitionKey("leave").finished().list();可跨多个流程定义查询。
.finished() / .unfinished().finished()查询已结束或未结束的流程实例.finished().orderByProcessInstanceEndTime().desc()用于统计完成情况。
.startedBy.startedBy(String userId)查询某用户启动的流程实例List myInstances = historyService.createHistoricProcessInstanceQuery().startedBy("zhangsan").list();结合时间范围使用更高效。
.startedAfter / .startedBefore.startedAfter(Date date)按启动时间范围查询Calendar cal = Calendar.getInstance(); cal.add(Calendar.DAY_OF_MONTH, -30); List recent = query.startedAfter(cal.getTime()).list();常用于月度报表。
.finishedAfter / .finishedBefore.finishedBefore(Date date)按结束时间范围查询仅对已结束实例有效。
.durationGreaterThan.durationGreaterThan(Long millis)查询执行时长超过指定值的实例query.durationGreaterThan(3600000L) // 超过1小时时长单位为毫秒。
createHistoricTaskInstanceQueryhistoryService.createHistoricTaskInstanceQuery()创建历史任务实例查询对象HistoricTaskInstanceQuery taskQuery = historyService.createHistoricTaskInstanceQuery();查询已完成的任务。
.taskAssignee.taskAssignee(String userId)查询某用户办理过的历史任务List tasks = historyService.createHistoricTaskInstanceQuery().taskAssignee("lisi").finished().list();包含已结束的任务。
.taskCandidateUser.taskCandidateUser(String userId)查询某用户曾可办理的任务即使未领取也可查询到。
.processInstanceId.processInstanceId(String procInstId)查询某流程实例中的所有历史任务List taskList = historyService.createHistoricTaskInstanceQuery().processInstanceId("5001").orderByHistoricTaskInstanceStartTime().asc().list();用于流程追溯。
.taskDeleteReasonLike.taskDeleteReasonLike("%timeout%")按删除原因模糊查询适用于被跳过或超时取消的任务原因由 deleteTask 时指定。
.orderBy + .asc() / .desc().orderByProcessInstanceStartTime().desc()排序支持按开始、结束、持续时间等排序结合分页使用。
.listPage.listPage(int firstResult, int maxResults)分页查询List page = query.listPage(0, 10);避免一次性查询大量数据。
.count.count()获取查询结果总数long total = historyService.createHistoricProcessInstanceQuery().processDefinitionKey("loan").count();用于分页控件显示总条数。

8.3 历史变量与审计日志

方法名称语法用途代码示例注意事项
createHistoricVariableInstanceQueryhistoryService.createHistoricVariableInstanceQuery()查询历史变量实例HistoricVariableInstanceQuery varQuery = historyService.createHistoricVariableInstanceQuery();变量在 ACT_HI_VARINST 表中。
.variableName.variableName("totalAmount")按变量名查询List vars = varQuery.variableName("approved").list();可查询所有流程实例中的该变量。
.variableValueEquals.variableValueEquals("status", "APPROVED")按变量值精确查询支持 String, Integer 等基本类型。
.processInstanceId.processInstanceId("5001")查询某流程实例的所有历史变量List instanceVars = historyService.createHistoricVariableInstanceQuery().processInstanceId("5001").list();获取实例的完整变量快照。
.excludeVariableValues.excludeVariableValues()查询时不加载变量值(仅元数据)List metadata = varQuery.excludeVariableValues().list();提高查询性能,适用于大数据量场景。
getActualValuehistoricVariableInstance.getValue()获取变量的实际值(反序列化后)Object value = historicVar.getValue();对于 Serializable 对象,需确保类路径存在。
getTextValue / getDoubleValuehistoricVar.getTextValue()获取变量的文本或数字值String name = historicVar.getTextValue();适用于简单类型,避免类型转换异常。
createHistoricDetailQueryhistoryService.createHistoricDetailQuery()查询历史详情,包括变量更新、任务表单属性等HistoricDetailQuery detailQuery = historyService.createHistoricDetailQuery();记录在 ACT_HI_DETAIL 表中,用于审计日志。
.processInstanceId.processInstanceId("5001")查询某流程实例的详细变更记录List details = historyService.createHistoricDetailQuery().processInstanceId("5001").orderByTime().asc().list();可追溯变量每次变更。
.variableUpdates().variableUpdates()仅查询变量更新记录List updates = detailQuery.variableUpdates().list();过滤出 HistoricVariableUpdate 类型。
.formProperties().formProperties()查询表单属性提交记录与 HistoricFormProperty 关联。
getVariableUpdates(HistoricVariableUpdate) detail将 HistoricDetail 转换为变量更新对象for (HistoricDetail detail : details) { if (detail instanceof HistoricVariableUpdate) { HistoricVariableUpdate update = (HistoricVariableUpdate) detail; System.out.println(update.getVariableName() + " from " + update.getOldValue() + " to " + update.getValue()); } }获取变更前后的值,实现审计日志。
getTaskIdhistoricDetail.getTaskId()获取详情关联的任务 ID可用于关联任务上下文为空表示全局变量变更。
getTimehistoricDetail.getTime()获取变更发生的时间精确到毫秒,用于时间线展示。
getRevisionhistoricDetail.getRevision()获取变量的版本号反映变量的修改次数。
审计日志展示综合 HistoricProcessInstance, HistoricTaskInstance, HistoricDetail构建完整的流程审计视图按时间顺序合并流程启动、任务办理、变量变更等事件是实现”流程追溯”功能的核心。

第九章:身份管理与用户组集成

9.1 IdentityService 与用户/组管理

概念名称说明语法/方法代码示例注意事项
IdentityService 接口Flowable 提供的身份管理服务,用于管理用户、组及成员关系identityService.createUserQuery()IdentityService identityService = processEngine.getIdentityService();仅用于流程引擎内部的身份存储。
内置身份表Flowable 自带用户和组的数据库表ACT_ID_* 表(如 ACT_ID_USER, ACT_ID_GROUP, ACT_ID_MEMBERSHIP可独立使用,也可与外部系统集成。
用户(User)表示流程中的参与者,具有唯一 ID、姓名、邮箱等属性User 对象User user = new UserEntityImpl(); user.setId("zhangsan"); user.setFirstName("Zhang"); user.setLastName("San");id 字段为必填且唯一。
组(Group)用户的逻辑集合,用于任务分配(如 candidateGroup)Group 对象Group group = new GroupEntityImpl(); group.setId("managers"); group.setName("Managers");id 通常用于流程定义中的 candidateGroup。
成员关系(Membership)用户与组的关联关系,一个用户可属于多个组通过 createMembership(userId, groupId) 建立identityService.createMembership("zhangsan", "hr");关系存储在 ACT_ID_MEMBERSHIP 表中。
密码管理支持为用户设置密码,用于身份验证user.setPassword("pwd123")在用户创建或更新时设置Flowable 本身不提供登录功能,密码用于扩展场景。
外部系统集成IdentityService 可被替换,以连接 LDAP、数据库或 REST API实现 IdentityService 接口或使用 ReadOnlyIdentityServiceProvider推荐在企业环境中集成统一身份认证系统。
会话机制Flowable 在流程执行时使用 Authentication 类进行身份上下文管理Authentication.setAuthenticatedUserId("zhangsan")在任务提交前设置当前用户用于自动设置 assignee 或 owner。
与流程执行关联用户 ID 可作为 assignee 或 candidateUser 在任务中使用在 BPMN 中设置 flowable:assignee="${userId}"身份数据是任务分配的基础。
只读模式可配置为只读模式,从外部系统同步用户组数据flowable.identity-service.read-only=true避免在 Flowable 中修改用户数据适用于与主身份系统同步的场景。

9.2 用户与组的增删改查

操作类型方法名称用途代码示例注意事项
用户管理
创建用户saveUser(User user)新增用户到 ACT_ID_USER 表User user = identityService.newUser("lisi"); user.setFirstName("Li"); user.setLastName("Si"); user.setEmail("lisi@company.com"); user.setPassword("pass"); identityService.saveUser(user);id 必须设置,密码可选。
查询用户createUserQuery()构建用户查询对象List users = identityService.createUserQuery().userEmailLike("%@company.com").list();支持按 ID、姓名、邮箱、组等条件查询。
按 ID 查询.userId("zhangsan")精确查询单个用户User user = identityService.createUserQuery().userId("zhangsan").singleResult();返回 null 若用户不存在。
模糊查询.userFirstNameLike("Zhang%")按姓名模糊查询List zhangUsers = identityService.createUserQuery().userFirstNameLike("Zhang%").list();支持 % 通配符。
按组查询.memberOfGroup("managers")查询属于某组的所有用户List managers = identityService.createUserQuery().memberOfGroup("managers").list();基于 ACT_ID_MEMBERSHIP 关联查询。
更新用户saveUser(User user)修改已存在用户的信息User user = identityService.createUserQuery().userId("lisi").singleResult(); user.setEmail("newemail@company.com"); identityService.saveUser(user);调用 saveUser 更新,无需单独的 update 方法。
删除用户deleteUser(String userId)从系统中删除用户identityService.deleteUser("lisi");同时删除其成员关系,但历史任务中的用户引用仍保留。
组管理
创建组saveGroup(Group group)新增组到 ACT_ID_GROUP 表Group group = identityService.newGroup("dev"); group.setName("Developers"); identityService.saveGroup(group);id 和 name 通常都需设置。
查询组createGroupQuery()构建组查询对象List groups = identityService.createGroupQuery().groupId("hr").list();支持按 ID、名称等查询。
按名称查询.groupName("Managers")按组名称查询支持精确和模糊查询(groupNameLike)。
更新组saveGroup(Group group)修改组信息Group group = identityService.createGroupQuery().groupId("dev").singleResult(); group.setName("Development Team"); identityService.saveGroup(group);同用户更新,使用 saveGroup。
删除组deleteGroup(String groupId)删除组及所有成员关系identityService.deleteGroup("dev");组内用户不会被删除,仅解除关联。
成员关系管理
添加成员createMembership(String userId, String groupId)将用户加入组identityService.createMembership("zhangsan", "managers");若关系已存在,可能抛异常,建议先查询。
移除成员deleteMembership(String userId, String groupId)将用户从组中移除identityService.deleteMembership("zhangsan", "managers");仅删除关联,不删除用户或组。
查询成员createUserQuery().memberOfGroup(groupId)createGroupQuery().groupMember(userId)双向查询成员关系List userGroups = identityService.createGroupQuery().groupMember("zhangsan").list();提供灵活的查询方式。

9.3 与 Spring Security 集成方案

集成方式说明配置/代码示例注意事项
共享用户上下文将 Spring Security 的当前用户同步到 Flowable在服务层调用 Authentication.setAuthenticatedUserId()@Service public class WorkflowService { @Autowired private RuntimeService runtimeService; public void startProcess(String procDefKey) { String currentUser = SecurityContextHolder.getContext().getAuthentication().getName(); Authentication.setAuthenticatedUserId(currentUser); runtimeService.startProcessInstanceByKey(procDefKey); } }
自定义身份提供者实现 ReadOnlyIdentityServiceProvider,从 Spring Security 用户库加载用户组public class SpringSecurityIdentityProvider implements ReadOnlyIdentityServiceProvider { @Override public User getUserById(String userId) { return convertToFlowableUser(loadUserFromSpringSecurity(userId)); } @Override public List getGroupsForUser(String userId) { // 获取用户权限并转换为 Flowable Group } }需注册为 Spring Bean 并在 Flowable 配置中启用。
配置只读模式避免 Flowable 修改身份数据,所有读取委托给自定义提供者flowable.cfg.xml 中配置确保 IdentityService 的写操作被禁用。
任务分配集成使用 Spring Security 的 Authentication 信息动态分配任务在表达式中使用 ${authenticatedUserId}<userTask flowable:assignee="${authenticatedUserId}" />
权限控制在 Web 层基于 Spring Security 的 @PreAuthorize 控制流程操作权限@PreAuthorize("hasRole('APPROVER')") public void approveTask(String taskId) { taskService.complete(taskId); }将业务角色映射到 Spring Security 的 GrantedAuthority。
用户信息转换将 Spring Security 的 UserDetails 转换为 Flowable User 对象private User convertToFlowableUser(UserDetails userDetails) { User user = identityService.newUser(userDetails.getUsername()); user.setFirstName(userDetails.getUsername()); return user; }需处理字段映射,如邮箱、姓名等。
会话清理在请求结束时清除 Flowable 的身份上下文使用 @After@EventListener 清理@EventListener public void handleRequestEnd(RequestHandledEvent event) { Authentication.clearAuthenticatedUserId(); }
优势统一身份认证、避免重复管理用户、利用现有权限体系企业级应用推荐方案。
挑战需要编写适配代码、处理用户同步延迟、调试复杂建议充分测试用户加载和权限判断逻辑。

第十章:事件与消息机制

10.1 中间消息事件(Message Event)与消息触发

概念名称说明语法/方法代码示例注意事项
捕获消息中间事件(Intermediate Message Catch Event)流程执行到该节点时暂停,等待指定消息到达后继续。图形:圆圈内带信封图标,位于流程线上<intermediateCatchEvent id="waitPayment" name="等待支付完成"><messageEventDefinition messageRef="paymentConfirmed" /></intermediateCatchEvent>用于同步外部系统调用。
发送消息中间事件(Intermediate Message Throw Event)主动向流程外发送一条消息。图形:圆圈内带向上的信封图标<intermediateThrowEvent id="sendNotification" name="发送通知"><messageEventDefinition messageRef="taskAssigned" /></intermediateThrowEvent>消息可被其他流程或监听器捕获。
messageRef消息的引用名称,用于关联发送和接收在 BPMN 中定义 <message id="paymentConfirmed" name="Payment Confirmed" /><message id="orderShipped" name="Order Shipped" />必须全局唯一。
MessageCorrelationBuilder用于从外部向流程实例发送消息,触发捕获事件runtimeService.createMessageCorrelation("paymentConfirmed")runtimeService.createMessageCorrelation("paymentConfirmed").processInstanceId("5001").setVariable("txnId", "txn_123").correlate();correlate() 触发流程继续执行。
消息关联(Correlation)通过流程实例 ID、业务键或消息关联键精确定位目标流程.processInstanceId("5001").processInstanceBusinessKey("ORDER-1001")可结合变量进行更复杂的关联若未指定,可能匹配多个流程实例,导致异常。
消息边界事件(Message Boundary Event)附加在任务上,当消息到达时中断当前任务并跳转attachedToRef="userTask1", cancelActivity="true"<boundaryEvent id="msgCancel" attachedToRef="reviewTask" cancelActivity="true"><messageEventDefinition messageRef="cancelReview" /></boundaryEvent>常用于”取消审批”场景。
消息非中断边界事件消息到达时不中断原任务,而是开启并行分支cancelActivity="false"实现”提醒”或”补充流程”。
消息队列集成可通过监听器将 Flowable 消息桥接到 JMS、RabbitMQ 等实现 FlowableEngineEventListener 监听 MESSAGE_THROWED 事件将 intermediateThrowEvent 发出的消息转发到外部队列实现系统间解耦。
错误处理若消息无法关联到任何等待的流程实例,会抛出 FlowableException建议在调用 correlate() 时捕获异常可记录日志或重试。
使用场景订单支付确认、物流状态同步、跨系统任务协调如”支付系统回调后,订单流程继续发货”是实现异步集成的关键机制。

10.2 信号事件(Signal Event)与广播

概念名称说明语法/方法代码示例注意事项
信号中间事件(Intermediate Signal Catch Event)等待一个全局信号,可被任意流程实例触发。图形:圆圈内带三角形图标<intermediateCatchEvent id="waitForUpdate"><signalEventDefinition signalRef="systemUpdate" /></intermediateCatchEvent>信号是全局广播,不限于单个流程实例。
信号抛出事件(Intermediate Signal Throw Event)发送一个全局信号,所有监听该信号的流程实例都会收到。图形:圆圈内带向上的三角形图标<intermediateThrowEvent id="broadcastUpdate"><signalEventDefinition signalRef="systemUpdate" /></intermediateThrowEvent>实现”一对多”通知。
signalRef信号的引用名称,用于标识信号类型定义 <signal id="systemUpdate" name="System Update" />必须在流程定义中声明。
SignalEventPropagationMode控制信号的传播范围可设置为 DEFAULT, EXCLUSIVE, ASYNC默认为 DEFAULT,信号同步传播到所有监听器ASYNC 可提高性能,但顺序不保证。
runtimeService.signalEventReceived从外部代码触发一个信号事件runtimeService.signalEventReceived("systemUpdate")runtimeService.signalEventReceived("systemUpdate");可选地传递变量:.signalEventReceived("alert", variables)
信号边界事件(Signal Boundary Event)附加在活动上,当信号到达时中断或并行执行attachedToRef="task1"<boundaryEvent id="emergency" attachedToRef="normalProcess" cancelActivity="true"><signalEventDefinition signalRef="EMERGENCY_STOP" /></boundaryEvent>常用于”紧急停止”或”系统维护”通知。
非中断信号边界事件信号触发时不中断原流程,开启新分支cancelActivity="false"用于”提醒”、“数据刷新”等场景。
信号事件子流程顶级子流程,监听信号,收到后启动triggeredByEvent="true"<eventSubProcess id="signalSubProcess" triggeredByEvent="true"><startEvent><signalEventDefinition signalRef="newUserRegistered" /></startEvent>...</eventSubProcess>实现”事件驱动”流程启动。
广播特性一个信号可被多个流程实例同时捕获如”发布新版本”信号,触发所有相关系统的更新流程与消息事件的”点对点”不同。
与消息事件对比信号是广播、全局、无状态;消息是定向、实例级、有上下文选择依据:是否需要精确关联特定流程实例信号更灵活,但控制粒度较粗。
使用场景系统级通知、批量操作触发、事件驱动架构如”夜间批处理开始”、“价格更新”适合解耦和事件驱动设计。

10.3 事件子流程(Event Sub-Process)

概念名称说明语法/方法代码示例注意事项
事件子流程(Event Sub-Process)嵌套在主流程中的子流程,由特定事件(如错误、消息、信号)触发执行。图形:虚线边框的子流程<eventSubProcess id="errorHandler" triggeredByEvent="true">...</eventSubProcess>triggeredByEvent="true" 是关键属性。
开始事件类型事件子流程必须以捕获型开始事件(如错误、消息、信号、定时器)启动支持 errorEventDefinition, messageEventDefinition, signalEventDefinition, timerEventDefinition<startEvent id="errorStart"><errorEventDefinition errorRef="BusinessError" /></startEvent>不能使用普通开始事件。
错误事件子流程(Error Event Sub-Process)捕获特定错误,执行补偿或降级逻辑errorRef 指向 error 元素<error id="BusinessError" errorCode="BUS-001" />可定义在任务、事务子流程或主流程级别。
范围(Scope)事件子流程的捕获范围取决于其位置定义在 userTask 内 → 仅捕获该任务的错误;定义在流程根 → 捕获整个流程的错误位置决定作用域。
中断性默认为中断性,触发后终止主流程的当前执行流可设置 cancelActivity="false" 为非中断非中断用于并行处理(如记录日志)中断性常用于错误处理。
消息事件子流程由消息触发的事件子流程messageEventDefinition 开始<startEvent><messageEventDefinition messageRef="timeoutAlert" /></startEvent>可在流程运行中动态注入处理逻辑。
信号事件子流程由全局信号触发signalEventDefinition 开始如上文 10.2 所示实现跨流程协调。
定时器事件子流程在特定时间触发,如”超时处理”timerEventDefinition 开始<startEvent><timerEventDefinition><timeDuration>PT1H</timeDuration></timerEventDefinition></startEvent>常用于 SLA 超时升级。
执行上下文事件子流程与主流程共享变量和执行上下文可直接访问主流程的变量在子流程中可修改变量,影响主流程便于状态传递。
结束行为事件子流程结束后,主流程的后续行为取决于中断性中断性:主流程终止;非中断性:主流程继续设计时需明确预期行为。
使用场景异常处理、超时升级、动态流程扩展如”用户任务 24 小时未处理,启动催办子流程”增强流程的健壮性和灵活性。
限制一个活动或流程只能有一个同类型事件子流程如不能有两个错误事件子流程需合并逻辑到单个子流程中。

第十一章:REST API 与外部集成

11.1 Flowable REST 模块介绍

概念名称说明语法/方法代码示例注意事项
flowable-rest 模块Flowable 提供的独立 REST API 服务,基于 Spring MVC 实现Maven 依赖:org.flowable:flowable-rest可部署为独立 WAR 包或嵌入 Spring Boot 应用提供标准 HTTP 接口操作流程引擎。
REST API 端点提供对流程定义、实例、任务、历史等资源的操作接口基础路径:/flowable-rest/serviceGET /process-definition 获取所有流程定义遵循 RESTful 风格,使用 JSON 格式。
认证方式默认无认证,需集成 Spring Security 或其他机制可通过 HTTP Basic、Token、OAuth2 等方式Authorization: Basic dXNlcjpwYXNz生产环境必须配置安全认证。
资源分类API 按资源类型组织,如 process, task, history, identity各资源有独立的 URL 路径POST /task/{taskId}/complete 完成任务易于发现和使用。
流程定义管理支持部署、查询、挂起/激活流程定义POST /repository/deployment 部署 BPMN 文件使用 multipart/form-data 上传文件部署后返回部署 ID 和流程定义 ID。
流程实例操作支持启动、查询、删除流程实例POST /runtime/process-instance 启动实例请求体:{"processDefinitionKey": "leave", "variables": [...]}可传递启动变量。
任务管理支持查询、领取、完成、委托任务GET /task 查询任务,POST /task/{id}/complete 完成完成任务时可提交表单变量任务操作需用户上下文(通过认证传递)。
历史数据查询支持查询历史流程实例、任务、变量GET /history/historic-process-instances可添加过滤条件如 finished=true用于报表和审计。
身份管理提供用户、组的 CRUD 接口(若使用内置身份)POST /identity/users 创建用户请求体包含用户信息与 IdentityService 对应。
错误响应统一的错误格式,包含错误码和消息返回 JSON:{"message": "Not found", "exception": "..."}HTTP 状态码反映错误类型(404, 500 等)便于客户端处理异常。
跨域支持(CORS)默认可能禁用,需配置以支持前端调用在 WebMvcConfigurer 中配置 CorsRegistry允许前端跨域访问前后端分离架构必需。

11.2 使用 REST API 操作流程与任务

操作类型HTTP 方法API 端点用途示例请求/响应
部署流程POST/repository/deployments上传 BPMN 文件并部署请求(multipart/form-data): file: leave.bpmn20.xml;响应: {"id": "5001", "name": "leave-process", "deploymentTime": "2025-10-17T10:00:00"}
查询流程定义GET/repository/process-definitions获取所有已部署的流程定义请求: /repository/process-definitions?nameLike=leave%;响应: [{"id":"leave:1:4","key":"leave","name":"Leave Process"}]
启动流程实例POST/runtime/process-instances根据流程定义 Key 启动新实例请求体: {"processDefinitionKey": "leave", "variables": [{"name": "days", "type": "integer", "value": 3}]};响应: {"id": "7501", "processDefinitionId": "leave:1:4", "startTime": "..."}
查询用户任务GET/task查询当前用户可办理的任务请求: /task?assignee=zhangsan&sort=created&order=desc;响应: [{"id":"1001","name":"审批请假","assignee":"zhangsan","created":"2025-10-17T..."}]
领取任务POST/task/{taskId}/claim将候选任务分配给指定用户请求体: {"userId": "lisi"};响应: 200 OK
完成任务POST/task/{taskId}/complete完成用户任务,流程继续请求体: {"variables": [{"name": "approved", "value": true}]};响应: 200 OK
查询历史流程实例GET/history/historic-process-instances获取已结束的流程实例请求: /history/historic-process-instances?processDefinitionKey=leave&finished=true;响应: [{"id":"7501","processDefinitionId":"...","endTime":"..."}]
查询历史变量GET/history/historic-variable-instances获取历史变量值请求: /history/historic-variable-instances?processInstanceId=7501;响应: [{"variableName":"days","value":3,"type":"integer"}]
查询流程图GET/process-definition/{id}/diagram获取流程定义的 SVG 图像响应: 直接返回 SVG 文件流
删除流程实例DELETE/runtime/process-instances/{id}删除正在运行的流程实例请求: /runtime/process-instances/7501;响应: 204 No Content

11.3 自定义 REST 接口集成

集成方式说明配置/代码示例注意事项
嵌入 Spring Boot 应用将 flowable-rest 的控制器集成到自有 Spring Boot 项目中添加 flowable-spring-boot-starter-rest-api 依赖避免端口冲突,可自定义基础路径。
自定义 Controller编写自己的 REST 接口,调用 RuntimeService, TaskService 等@RestController @RequestMapping("/api/workflow") public class WorkflowController { @Autowired private RuntimeService runtimeService; @PostMapping("/start") public ResponseEntity startProcess(@RequestBody StartRequest req) { ProcessInstance instance = runtimeService.startProcessInstanceByKey(req.getProcDefKey(), req.getVariables()); return ResponseEntity.ok(instance.getId()); } }可添加业务逻辑、权限校验、日志等。
AOP 或 Interceptor在 REST 调用前后添加横切逻辑使用 @ControllerAdviceHandlerInterceptor如记录操作日志、性能监控。
参数校验使用 @Valid 和 JSR-303 注解校验请求参数public class StartRequest { @NotBlank private String procDefKey; @Min(1) private Integer days; }提高接口健壮性。
异常统一处理使用 @ControllerAdvice 捕获 FlowableException 并返回友好错误@ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(FlowableObjectNotFoundException.class) public ResponseEntity handleNotFound(FlowableObjectNotFoundException e) { return ResponseEntity.status(404).body(new ErrorResponse("PROCESS_NOT_FOUND", e.getMessage())); } }避免将内部异常暴露给客户端。
与前端框架集成提供 API 供 Angular、React、Vue 等前端调用在前端使用 axios 或 fetch 调用 API需处理认证(如 JWT)。
API 文档使用 Swagger/OpenAPI 生成接口文档集成 springdoc-openapi方便前后端协作和测试。
安全加固结合 Spring Security 实现细粒度权限控制@PreAuthorize("hasRole('APPROVER')") 在 Controller 方法上确保只有授权用户能操作流程。
异步处理对耗时操作(如批量启动)使用 @Async@Async 方法返回 CompletableFuture提高接口响应速度。
性能监控集成 Micrometer 和 Prometheus记录 API 调用次数、耗时用于生产环境监控和优化。

第十二章:Spring Boot 集成与最佳实践

12.1 Spring Boot 中集成 Flowable

集成方式说明配置方法注意事项
Starter 依赖使用官方 Spring Boot Starter 快速集成添加 flowable-spring-boot-starter-engineflowable-spring-boot-starter-rest-api推荐方式,自动配置。
自动配置Starter 会自动配置 ProcessEngine, DataSource, TransactionManager 等无需手动创建 ProcessEngineConfiguration遵循 Spring Boot 约定优于配置。
数据源配置Flowable 使用 Spring 的 DataSourceapplication.yml 中配置:spring.datasource.url, username, password需与业务数据源一致或独立配置。
事务管理自动集成 Spring 的声明式事务(@Transactional在 Service 方法上使用 @Transactional确保流程操作与业务操作在同事务中。
流程定义扫描自动部署 resources/processes/ 目录下的 BPMN 文件可通过 flowable.deployment-resource-pattern 自定义路径classpath*:/my-processes/*.bpmn
引擎配置通过 application.yml@Bean 自定义 ProcessEngineConfigurationflowable.history-level=audit覆盖默认配置。
禁用自动部署开发环境启用,生产环境建议手动控制flowable.deployment-mode=disabled避免意外部署。
多数据源支持Flowable 可配置独立的数据源定义 @FlowableDataSource 注解的数据源 Bean隔离流程数据与业务数据。
健康检查提供 /actuator/health 端点检查引擎状态需启用 flowable.health.enabled=true用于运维监控。
指标监控集成 Micrometer,暴露流程相关指标如流程实例数、任务数等需添加 micrometer-registry-* 依赖。

12.2 自动部署与配置项详解

配置项配置键(application.yml)说明示例值注意事项
部署资源模式flowable.deployment-resource-pattern指定自动部署的 BPMN 文件路径classpath*:/processes/*.bpmn支持 Ant 风格通配符。
部署模式flowable.deployment-mode控制自动部署行为default, single-resource, resource-parent-folder, disableddisabled 表示不自动部署。
部署名称flowable.deployment-name自动生成部署的名称MyApp Processes默认为应用名。
历史级别flowable.history-level设置历史记录级别none, activity, audit, full影响性能和存储,推荐 audit。
异步历史flowable.async-history-enabled是否异步写入历史数据true生产环境推荐开启,提升性能。
异步执行器flowable.async-executor-activate是否激活异步作业执行器true用于定时器、消息等异步任务。
数据库模式更新flowable.database-schema-update控制数据库表的创建和更新true, false, create-drop, customtrue 为开发环境常用。
作业执行器flowable.job-executor-activate是否激活作业执行器(旧版)false新版推荐使用异步执行器。
时区flowable.process-definition-time-zone设置流程定义的默认时区Asia/Shanghai影响定时器事件的触发时间。
限制并发flowable.async-history-executor.nb-threads异步历史执行器线程数5根据服务器性能调整。
自定义配置 Bean通过 Java Config 覆盖默认配置@Bean public ProcessEngineConfigurationCustomizer customizer() { return config -> config.setHistoryLevel(HistoryLevel.FULL); }优先级高于 application.yml

12.3 事务管理与异常处理

主题说明实现方式代码示例注意事项
声明式事务使用 @Transactional 管理流程与业务数据的一致性在 Service 方法上添加注解@Service public class LeaveService { @Autowired private RuntimeService runtimeService; @Transactional public void applyForLeave(LeaveForm form) { leaveRepository.save(form); runtimeService.startProcessInstanceByKey("leave", vars); } }若流程启动失败,业务数据也回滚。
事务传播控制事务的传播行为@Transactional(propagation = Propagation.REQUIRED)默认行为,若存在事务则加入,否则新建避免在异步方法中使用 REQUIRES_NEW 导致流程状态不一致。
异常处理捕获 FlowableException 及其子类try-catch 或 @ControllerAdvicetry { taskService.complete(taskId, variables); } catch (FlowableObjectNotFoundException e) { log.warn("Task not found: " + taskId); throw new BusinessException("TASK_NOT_EXISTS"); }区分业务异常与流程引擎异常。
流程内异常在流程中使用错误事件子流程处理异常定义 errorEventDefinition如 10.3 节所述实现流程级别的容错。
补偿机制使用补偿边界事件和补偿处理器<compensateEventDefinition />用于撤销已执行的操作如”扣款后订单取消,需退款”。
重试机制对异步作业(如服务任务)配置重试次数flowable:asyncExecutorRetryWaitTimeInMillis=5000结合 failedJobRetryTimeCycle防止临时故障导致流程中断。
最终一致性通过事件驱动或定时任务修复不一致状态发布领域事件,由监听器处理在分布式系统中保证数据最终一致。
日志记录记录关键流程操作,便于排查使用 SLF4J 在事务方法中记录log.info("Process started: {}", processInstanceId);结合 MDC 记录用户、请求 ID。
只读事务查询操作使用 @Transactional(readOnly = true)@Transactional(readOnly = true)提高性能,避免脏写适用于历史查询、任务列表等。

12.4 多租户支持与性能优化建议

主题说明实现方式建议/示例注意事项
多租户支持支持一个引擎实例为多个租户服务1. 数据库隔离:每个租户独立数据库Flowable 原生支持通过 tenantId 参数区分租户需在部署、启动流程时指定 tenantId。
2. Schema 隔离:每个租户独立 Schema
3. 数据行隔离:共享表,通过 TENANT_ID_ 字段区分
租户 ID 设置在流程定义和实例中设置租户repositoryService.createDeployment().tenantId("tenant1").deploy()runtimeService.startProcessInstanceByKey("proc", variables).setTenantId("tenant1")租户 ID 会自动传递到任务、历史等表。
查询租户数据按租户 ID 过滤查询结果runtimeService.createProcessInstanceQuery().tenantId("tenant1")所有查询接口均支持 tenantId() 方法确保业务逻辑中正确传递租户上下文。
性能优化 - 异步执行将历史记录、作业执行异步化启用 async-history-enabledasync-executor-activate显著提升流程启动和任务完成的响应速度需监控异步队列积压情况。
性能优化 - 历史级别选择合适的 history-level生产环境使用 audit,避免 fullactivity 级别性能最佳,但审计信息最少根据业务需求权衡。
性能优化 - 分页查询避免一次性查询大量历史数据使用 listPage(firstResult, maxResults)查询任务列表时,每次加载 20 条防止内存溢出。
性能优化 - 缓存启用流程定义缓存Flowable 默认缓存流程定义减少数据库查询缓存失效机制需关注。
性能优化 - 数据库索引为常用查询字段添加数据库索引ACT_RU_EXECUTION.PROC_INST_ID_, ACT_HI_PROCINST.BUSINESS_KEY_提高查询效率根据慢查询日志分析。
性能优化 - 连接池配置高性能数据库连接池使用 HikariCP,合理设置连接数spring.datasource.hikari.maximum-pool-size=20避免连接等待。
性能监控监控流程实例数、任务数、作业数使用 /actuator/metrics 或自定义监控设置告警阈值及时发现性能瓶颈。
定期清理清理已完成的历史数据自定义批处理任务,定期删除 ACT_HI_* 表中过期数据如保留 2 年内的历史数据避免数据库无限增长。

第十三章:监控、运维与调试

13.1 Flowable UI 应用介绍(Modeler、Task、Admin)

UI 应用说明主要功能访问路径注意事项
Flowable Modeler在线流程建模工具,用于设计 BPMN、表单、规则等可视化拖拽设计 BPMN 2.0 流程图;设计 CMMN 案例和 DMN 决策表;创建内嵌表单(Form);管理流程分类(Category)/flowable-modeler基于 Web 的图形化编辑器;生成 .bpmn 文件并可直接部署到引擎
Flowable Task用户任务中心,用于办理和管理待办任务查询个人任务、候选任务、组任务;领取、完成、委托任务;查看流程变量和流程图;上传附件、添加评论/flowable-task面向业务用户的操作界面;支持任务表单数据绑定
Flowable Admin管理员控制台,用于监控和运维流程引擎监控流程实例、任务、作业状态;查看历史数据和审计日志;管理部署、流程定义;管理用户、组(若使用内置身份);查看数据库信息和指标/flowable-admin面向系统管理员和开发人员;可连接多个 Flowable 引擎实例进行集中管理
技术栈基于 AngularJS 和 REST API 构建前端:AngularJS + Bootstrap;后端:Flowable REST API独立部署或集成到应用需配置 flowable.admin.app.engine-api-connectors 连接引擎
用户认证默认使用 Spring Security 内存用户或数据库用户可配置为 LDAP、OAuth2 等admin / test 为默认管理员账号生产环境需替换为安全认证机制
部署方式可作为独立 WAR 包部署在 Tomcat,或嵌入 Spring BootMaven 打包后部署需配置 application.properties 指向正确的数据库和 REST API
集成方式可与自定义系统集成,通过 iframe 或单点登录(SSO)使用 iframe 嵌入已有系统需处理跨域和认证传递
优势快速搭建流程管理前端,无需从零开发提供开箱即用的流程管理功能适合中小型项目或快速原型开发
局限性功能较为通用,难以满足复杂定制需求表单能力有限,复杂业务逻辑需扩展建议在 Task 应用基础上二次开发大型企业通常基于 REST API 自研前端
维护状态Flowable 6.x 后,官方更推荐基于 REST API 自建 UIModeler 功能稳定,但更新较少考虑未来向自定义前端迁移

13.2 流程建模与在线设计

建模功能说明使用方式示例/技巧注意事项
BPMN 设计器可视化拖拽设计流程图在 Modeler 中创建”BPMN 2.0 模型”使用左侧工具栏拖拽任务、网关、事件支持标准 BPMN 2.0 元素
元素属性配置设置任务、流程、事件的属性选中元素后在右侧属性面板编辑任务:设置 Assignee(办理人)、Candidate Users/Groups(候选);流程:设置 Key、Name、VersionKey 用于 API 调用,应语义化(如 leave-approval
流程变量(Variables)定义流程中使用的变量在任务或流程上配置 flowable:field 或通过脚本${employeeName}, ${days}变量可用于表达式、条件判断、表单绑定
脚本任务(Script Task)执行 Groovy、JavaScript 等脚本<scriptTask scriptFormat="groovy">execution.setVariable("approved", true)避免复杂逻辑,影响可维护性
服务任务(Service Task)调用 Java 类或外部服务配置 flowable:classflowable:delegateExpressionclass="com.example.LeaveService"推荐使用 delegateExpression 实现松耦合
用户任务(User Task)人工处理的任务节点设置办理人(Assignee)或候选(Candidate)assignee="${initiator}"候选用户/组可多人领取
排他网关(Exclusive Gateway)基于条件的分支判断为每个流出连线设置 conditionExpression${days > 3}条件表达式使用 JUEL 语法
并行网关(Parallel Gateway)实现并行分支和汇聚使用一对并行网关(分支 + 汇聚)汇聚时等待所有分支完成
内嵌表单(Form)为用户任务设计数据输入表单在 Modeler 中设计表单字段添加 text, number, date 等字段表单字段自动绑定到流程变量
流程分类(Category)对流程进行分组管理在 Modeler 中创建分类(如”HR”, “Finance”)分类名用于过滤和组织提升管理效率
部署到引擎将设计好的流程发布到运行时引擎点击”部署”按钮部署后可在 Task 或 Admin 应用中使用部署后生成 ProcessDefinition

13.3 流程实例监控与问题排查

监控/排查项说明工具/方法操作示例注意事项
流程实例状态查看流程实例是运行中、已结束还是被挂起Flowable Admin 控制台 / RuntimeService / HistoryServiceruntimeService.createProcessInstanceQuery().processInstanceId("7501").singleResult()运行中实例在 ACT_RU_EXECUTION,历史实例在 ACT_HI_PROCINST
当前执行节点确定流程实例当前停留在哪个任务Admin 控制台查看高亮节点 / 查询 Execution 对象runtimeService.createExecutionQuery().processInstanceId("7501").list()多实例或并行分支可能有多个执行流
任务分配情况查看任务的办理人、候选人、创建时间TaskService.createTaskQuery() / Admin 任务列表taskService.createTaskQuery().taskAssignee("zhangsan").list()候选任务需调用 claim() 才能办理
流程变量值检查流程执行过程中的变量值RuntimeService.getVariables(executionId) / HistoryServiceMap<String, Object> vars = runtimeService.getVariables("7501");变量是调试流程逻辑的关键
历史审计日志追踪流程从启动到结束的所有操作HistoryService 查询历史表 / Admin 的历史视图List<HistoricActivityInstance> activities = historyService.createHistoricActivityInstanceQuery().processInstanceId("7501").orderByHistoricActivityInstanceStartTime().asc().list();记录每个活动的开始/结束时间、执行人
作业(Job)监控查看定时器、异步任务的执行状态Admin 的”作业”页面 / ManagementService.createJobQuery()List<Job> jobs = managementService.createJobQuery().list();失败的作业会重试,可手动执行或删除
数据库表分析直接查询 Flowable 系统表定位问题ACT_RU_*(运行时),ACT_HI_*(历史),ACT_GE_*(通用)SELECT * FROM ACT_RU_EXECUTION WHERE PROC_INST_ID_ = '7501';需熟悉表结构,生产环境谨慎操作
日志级别调整开启 DEBUG 日志查看详细执行过程在 logback.xml 或 application.yml 中设置logging.level.org.flowable=DEBUG会产生大量日志,仅用于问题排查
流程图可视化图形化展示流程执行路径Admin 控制台自动高亮当前节点直观了解流程卡点

常见问题:

问题可能原因排查方法解决方案
流程卡住不往下走检查是否有等待消息/信号、定时器未到时、任务未完成查看当前节点类型和作业状态确认是否有外部依赖未触发
任务未分配给正确用户检查 assignee 表达式或候选组配置验证用户是否在候选组中:identityService.createMembership("user1", "hr")表达式语法错误会导致分配失败
变量值不正确检查脚本任务、服务任务或表达式中的变量设置打印变量日志或查询历史变量变量作用域(流程实例 vs 执行流)需注意

第十四章:扩展与定制开发

14.1 自定义 ActivityBehavior

概念说明实现方式代码示例注意事项
ActivityBehaviorFlowable 中每个 BPMN 元素(如任务、网关)的行为接口实现 org.flowable.engine.impl.bpmn.behavior.ActivityBehaviorpublic class CustomTaskBehavior implements ActivityBehavior { @Override public void execute(DelegateExecution execution) { System.out.println("执行自定义任务: " + execution.getCurrentActivityId()); execution.getProcessEngineServices().getRuntimeService().trigger(execution.getId()); } }必须手动调用 trigger 推进流程(除结束节点外)
注册方式将自定义行为绑定到 BPMN 元素在 BPMN 文件中使用 flowable:behaviorClass 属性<userTask id="customTask" name="自定义任务" flowable:behaviorClass="com.example.CustomTaskBehavior" />仅适用于 userTask 等支持该属性的元素
常用子接口更具体的接口,简化实现UserTaskActivityBehavior, ServiceTaskDelegateExpressionActivity, AbstractBpmnActivityBehavior继承 AbstractBpmnActivityBehavior 可自动处理基础逻辑推荐继承抽象类而非直接实现接口
访问执行上下文在行为中获取流程变量、执行 ID 等通过 DelegateExecution 参数String value = (String) execution.getVariable("key"); execution.setVariable("result", "success");DelegateExecution 提供了丰富的 API
应用场景
复杂业务逻辑超出表达式或简单 Java 代理的能力在 execute 方法中调用多个服务、处理事务需注意事务边界
动态任务分配根据运行时条件动态设置办理人在行为中调用 taskService.setAssignee(taskId, userId)需获取当前任务 ID
特殊网关逻辑实现非标准的分支逻辑自定义 ExclusiveGatewayBehavior需明确分支条件
优势完全控制节点行为,灵活性极高
风险容易破坏流程引擎的正常执行流程错误的推进逻辑可能导致流程卡住或死循环需充分测试

14.2 扩展流程引擎行为(ProcessEngineConfiguration)

扩展点说明配置方式代码示例注意事项
customPreBPMNParseHandlers在 BPMN 解析前执行自定义处理注册 BpmnParseHandler@Bean public ProcessEngineConfigurationCustomizer customizer() { return config -> config.setCustomPreBPMNParseHandlers(Collections.singletonList(new CustomParseHandler())); };可修改 BPMN 模型结构,如自动添加监听器
customPostBPMNParseHandlers在 BPMN 解析后执行处理同上常用于添加全局行为
variableTypes注册自定义变量类型实现 VariableType 接口并注册config.getVariableTypes().addType(new CustomVariableType());用于序列化复杂对象
jobExecutor自定义作业执行器实现 JobExecutor高级用法,一般无需修改
transactionFactory自定义事务工厂实现 TransactionFactory与特定事务管理器集成时使用
dataSource指定数据源通过 Spring 配置 DataSource Bean@Bean @FlowableDataSource public DataSource flowableDataSource() { ... }多数据源场景必需
idGenerator自定义 ID 生成策略实现 IdGeneratorconfig.setIdGenerator(new UUIDIdGenerator());默认为 DBIdGenerator
eventListeners注册全局事件监听器实现 FlowableEventListenerconfig.setEventListeners(Collections.singletonList(new CustomEventListener()));见 14.3 节
failedJobCommandFactory自定义失败作业的处理逻辑实现 FailedJobCommandFactory控制重试策略
Spring Boot 配置通过 ProcessEngineConfigurationCustomizer使用 Java Config如上所示优先级高于 application.yml
注意事项扩展需谨慎,避免影响引擎稳定性建议在测试环境充分验证

14.3 插件机制与事件总线(Flowable Event Bus)

机制说明实现方式代码示例注意事项
事件总线(Event Bus)Flowable 内置的事件发布/订阅机制基于观察者模式,发布 FlowableEvent核心扩展点,用于解耦
事件类型引擎在关键节点发布的事件ENTITY_CREATED, ENTITY_UPDATED, TASK_ASSIGNED, TASK_COMPLETED, PROCESS_STARTED, PROCESS_COMPLETED, JOB_EXECUTION_SUCCESS, JOB_EXECUTION_FAILURE全面覆盖流程生命周期可用于监控、审计、通知
事件监听器订阅并处理事件的组件实现 FlowableEventListener 接口public class TaskCompleteListener implements FlowableEventListener { @Override public void onEvent(FlowableEvent event) { if (event instanceof FlowableEntityEvent && event.getType() == FlowableEngineEventType.TASK_COMPLETED) { TaskEntity task = (TaskEntity) ((FlowableEntityEvent) event).getEntity(); log.info("任务完成: " + task.getName()); } } }需注册到 ProcessEngineConfiguration
监听器注册将监听器添加到引擎配置config.setEventListeners(...)config.setTypedEventListeners(...)// 监听所有事件 config.setEventListeners(Arrays.asList(new GlobalEventListener())); // 监听特定类型 config.setTypedEventListeners(Collections.singleton(FlowableEngineEventType.TASK_COMPLETED), Arrays.asList(new TaskCompleteListener()));typedEventListeners 性能更优
异步监听器事件处理在异步线程中执行实现 FlowableAsyncEventListenerpublic class AsyncEmailListener implements FlowableAsyncEventListener { @Override public void onEventAsync(FlowableEngineEntityEvent event) { // 异步发送邮件 } }避免阻塞主流程执行
插件机制通过 ProcessEnginePlugin 在引擎启动/关闭时执行逻辑实现 ProcessEnginePlugin 接口public class CustomPlugin implements ProcessEnginePlugin { @Override public void preInit(ProcessEngineConfigurationImpl config) { } @Override public void postInit(ProcessEngineConfigurationImpl config) { } @Override public void postProcessEngineBuild(ProcessEngine processEngine) { } }是最强大的扩展方式,可用于注册所有扩展点
插件注册将插件注册到引擎Spring Boot 中声明为 @Bean@Bean public ProcessEnginePlugin customPlugin() { return new CustomPlugin(); }自动被 ProcessEngineFactoryBean 发现并调用
应用场景
全局审计日志记录所有流程操作在监听器中写入数据库或发送到日志系统替代数据库触发器
自动通知任务完成时发送邮件/消息在 TASK_COMPLETED 事件中调用通知服务异步执行避免影响性能
数据同步流程状态变化时同步到其他系统在 PROCESS_COMPLETED 事件中调用 REST API实现系统间集成
监控与告警监控超时、失败作业在 JOB_EXECUTION_FAILURE 事件中触发告警提升系统稳定性
优势非侵入式扩展,保持核心逻辑纯净推荐的扩展方式
性能影响监听器过多或处理逻辑过重会影响性能使用异步监听器,优化处理逻辑避免在监听器中执行耗时操作