简介:一套面向Java开发者与BPM初学者的Spring Boot 2.6.13+MySQL 8+Flowable 6.8.1整合资源包,重点解决工作流开发环境搭建繁琐、依赖版本匹配难的问题。包内自带MySQL 8安装程序,降低部署门槛,适合需要快速体验流程引擎、搭建可扩展企业级应用骨架或进行流程自动化改造的技术人员。资源共29个文件,涵盖java源码、xml/yml配置、properties配置、jar依赖包、编译后class文件以及MySQL msi安装程序等,整体382.9MB,通过pom.xml可快速核对依赖版本,目录结构清晰,能够直接构建可运行的flowable开发环境。目前已有108人学习下载。借助这套整合包,读者可以掌握Spring Boot与Flowable的集成方式、BPMN流程定义与任务管理调用方法,还能利用预置MySQL安装器简化数据库初始化,有效减少本地环境差异带来的排错成本。
1. 一个审批流项目,为什么让我把 springboot2.6.13、mysql8 和 flowable6.8.1 绑在一起
前阵子要交付一个内部审批流,选型定了 Flowable,但版本搭配立刻成了麻烦:Spring Boot 2.6.13、MySQL 8 和 Flowable 6.8.1 这三个版本号看似常规,真正放到一起的时候,光是把引擎的表建出来、把第一个请假流程跑通,就花了我一个下午。更要命的是,接手的小伙电脑上连 MySQL 8 都没有装,临时下载安装又是半小时。后来我把 MySQL 8 免安装版直接放进项目目录,写了一个初始化脚本,让任何人拿到代码都能在十分钟内起一个可用的引擎环境。
这套组合:“springboot2.6.13 + mysql8 + flowable6.8.1(自带 mysql8 安装程序)”,其实就是工作流引擎落地的最小可运行单元。Flowable 6.8.1 负责流程定义、任务推动和历史记录,MySQL 8 负责持久化,Spring Boot 2.6.13 负责把它们包成一个可维护的服务。适合谁?适合要快速搭建流程中心原型的团队,也适合想搞清楚 Flowable 怎么在 Spring Boot 项目里省心运行的开发者。下面按我的实际落地顺序讲:先定版本,再装数据库,然后跑通流程,最后把那些能让人血压升高的坑列出来。
2. 锁定版本:springboot2.6.13 与 flowable6.8.1 的依赖矩阵
2.1 为什么选 2.6.13 而不是 2.7.x:Flowable 6.8.1 的兼容性考量
Spring Boot 2.6.13 是 2.6 这条线的收尾版本,安全更新和已知问题修复都比较完整,但又没有进入 2.7 之后那段“什么都升级、缓存配置全变”的折腾期。Flowable 6.8.1 官方对 Spring Boot 的支持集中在中版本,它内部用到的flowable-spring-boot-starter对 Spring Boot 2.5、2.6 的自动配置机制最熟,尤其对条件注解@ConditionalOnMissingBean的处理比较稳。我试过在 Spring Boot 2.7 里跑 Flowable 6.8.1,虽然基本流程也能动,但安全过滤器的排序、自动配置的排除项都跟 2.6 有差异,日志里会多出几条不痛不痒但很碍眼的 warning。如果团队没有强制升级需求,留在 2.6.13 是成本最低的选择。
另一个原因是 MySQL 8 在这个版本组合下,驱动行为更可控。Spring Boot 2.6.13 默认管理的 MySQL 驱动是com.mysql:mysql-connector-j8.0 系列,正好对应 MySQL 8 服务端。如果用旧版mysql-connector-java5.1.49,连接 MySQL 8 时会直接抛认证插件不支持的异常。所以我把版本矩阵固定成三件套:Spring Boot 2.6.13,Flowable 6.8.1,MySQL 8.0.x。这个组合的依赖关系,我在真实项目里至少验证了三个月,没有出现因为版本交错导致的诡异问题。
2.2 最小化 pom.xml:引入 flowable starter 与 mysql 驱动
我用的是 Maven 项目,spring-boot-starter-parent定为 2.6.13。最核心的依赖不是spring-boot-starter-web,而是org.flowable:flowable-spring-boot-starter-process:6.8.1,它会把引擎、流程定义管理、任务服务、历史服务全部拉进来。数据库驱动方面,我直接使用 Spring Boot 依赖管理里的mysql-connector-j,不需要写版本号。下面是一个能跑起流程引擎的最小pom.xml片段:
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.6.13</version> <relativePath/> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.flowable</groupId> <artifactId>flowable-spring-boot-starter-process</artifactId> <version>6.8.1</version> </dependency> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <scope>runtime</scope> </dependency> </dependencies>这里有两个要点。第一,flowable-spring-boot-starter-process只包含流程引擎和自动配置,不包含flowable-ui那套后台界面,适合直接用 API 写业务。如果你还需要在线设计流程图,要额外加flowable-spring-boot-starter-ui-designer,但那个依赖很重,新手不建议一上来就上。第二,mysql-connector-j是 Spring Boot 2.6.13 中驱动的新坐标,旧的mysql-connector-java虽然也能用,但 Maven 会自动把它重定向到新的,没必要自己写版本。
2.3 版本仲裁:避免 mysql-connector-java 与 mysql-connector-j 的坑
很多人在这一步翻车。项目里已经有别处引入了mysql:mysql-connector-java:8.0.28,再单独加com.mysql:mysql-connector-j会导致两个坐标同时存在,最终复制到运行环境里只有一个 jar 但版本不确定。我见过的情况是:本地跑得好好的,部署到服务器上启动直接报ClassNotFoundException: com.mysql.cj.jdbc.Driver。问题不是驱动缺失,而是旧协调版本被排掉了,但新坐标没跟上。
我的处理方式很直接:只要想用 Spring Boot 2.6.13 的默认驱动管理,就把项目里所有mysql-connector-java显式排除掉,只保留mysql-connector-j。如果没法全局排除,可以在pom.xml里用dependencyManagement强制统一版本:
<dependencyManagement> <dependencies> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <version>8.0.33</version> </dependency> </dependencies> </dependencyManagement>搭配mvn dependency:tree -Dincludes=com.mysql看最终到底选了哪个,这一步能省掉很多后面启动时的报错。版本锁定后,数据源 URL 里驱动类必须写com.mysql.cj.jdbc.Driver,这个类在 8.0 系列里是标准的Driver接口实现,和 MySQL 8 服务端的caching_sha2_password认证插件直接兼容。
3. 自带 mysql8 安装程序:免安装版下载、初始化脚本与 Spring Boot 数据源
3.1 免安装版 MySQL8 的下载与目录规划
团队里一半人的电脑都是 Windows,而且都没有数据库管理权限。让他们单独装 MySQL 8 服务端,会遭遇安装向导卡住、服务启动失败、端口被占用一系列问题。所以我放弃了传统 MSI 安装,直接选用 MySQL 8 免安装版(zip 格式)。这种包解压出来就是完整的 mysql 目录,不写注册表,不创建 Windows 服务,适合在项目里当“本地工具”用。
目录规划是这样做:在项目根目录建一个tools/mysql8文件夹,把解压后的bin、lib、share、support-files等文件夹放进去。注意不要直接把 zip 包提交到 Git 仓库,一般会在README里写清楚去哪里下载 mysql8 免安装版,然后让各人自己解压。如果必须提交,记得用 Git LFS,否则一个 300MB 的包会让仓库膨胀到没法用。实际上下载 mysql8 免安装版时,找官网mysql-8.0.33-winx64.zip这类名字就对了,下载完成后校验一下 SHA256 再解压。
3.2 初始化脚本:mysqld initialize-insecure 与建库建用户
免安装版解压后还不能直接用,必须先初始化数据目录。MySQL 8 的mysqld --initialize会生成一个随机 root 密码,这在自动化脚本里很难处理。我更推荐--initialize-insecure,它会生成一个 root 空密码实例,脚本里可以无密码登录,然后再立刻设置密码。下面是项目里的一个init_mysql.bat脚本片段,它在 Windows 命令行下执行:
@echo off set MYSQL_BIN=.\tools\mysql8\bin set MYSQL_DATA=.\data\mysql if not exist "%MYSQL_DATA%" ( "%MYSQL_BIN%\mysqld" --initialize-insecure --datadir="%MYSQL_DATA%" ) start "" "%MYSQL_BIN%\mysqld" --datadir="%MYSQL_DATA%" --port=3306 --console timeout /t 5 /nobreak "%MYSQL_BIN%\mysql" -u root -e "ALTER USER 'root'@'localhost' IDENTIFIED BY 'root123'; CREATE DATABASE IF NOT EXISTS flowable DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;"逻辑说明:第一步判断数据目录是否存在,如果不存在就用mysqld --initialize-insecure初始化,这一步会在tools/mysql8/bin所在的同级生成data/mysql目录,里面包含mysql、performance_schema、sys等系统库。第二步启动mysqld,注意这里用的是start,它会另开一个命令行窗口跑服务,避免阻塞脚本。第三步等待 5 秒后,用 root 空密码进入 MySQL,执行ALTER USER把密码改成root123,同时创建业务库flowable。
参数说明:--initialize-insecure对应的老参数是--initialize --user=root,如果你不习惯insecure,也可以用初始化后从日志里捞临时密码的方式,但脚本化不方便。--port=3306要在启动和客户端连接时保持一致,如果本机 3306 被占用,建议改成 3307,后面 Spring Boot 的 URL 也要跟着改。密码不要用root123这种太简单的,至少在本地环境中换成一个只作用于开发库的密码。另外,utf8mb4是必须的,Flowable 的表里有注释和中文流程定义,用utf8会存不下某些字符。
3.3 配置 spring.datasource:连接参数逐个说清
MySQL 8 起来之后,Spring Boot 这边的数据源配置要把驱动、URL、用户名、密码全部对好。我在application.yml里通常写下面这组配置:
spring: datasource: url: jdbc:mysql://localhost:3306/flowable?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true username: root password: root123 driver-class-name: com.mysql.cj.jdbc.Driver flowable: database-schema-update: true history-level: auditallowPublicKeyRetrieval=true是我建议固定写上的参数。MySQL 8 默认认证插件是caching_sha2_password,在没有 SSL 加密的情况下,客户端首次连接需要向服务端请求公钥,如果不允许,连接会直接失败。serverTimezone要明确指定,不然 MySQL 会返回系统默认时区,可能让java.sql.Timestamp转换出现数小时的偏移。useSSL=false是因为本地开发环境不需要加密,避免额外握手。
这段配置同时开启了 Flowable 的database-schema-update: true。它的意思是:启动时让 Flowable 自动创建或者升级引擎表。首次运行会创建ACT_RE_*、ACT_RU_*、ACT_HI_*等几十张表,表名都是大写前缀,这是 Flowable 的命名约定,不要试图改成小写。后面我会说,这配置在生产环境不能长期开着,但在开发阶段非常省心。
4. 跑通 flowable6.8.1:自动建表与请假流程最小闭环
4.1 引擎自动建表:database-schema-update 与 history-level
数据源配好后,直接启动 Spring Boot 应用,控制台会刷出一堆建表 SQL 日志。这是因为flowable-spring-boot-starter-process在启动时会创建一个ProcessEngine,它会读取spring.flowable.database-schema-update。这里注意值不是true/false这么简单,Flowable 支持true、false、create-drop、drop-create四个值,其中true表示“如果需要则创建,然后不再改变表结构”,create-drop会在关闭时删表,绝不能在开发环境用。
history-level我设成audit,默认实际是audit。它控制历史表的粒度:none不记录任何历史,activity记录流程实例和活动执行历史,audit额外记录流程变量和表单属性,full记录所有内容。对于审批流场景,audit就够了,能查“谁在什么时候提交了什么申请”,不需要full产生的大量冗余数据。如果忘了配置,Flowable 默认也是audit,但显式写出来能让维护的人看到意图。
4.2 写一个 bpmn 流程:请假审批的资源与部署
Flowable 的表建好后,还需要一个流程图才能驱动流程实例。我把请假流程放在src/main/resources/processes/leave.bpmn20.xml,Spring Boot 启动时会自动扫描这个目录并部署到引擎。一个最简的“提交申请 -> 经理审批 -> 结束”流程如下:
<?xml version="1.0" encoding="UTF-8"?> <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:flowable="http://flowable.org/bpmn" targetNamespace="http://flowable.org/bpmn"> <process id="leaveProcess" name="请假审批" isExecutable="true"> <startEvent id="start" name="开始"/> <userTask id="apply" name="提交申请" flowable:assignee="${applyUserId}"/> <userTask id="approve" name="经理审批" flowable:assignee="${approveUserId}"/> <endEvent id="end" name="结束"/> <sequenceFlow id="flow1" sourceRef="start" targetRef="apply"/> <sequenceFlow id="flow2" sourceRef="apply" targetRef="approve"/> <sequenceFlow id="flow3" sourceRef="approve" targetRef="end"/> </process> </definitions>这个文件看起来简单,但几个属性必须解释清楚。isExecutable="true"表示这个流程定义可以被引擎执行,如果你从其他工具导出的 bpmn 文件里没有这个属性,部署会成功但启动流程时会报“流程不存在”。userTask上的flowable:assignee指定了任务办理人,它是表达式,${applyUserId}会在运行时从流程变量里取值。如果你想做成候选组,可以用flowable:candidateGroups="managers",但那需要配置 Spring Boot 和 Flowable 的用户组集成,最开始没必要。
4.3 启动流程与完成任务:Service + Controller 代码
有了流程定义,下一步是写一个接口来发起流程、查任务、完成任务。我用RuntimeService和TaskService最顺手。下面是简化后的 Controller 代码片段:
@RestController @RequestMapping("/leave") public class LeaveController { private final RuntimeService runtimeService; private final TaskService taskService; public LeaveController(RuntimeService runtimeService, TaskService taskService) { this.runtimeService = runtimeService; this.taskService = taskService; } @PostMapping("/start") public String start(@RequestParam String applyUserId, @RequestParam String approveUserId) { Map<String, Object> variables = new HashMap<>(); variables.put("applyUserId", applyUserId); variables.put("approveUserId", approveUserId); ProcessInstance instance = runtimeService .startProcessInstanceByKey("leaveProcess", variables); return instance.getId(); } @GetMapping("/tasks") public List<Task> tasks(@RequestParam String userId) { return taskService.createTaskQuery().taskAssignee(userId).list(); } @PostMapping("/complete") public String complete(@RequestParam String taskId) { taskService.complete(taskId); return "ok"; } }逻辑说明:startProcessInstanceByKey的第一个参数是流程定义 key,对应 bpmn 里的id,不是流程名称。第二个参数是流程变量,它会把applyUserId和approveUserId放进流程上下文,这样userTask的assignee表达式就能拿到值。查任务的时候用taskAssignee(userId)过滤,这模拟了登录人看到自己的待办列表。taskService.complete(taskId)会把任务推进到下一节点,如果还有后续任务, Flowable 会自动创建。
参数说明:这个最简接口里没有做任务认领和权限校验,真实项目里至少要把taskAssignee换成任务候选组,再配合 Spring Security 做身份绑定。但作为跑通闭环,这三段代码足够。启动流程后,你可以先调start拿到流程实例 ID,再调tasks看第一个任务落在谁名下,然后调complete,再调一次tasks看任务是否转移到经理节点。这一步能验证工作流引擎的核心流转没有白配置。
5. flowable6.8.1 + mysql8 避坑排查:从连接失败到建表异常
5.1 连接被拒绝:caching_sha2_password 与 Public Key Retrieval 问题
现象是应用启动时数据源初始化报错,日志里有Unable to connect to MySQL server或者Public Key Retrieval is not allowed。第一类错通常是 MySQL 8 服务没启动,端口不对,或者datadir路径有空格导致mysqld启动失败。第二类错是 MySQL 8 认证插件导致的,它出现在用户名密码都对的情况下,很反直觉。
原因:MySQL 8 默认的认证插件是caching_sha2_password,如果不走 SSL,客户端在认证握手时需要从服务端获取 RSA 公钥来加密密码。JDBC 驱动出于安全考虑默认不允许自动获取公钥,所以会拒绝连接。解决:在 JDBC URL 上增加allowPublicKeyRetrieval=true参数。同时确认user表的plugin字段是caching_sha2_password还是mysql_native_password,如果项目里某些老框架不支持新插件,可以执行ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'root123';换回旧认证,但我不建议这么做,新驱动都能兼容caching_sha2_password。
5.2 时区与字符:serverTimezone 与 utf8mb4 的连锁反应
现象是流程启动正常,但查询历史任务时发现时间差了 8 小时;或者部署流程定义时,中文变量名在数据库里变成乱码。这两个问题经常一起出现,但排查路径完全不同。时间问题出在serverTimezone没设。MySQL 8 服务端默认时区是系统时区,如果操作系统是 CST(中国标准时间),JDBC 驱动拿到的时间可能包含夏令时干扰,Spring Boot 里LocalDateTime序列化后又糊一层时区。解决:JVM 和 JDBC URL 都统一成Asia/Shanghai,并在mysqld启动参数里加--default-time-zone=+08:00。
乱码问题出在数据库字符集。Flowable 的ACT_RU_VARIABLE表如果用了latin1排序规则,流程变量里的中文会变成?。解决:建库时明确CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci。如果你已经建错了库,可以改库的默认字符集,但已有表的列字符集不会跟着变,更稳妥的做法是DROP DATABASE后重建。反正开发环境还没有业务数据,早删早省心。
5.3 建表失败:表名大小写与数据库初始化的顺序
现象是 Flowable 在启动时建表,日志里突然报Table 'FLOWABLE.ACT_RU_TASK' doesn't exist,但明明ACT_RU_*已经建好了。另一个奇怪现象是同一套代码,Windows 上跑得好好的,Linux 上启动直接挂掉。这本质上是 MySQL 的lower_case_table_names参数在作怪。Windows 上默认值为 1,表名不区分大小写;Linux 上默认值为 0,表名区分大小写。Flowable 生成的表名是小写,但它内部某些 SQL 会引用大写表名,如果在 Linux 上用严格模式,就会报表不存在。
解决:我的习惯是在初始化 MySQL 时,直接在my.ini里把lower_case_table_names设为 1。这里要特别提醒,MySQL 8 要求在初始化数据目录之前就要定下这个参数,否则实例启动后修改会报错,甚至无法启动。所以免安装版脚本里,我会在mysqld --initialize-insecure之前先写好my.ini,内容大致为:
[mysqld] basedir=C:/work/project/tools/mysql8 datadir=C:/work/project/data/mysql port=3306 lower_case_table_names=1basedir和datadir要对准项目里实际的路径,否则mysqld启动时会用默认目录找一圈然后报错。还有一点:脚本初始化流程必须是“先有my.ini再跑mysqld初始化”,反过来表结构和系统库已经按大小写敏感创建了,再改参数就是给自己找坑。
5.4 Actuator 与日志:如何快速定位引擎初始化失败
当你已经排除了连接、时区、大小写问题,引擎还是起不来,我让你直接看两处。第一处是 Spring Boot 启动日志里的Flowable相关行,通常会看到FlowableActivitiAutoConfiguration或者ProcessEngineAutoConfiguration的 condition 评估。如果日志里提示某个Bean没创建,就用--debug参数启动,把自动配置报告打出来。
第二处是直接查询库里有没有表。在应用启动前,先手动执行SELECT * FROM ACT_RU_EXECUTION,如果报“表不存在”,说明引擎还没连接成功;如果表在但启动依然报初始化失败,可以把spring.flowable.database-schema-update临时设置成false,让 Flowable 跳过建表逻辑,只看能不能拉起引擎。这样能区分“建表脚本问题”和“引擎 Bean 初始化问题”。常见错误还有在pom.xml里同时引入flowable-spring-boot-starter-process和flowable-spring-boot-starter-rest,导致两个ProcessEngine自动配置冲突,日志里会出现端口占用或重复初始化。我的原则是:只加一个 starter,把 REST 接口自己用 Controller 写,别偷懒。
6. 验证与进阶:用 hikari 参数和异步执行器让流程真正可用
到这里,基础环境已经跑通了,但离“能投入小组开发”还差一步。我建议你按下面的顺序做一次完整验证。第一步,启动 Spring Boot 应用后访问http://localhost:8080/leave/start?applyUserId=zhaoyi&approveUserId=manager,记录返回的流程实例 ID。第二步,调/leave/tasks?userId=manager,你会在 JSON 里看到applyUserId节点生成的任务。第三步,调/leave/complete?taskId=xxx,再调/leave/tasks?userId=manager,发现任务列表为空,而历史库里多了ACT_HI_PROCINST记录。这三步说明引擎、数据库、流程定义三者已经闭环。
然后我把数据源连接池参数调了一下。Spring Boot 2.6.13 默认用 HikariCP,maximum-pool-size默认是 10,对工作流引擎来说其实偏大。因为 Flowable 的异步执行器会额外拿连接,如果不限制,开发环境很容易把 MySQL 的连接数打满。我习惯设置spring.datasource.hikari.maximum-pool-size=20,同时把minimum-idle保持默认,让连接池在活跃两个连接时就够用。另一个必须开的是spring.flowable.async-executor-activate=true,这个配置让 Flowable 的定时任务和异步触发器真正生效,例如延时审批、超时自动提醒都依赖它。
除连接池外,我还有一个绕不开的教训:Flowable 的监听器里不要直接调远程服务。之前我把一个流程监听器写在taskService.complete()的调用堆栈里,里面同步调用外部接口,结果数据库事务没提交,任务已经推进了,外部系统却回滚了。后来我把这种动作放到消息队列里,监听器只负责发布事件,Flowable 引擎的事务边界就干净了。过程中我也吃过几次滥用history-level=full的亏,生产环境一张ACT_HI_VARINST用了几个月就膨胀到几十个 G。开发阶段用audit就好。顺带一提,项目里自带的 mysql8 安装程序虽然是临时方案,但它让我在任何一台新机器上都能快速复现问题,这个做法我保留了。每到一个新环境,先跑初始化脚本,再启动 Spring Boot,流程必然能原地站起来。希望帮到你。
本文还有配套的精品资源,点击获取