☰
Flowable 6.7.2 流程引擎实战:从解压到跑通第一个BPMN流程
2026/10/15 12:11:19 网站建设 项目流程

简介:Flowable 6.7.2 是一款基于 BPMN 2.0 标准的开源工作流引擎,面向企业级流程自动化开发人员、Java 后端工程师及业务流程管理人员,用于解决复杂业务流程的定义、执行、监控与优化问题。压缩包共收录 2000 个文件,约 201.97MB,涵盖 1508 个 html 文档、894 个 sql 脚本、275 个 jar 依赖包、180 个 js 脚本及 156 张 png 图片等,分别对应引擎文档、数据库初始化脚本、核心依赖库与前端资源,结构完整便于按模块查阅。资源围绕 BPMN 2.0 支持、流程引擎执行、任务管理、表单集成、事件驱动、RESTful 与 Java API、监控诊断、用户界面及安全权限管理等关键知识点展开,可帮助读者快速理解流程定义、任务分配与事件处理机制,并借助示例与文档完成集成扩展与性能调优。目前已有 1644 人学习下载,适合需要系统掌握 Flowable 6.7.2 版本特性与流程自动化落地方案的中高级开发者参考。

1. 拿到 flowable-6.7.2.zip 之后:先别急着解压,想清楚你要的是引擎还是全家桶

很多人第一次接触 Flowable,是从一个flowable-6.7.2.zip压缩包开始的。下载完解压一看,里面一堆 war、jar、sql 脚本、示例工程,瞬间懵了——我到底该用哪个?这个包能解决什么问题?简单说,Flowable 是一套 BPMN 2.0 流程引擎,核心能力是把「请假审批」「工单流转」「订单状态机」这类有状态、有分支、有会签的业务逻辑,从代码里抽出来,变成可配置、可追踪、可回放的流程图。flowable-6.7.2.zip是它的完整发行包,里面既有可以直接扔进 Servlet 容器跑的flowable-rest.war,也有嵌入到 Spring Boot 项目里的flowable-spring-boot-starter依赖,还有一整套建表 SQL。适合谁?适合那些业务里已经出现「审批链动不动改、状态流转靠 if-else 堆、流程走到哪了没人说得清」的团队。这篇笔记不聊虚的,就按「解压后先看什么 → 最小可跑环境怎么搭 → 表怎么建 → 坑在哪」的顺序,把这条路走一遍。

2. 拆开 flowable-6.7.2.zip:目录结构决定你的接入姿势

2.1 发行包里到底有什么,哪些是给你用的

解压之后,根目录通常能看到libs、wars、database、docs这几个关键目录。libs里是引擎核心 jar 和 Spring 集成 jar,wars里是flowable-rest.war和flowable-ui.war这类开箱即用的 Web 应用,database下按数据库类型分目录存放建表脚本,docs里是 Javadoc 和 XSD 文件。这里有个选型分叉点:如果你只是想快速看流程长什么样、画图、发起一个实例试试,直接用flowable-ui.war扔进 Tomcat 最省事;如果你是要把引擎嵌进自己的业务系统,那就只取libs下的 jar,配合 Maven 坐标引入。我一般会先看libs里有没有flowable-spring-boot-starter相关的包,有的话说明这个版本对 Spring Boot 支持是完整的,可以直接走 starter 路线,省去手工配ProcessEngineConfiguration的麻烦。

2.2 三种接入方式的选择依据

第一种是独立部署 REST 服务:把flowable-rest.war部署到 Tomcat,业务系统通过 HTTP 调用流程接口。好处是语言无关,坏处是多了一跳网络开销,而且流程里的 Java 委托类得单独打包成 jar 放到容器 lib 下,调试起来很割裂。第二种是嵌入式:在 Spring Boot 项目里加依赖,引擎和业务共用同一个数据源和事务。这是目前最常见的做法,流程状态和业务数据能在同一个事务里提交,不会出现「业务回滚了流程却推进了」的玄学问题。第三种是远程 API 模式,适合流程引擎单独作为一个服务团队维护的场景。对大多数中小团队,我建议直接走嵌入式,flowable-6.7.2.zip里的libs就是为这种方式准备的。

2.3 用 Maven 坐标替代手工拷 jar

虽然你手里有 zip,但生产项目里不建议把 jar 直接塞进WEB-INF/lib,版本冲突时你会后悔没有用依赖管理。常见做法是在pom.xml里声明对应版本的 starter:

<dependency> <groupId>org.flowable</groupId> <artifactId>flowable-spring-boot-starter</artifactId> <version>6.7.2</version> </dependency> <!-- 如果要用流程设计器嵌入到自己的前端,再加这个 --> <dependency> <groupId>org.flowable</groupId> <artifactId>flowable-ui-modeler-rest</artifactId> <version>6.7.2</version> </dependency>

这里flowable-spring-boot-starter会自动带入引擎核心、Spring 集成、MyBatis 等传递依赖。参数上唯一要注意的是版本号必须和 zip 包一致,6.7.2 的 starter 和 6.7.0 的数据库脚本之间可能存在字段差异,混用会在启动时抛Table 'ACT_GE_PROPERTY' doesn't exist或者更隐蔽的列缺失异常。逻辑说明:starter 的作用是自动装配ProcessEngine、RepositoryService、RuntimeService等 Bean,你不需要再写flowable.cfg.xml。如果你是非 Spring 项目,那就得从libs里挑flowable-engine、flowable-bpmn-converter等核心 jar,并手动初始化引擎,那种方式配置量大,新手容易在数据源和事务管理器上翻车。

3. 建表与初始化:数据库脚本怎么跑,跑哪些

3.1 先确认数据库类型和版本

database目录下会按mysql、postgres、oracle、mssql等分文件夹,每个文件夹里又有create、drop、upgrade三类脚本。选错数据库类型的脚本,轻则建表失败,重则建出一半表结构导致后续升级脚本无法执行。我一般会先执行select version();确认数据库大版本,比如 MySQL 8.x 和 5.7 在索引长度和字符集默认值上有差异,虽然 Flowable 的脚本通常兼容,但如果你自己改过innodb_large_prefix之类的参数,就要留意。另一个容易忽略的点是字符集:流程定义里的中文名称、任务注释都依赖utf8mb4,建库时如果用了latin1,后面查出来的全是问号,那时候再改就麻烦了。

3.2 用 SQL 脚本手工建表的最小步骤

以 MySQL 为例,常见做法是新建一个空库,然后按顺序执行脚本。注意不要用source一次性导入整个目录,因为脚本之间有依赖顺序,先跑flowable.mysql.create.engine.sql,再跑flowable.mysql.create.history.sql,最后跑flowable.mysql.create.identity.sql。如果你要用flowable-ui里的模型器,还需要执行flowable.mysql.create.app.sql之类的扩展脚本。手工执行的好处是你能看到每一步的输出,哪张表建失败了立刻能定位。下面是一个用命令行执行的示例:

# 创建数据库,字符集必须是 utf8mb4 mysql -uroot -p -e "CREATE DATABASE flowable_demo DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;" # 按顺序执行核心脚本,注意路径替换成你解压后的实际路径 mysql -uroot -p flowable_demo < /path/to/database/create/flowable.mysql.create.engine.sql mysql -uroot -p flowable_demo < /path/to/database/create/flowable.mysql.create.history.sql mysql -uroot -p flowable_demo < /path/to/database/create/flowable.mysql.create.identity.sql

逻辑说明:engine脚本建的是运行时表,比如ACT_RU_EXECUTION、ACT_RU_TASK,这些表在流程实例结束后会清空;history脚本建的是历史表,ACT_HI_PROCINST、ACT_HI_TASKINST等,记录走过留痕;identity脚本建用户和组表。参数上,如果你希望历史级别是full,那history脚本必须执行,否则引擎启动时检查到历史表缺失会直接报错。执行完可以用show tables like 'ACT_%';确认表数量,通常核心表在 30 张左右,加上 identity 和 app 会更多。

3.3 让引擎自动建表的配置与风险

Spring Boot 集成下可以开启自动建表,配置项是flowable.database-schema-update=true。这个参数的含义是:引擎启动时对比数据库版本和 jar 内记录的版本,不一致就执行升级脚本。开发环境用起来很爽,但生产环境我强烈建议关掉,改成false,由 DBA 审核后手工执行升级脚本。原因很简单:自动升级在某些数据库上会锁表,而且一旦升级失败,引擎处于半升级状态,回滚很痛苦。另一个参数flowable.database-schema-update的可选值还有drop-create,这个只在单元测试里用,千万别带到生产,否则一次重启数据全没。

4. 跑通第一个流程:从 BPMN 文件到任务查询

4.1 画一个最小 BPMN 并部署

先别搞会签、网关、子流程,就画一个「开始 → 用户任务 → 结束」的三节点流程。用flowable-ui的模型器画完导出.bpmn20.xml,或者手写一个最简 XML。部署代码在 Spring Boot 里就一行:

@Autowired private RepositoryService repositoryService; public void deploy() { // 部署 BPMN 文件,流程定义 key 会在 XML 的 process 标签里指定 Deployment deployment = repositoryService.createDeployment() .addClasspathResource("processes/simple-leave.bpmn20.xml") .name("请假流程") .deploy(); System.out.println("部署 ID: " + deployment.getId()); }

逻辑说明:addClasspathResource从类路径加载文件,name是给这次部署起个人能看懂的名字。部署成功后,ACT_RE_PROCDEF表里会多一条记录,KEY_列就是流程定义 key,后续启动实例要用它。参数上注意:如果同一个 key 重复部署,版本号VERSION_会自增,启动实例时默认用最新版本,想指定旧版本得用processDefinitionId而不是 key。

4.2 启动实例并完成任务的完整调用链

启动实例和查询任务通常写在两个方法里,中间隔着用户操作:

@Autowired private RuntimeService runtimeService; @Autowired private TaskService taskService; // 启动流程实例,传入业务 key 方便关联业务数据 public String startProcess(String businessKey) { ProcessInstance instance = runtimeService.startProcessInstanceByKey("simpleLeave", businessKey); return instance.getId(); } // 查询某人待办并完成任务 public void completeTask(String assignee) { Task task = taskService.createTaskQuery() .taskAssignee(assignee) .singleResult(); if (task != null) { // 传变量,后续网关或监听器可以读取 Map<String, Object> vars = new HashMap<>(); vars.put("approved", true); taskService.complete(task.getId(), vars); } }

逻辑说明:startProcessInstanceByKey的第二个参数是业务主键,建议传你业务表的主键,这样以后排查问题时能直接通过ACT_HI_PROCINST.BUSINESS_KEY_反查。taskAssignee是任务办理人,对应 BPMN 里userTask的flowable:assignee属性。complete的第二个参数是流程变量,会写入ACT_RU_VARIABLE,流程结束后转到历史变量表。参数上要留意:如果查询返回多条任务,singleResult()会抛异常,生产代码里应该用list()然后判断,或者加上processInstanceId条件缩小范围。

4.3 验证流程是否真的走通了

跑完之后别只看控制台没报错就完事,去数据库里查三张表:ACT_HI_PROCINST看流程实例是否结束、结束时间是否为空;ACT_HI_TASKINST看任务是否有完成时间;ACT_HI_ACTINST看每个节点的进出时间。如果ACT_HI_TASKINST里任务存在但END_TIME_为空,说明任务没完成,大概率是complete没执行到或者事务回滚了。另一个验证点是ACT_GE_BYTEARRAY表,部署的 BPMN 文件内容会以字节数组存在这里,如果这表里没有记录,说明部署其实没成功,只是没抛异常而已。

5. 避坑与排查:那些让流程卡住的常见问题

5.1 现象:启动报错找不到 ACT_GE_PROPERTY 表

原因:数据源连的库不对,或者建表脚本没执行完。有时候开发环境配了多个数据源,Flowable 默认拿的是主数据源,但主数据源指向了另一个空库。解决:先确认spring.datasource.url指向的库里有ACT_开头的表,没有就补执行脚本;如果表在别的库,要么改数据源,要么给 Flowable 单独配数据源。

5.2 现象:任务查询查不到,但数据库里明明有记录

原因:多租户或者taskAssignee传了空字符串。Flowable 默认不开启租户隔离,但如果你在部署时指定了tenantId,查询时没带租户条件就会查不到。另一个常见原因是办理人字段存的是用户 ID,你传的是用户名。解决:用taskService.createTaskQuery().list()先不加条件查全量,看ASSIGNEE_列的实际值,再决定查询条件怎么写。

5.3 现象:流程走到网关后不动了,也没有报错

原因:排他网关的条件表达式返回了 null 或者没有匹配的分支。BPMN 里网关的conditionExpression如果引用了不存在的变量,表达式求值结果可能是 false,所有分支都不走,引擎就停在那里。解决:在网关前加一个执行监听器打印变量,或者把ACT_RU_EXECUTION里该实例的ACT_ID_查出来,看停在哪个节点。表达式里用${approved == true}比${approved}更稳,因为后者在变量为 null 时会抛异常。

5.4 现象:历史表数据量暴涨,查询越来越慢

原因:历史级别设成了full,而且流程实例量大、节点多。full会记录所有变量和节点进出,数据量是audit的好几倍。解决:非审计强需求场景改成flowable.history-level=audit,只记录实例和任务,不记录变量明细。已经产生的历史数据可以按时间分区或者定期归档,别直接 delete,否则外键约束会让你删不动。

5.5 现象:自定义 Java 委托类里注入的 Spring Bean 为 null

原因:委托类不是 Spring 管理的,引擎用反射 new 出来的实例,@Autowired不生效。解决:把委托类声明成 Spring Bean,然后在 BPMN 里用flowable:delegateExpression="${myDelegate}"而不是flowable:class。前者走 Spring 容器取 Bean,后者走反射新建。这个坑在 6.x 版本里依然常见,血泪经验是:只要委托类里要用数据库或别的服务,一律用 delegateExpression。

6. 进阶技巧:用表达式和监听器把业务逻辑从代码里挪出去

走到这里,流程能跑、任务能查、坑也踩过一轮了。最后分享一个我常用的技巧:尽量把「谁审批」「审批几天」「超时怎么办」这些规则从 Java 代码里挪到 BPMN 的表达式和监听器里。比如任务办理人不要写死,用${leaveService.findManager(employeeId)},其中leaveService是一个 Spring Bean。这样组织架构调整时,你改的是 Bean 里的逻辑,而不是重新部署流程定义。再比如超时提醒,用边界定时事件加执行监听器,监听器里发消息,而不是在业务代码里起定时任务轮询。下面是一个带表达式的用户任务片段:

<userTask id="managerApprove" name="经理审批" flowable:assignee="${leaveService.findManager(initiator)}" flowable:dueDate="${leaveService.calcDueDate(days)}"> <extensionElements> <flowable:executionListener event="end" expression="${leaveService.recordApprovalTime(execution)}"/> </extensionElements> </userTask>

逻辑说明:assignee和dueDate都通过表达式调用 Spring Bean 的方法,executionListener在节点结束时触发,把审批时间写回业务表。参数上注意表达式里的变量名要和流程变量或 Bean 名称一致,execution是引擎传入的内置对象,可以直接用。这样做的代价是 BPMN 文件里嵌了业务逻辑,可读性下降,所以我会在 Bean 方法名上写清楚意图,比如findManager而不是fm。验证方法很简单:部署后启动一个实例,看ACT_RU_TASK的ASSIGNEE_和DUE_DATE_是否按预期填充,再看监听器有没有在ACT_HI_ACTINST里留下记录。我自己的习惯是每加一个表达式,就写一个单元测试断言任务属性,不然等流程跑到一半才发现表达式写错,排查成本比写测试高得多。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询