Flowable低代码引擎源码解析与Spring Boot集成实战
2026/9/11 18:49:01 网站建设 项目流程

简介:这是一份基于 Flowable 的低代码开源工作流引擎设计源码,为 Java 开发者、架构师以及低代码平台研究人员提供一套可二次开发的工程参考。项目包含完整的前后端分离设计:261 个 Java 源文件承担流程引擎集成与业务逻辑,229 个 JavaScript 文件实现前端交互,70 个 CSS 文件用于界面样式,另有 59 个 SVG 图像、8 个 XML 配置和 8 个 SQL 数据库脚本,可用于流程定义、审批节点、表单设计等场景。包体共 662 个文件,压缩后仅 6.36MB,目录结构清晰,适合快速搭建流程中心原型或深入阅读源码,目前已有 1103 人学习下载。通过这套源码,读者可以拿到开箱即用的流程设计器前端模块、后端接口示例和数据库初始化脚本。同时,从节点包装、审批步骤、错误提示等界面组件中,可以理解低代码工作流的建模思路与工程拆分方式,便于在此基础上扩展自己的业务模块。

1. 这套 Flowable 低代码引擎源码,先看文件清单再谈架构

如果你以为 Flowable 就是几个 jar 包、写段 XML 跑起来完事,那拿到这份源码的第一反应大概率是懵:662 个文件里,Java 只有 261 个,JavaScript 却有 229 个,CSS 还占了 70 个。一个“工作流引擎”为什么前端文件接近一半?答案在这套源码的定位里——它不是裸的 Flowable 二次封装,而是一个把 BPMN 建模、流程设计器、表单渲染、运行时管理全部收进来的低代码工作流平台。设计器里拖一个审批节点,生成的是 JSON,引擎最终要执行的却是 BPMN 的 XML,中间这一层转换和兜底逻辑,才是这套源码值得拆的地方。适合两类人:一是要在若依这类脚手架里快速集成审批流的 Java 工程师,二是已经在用 Flowable 但被流程定义文件管理、节点参数传递折腾过的后端开发。下面从存储设计开始往里拆。

2. Flowable 表结构与存储设计:ACT_ 三组表的低代码化改造

2.1 按文件构成反推项目边界

拿到一个源码包,别急着跑。先把文件类型摊开看:261 个 Java 文件对应 Controller、Service、Mapper 和流程定义构建逻辑;229 个 JS 文件里,绝大部分是流程设计器的节点拖拽、属性面板和表单渲染,剩下的是管理后台的列表与统计;70 个 CSS 里能直接看到addNode-82269853.cssstep3-d5ef26d9.cssnodeWrap-8798d817.css这类命名,说明设计器的"增加节点、步骤三、节点包裹层"都有独立样式模块。8 个 XML 大概率是 MyBatis Mapper 和 Spring 配置,8 个 SQL 是引擎表和低代码扩展表的建表脚本。这套结构是一个典型"重前端、轻 BPMN 生成层"的低代码引擎:复杂交互全放在设计器,后端只做模型转换和工作流 API 透传。

2.2 ACT_ 三组核心表:哪些是低代码引擎自己该管的

Flowable 的数据库表分为三类,理解这个边界能解决"flowable 都需要创建那些表"的疑问。ACT_RE_*是流程定义与部署的静态资源表,ACT_RU_*是运行时表,流程结束即清理,ACT_HI_*是历史表,记录审批轨迹。低代码引擎一般不直接碰历史表和身份表,身份走自己的用户体系,历史通过查询接口返回。真正需要扩展的是流程定义和运行时节点配置。

表名归属在低代码引擎中的作用
ACT_RE_DEPLOYMENT静态资源一次部署的元数据,designer 导出的 BPMN 通过它落库
ACT_RE_PROCDEF静态资源部署后生成的流程定义,是发起流程的唯一依据
ACT_RU_EXECUTION运行时流程实例的当前执行树,排查流程卡在哪个环节先查它
ACT_RU_TASK运行时当前待办任务,低代码审批列表直接映射这张表
ACT_HI_PROCINST历史已结束流程的归档,报表和管理台的"我发起的"都查它
ACT_HI_TASKINST历史每个任务节点的办理记录,含开始、结束时间和办理人

低代码引擎自己扩展的表一般在ACT_前缀之外,常见做法是加LC_FLOW_NODE(节点配置)、LC_FLOW_FORM(表单定义)、LC_FLOW_CATEGORY(流程分类),Node 表存设计器生成的节点 JSON,Form 表存表单字段和节点 ID 的绑定关系。这样设计的好处是,BPMN 文件里只写审批链路,业务表单和节点参数作为扩展字段存在业务侧,改表单不需要重新部署流程。

2.3 数据库初始化的两条路线与参数权衡

先看源码里的 8 个 SQL 文件,里面会有flowable.mysql.sql这类官方建表脚本和低代码扩展表脚本。初始化时有两种做法:

-- 方法一:让 Flowable 自建表(开发环境) SET flowable.database-schema-update=true -- 引擎启动时自动检测并创建 ACT_ 前缀的所有表 -- 方法二:手动执行官方 SQL 后关闭自动建表(生产环境) -- 先执行 flowable.mysql.sql 创建 ACT_ 表 -- 再执行扩展表 SQL 创建 LC_FLOW_NODE / LC_FLOW_FORM SET flowable.database-schema-update=false

我一般会在生产环境锁死database-schema-update=false,因为一旦引擎升级,自动建表会尝试对老表做 ALTER,遇到大数据量表直接锁表,非常被动。同时强烈建议给低代码扩展表单独建一个 schema 或用统一前缀,后期做读写分离、归档历史数据都方便。排查问题时,最常用的诊断 SQL 是查流程实例走到了哪个节点:

SELECT p.PROC_INST_ID_, d.NAME_ AS DEPLOYMENT_NAME, p.START_TIME_, r.ACT_ID_ AS CURRENT_NODE FROM ACT_RU_EXECUTION p LEFT JOIN ACT_RE_DEPLOYMENT d ON p.DEPLOY_ID_ = d.ID_ LEFT JOIN ACT_RU_EXECUTION r ON r.PROC_INST_ID_ = p.PROC_INST_ID_ AND r.PARENT_ID_ IS NULL WHERE p.PARENT_ID_ IS NULL ORDER BY p.START_TIME_ DESC LIMIT 10;

这个查询把流程实例、部署名称和当前停留节点拼在一张结果集里,PARENT_ID_ IS NULL取的是流程实例的根执行流,避免把并行分支的子执行流混进来。如果CURRENT_NODEexclusiveGateway,说明流程卡在排他网关的出口判断上,优先检查分支条件表达式的变量名是否和发起时传入的参数一致。

3. 从设计器 JSON 到 BPMN 流程定义:JS 节点模型的转换实现

3.1 设计器保存的节点 JSON 数据结构

低代码引擎的核心输出不是 BPMN 文件,而是一棵节点树。打开源码里 JS 侧的设计器模块,会发现每个节点被抽象成统一的 JSON 对象。比如一个请假审批流程,通过拖拽生成的节点序列是"开始 → 部门经理审批 → 人事复核 → 结束",对应存储结构如下:

{ "processKey": "leave_approval", "processName": "请假审批", "nodes": [ { "nodeId": "start", "nodeType": "start", "nodeName": "开始", "next": ["deptManagerTask"] }, { "nodeId": "deptManagerTask", "nodeType": "userTask", "nodeName": "部门经理审批", "assigneeType": "role", "assigneeValue": "manager", "next": ["hrReviewTask"] }, { "nodeId": "hrReviewTask", "nodeType": "userTask", "nodeName": "人事复核", "assigneeType": "user", "assigneeValue": "${hrUserId}", "next": ["end"] }, { "nodeId": "end", "nodeType": "end", "nodeName": "结束", "next": [] } ] }

next数组是画布上连线关系的映射,assigneeTypeassigneeValue是低代码层对办理人的封装,设计器右侧属性面板改的"指定角色"还是"指定用户",最终都固化在这里。前端拿到这份 JSON 后,通过递归遍历节点树渲染出连线,addNode-82269853.css这种样式文件控制的正是节点卡片和连线的视觉呈现。数据一定要落到流程表单里,刷新之后才不丢配置。

3.2 Java 端把 JSON 转成 BpmnModel 并生成 XML

流程定义最终要落到 Flowable 的 BpmnModel。源码里 Java 端的转换逻辑不是一个一个拼 XML 字符串,而是用 Flowable 提供的模型对象构建,这样生成的 BPMN 文件语义最干净。我自己做这块时习惯写一个BpmnBuilder工具类,核心逻辑如下:

import org.flowable.bpmn.converter.BpmnXMLConverter; import org.flowable.bpmn.model.*; public BpmnModel buildBpmnModel(String processKey, List<NodeVO> nodes) { Process process = new Process(); process.setId(processKey); process.setName(processKey); // 遍历设计器传来的节点列表,按类型创建对应 Flowable 元素 for (NodeVO node : nodes) { if ("start".equals(node.getNodeType())) { StartEvent startEvent = new StartEvent(); startEvent.setId(node.getNodeId()); startEvent.setName(node.getNodeName()); process.addFlowElement(startEvent); } else if ("userTask".equals(node.getNodeType())) { UserTask userTask = new UserTask(); userTask.setId(node.getNodeId()); userTask.setName(node.getNodeName()); userTask.setAssignee(node.getAssigneeValue()); process.addFlowElement(userTask); } else if ("end".equals(node.getNodeType())) { EndEvent endEvent = new EndEvent(); endEvent.setId(node.getNodeId()); endEvent.setName(node.getNodeName()); process.addFlowElement(endEvent); } } // 根据 next 连线关系生成 SequenceFlow for (NodeVO node : nodes) { String sourceId = node.getNodeId(); for (String targetId : node.getNext()) { SequenceFlow flow = new SequenceFlow(sourceId, targetId); flow.setName("flow_" + sourceId + "_" + targetId); process.addFlowElement(flow); } } BpmnModel model = new BpmnModel(); model.addProcess(process); return model; }

Process是 BPMN 的容器对象,StartEventUserTaskEndEvent分别对应开始、审批、结束节点,SequenceFlow的构造参数是sourceReftargetRef,对应我们 JSON 里的nodeIdnext指向。注意setAssignee这里支持${hrUserId}这类表达式写法,Flowable 在任务创建时会把流程变量里 key 为hrUserId的值解析成实际办理人,这是低代码层和引擎层的重要衔接点。之后转成 XML 就交给官方转换器:

BpmnXMLConverter converter = new BpmnXMLConverter(); byte[] bpmnBytes = converter.convertToXML(bpmnModel); // 将 byte[] 以 .bpmn20.xml 后缀部署到 RepositoryService

convertToXML生产出的 BPMN 文件是标准格式,可以直接用 Flowable 官方设计器打开二次编辑,这在项目里是刚需。常见的坑是设计和后端只互传可视化节点,丢了连线关系,结果部署出来的流程是"断头路",点上发起后直接抛No outgoing sequence flow异常。所以保存动作完成前,最好前后端各校验一遍节点可达性:从 start 节点出发能否到达 end 节点。

3.3 表单渲染与 CSS 模块在流程配置中的角色

流程设计器除了画流程,还要绑定每个节点要展示的表单字段。这部分功能对应源码里的step3-d5ef26d9.css流程定义的第三步配置:节点选中后,右侧面板加载表单字段列表,通过拖拽或复选把字段绑定到当前任务节点。运行时待办页面根据节点拿到表单 ID,再去渲染动态表单。这是低代码引擎相对传统 BPM 系统最具差异化的点——每个审批环节看到的字段组合可以完全不同,而不用写死页面。后端存储上,节点和表单的绑定关系就落在扩展表LC_FLOW_NODE里,表里至少要有NODE_IDFORM_IDPROCESS_KEY三个字段,一个节点可以绑定多个表单,按SORT_NO排序展示。这套设计决定了一个流程模板从配置到上线不需要任何 Java 代码变更。

4. flowable 整合 Spring Boot 实战:部署、审批和数据库报错排解

4.1 application.yml 里的参数先对齐

任何 Flowable 项目启动前,先花两分钟核对application.yml。低代码引擎里因为要同时建 ACT_ 表和 LC_ 扩展表,配置比较容易出问题,我一般会这样写:

spring: datasource: url: jdbc:mysql://127.0.0.1:3306/flowable_lowcode?useUnicode=true&characterEncoding=utf8&nullCatalogMeansCurrent=true username: root password: your_password driver-class-name: com.mysql.cj.jdbc.Driver flowable: database-schema-update: true db-history-used: true history-level: audit async-executor-activate: false

history-level有 none、activity、audit、full 四档,低代码平台至少要audit,否则审批历史里看不到流程变量变化,很多统计报表会失去数据源。async-executor-activate在低代码场景建议关掉,因为流程里带业务回调时,异步执行器会脱离业务线程,事务边界变模糊,运维排错也难,除非你的引擎单独配置了消息队列。

4.2 部署、发起、待办、审批的完整链路代码

这一节把完整链路串起来,所有方法都来自ProcessEngine的各个 Service,不依赖额外中间件,照着这个顺序写就能跑通一条请假流:

// 1. 部署流程定义:将设计器生成的 bpmn 文件注入引擎 @Autowired private RepositoryService repositoryService; Deployment deployment = repositoryService.createDeployment() .addClasspathResource("processes/leave_approval.bpmn20.xml") .name("请假审批流程") .deploy(); System.out.println("Deployment ID: " + deployment.getId()); // 2. 发起流程:通过流程定义 key 启动一个实例 @Autowired private RuntimeService runtimeService; Map<String, Object> variables = new HashMap<>(); variables.put("hrUserId", 10086L); variables.put("days", 3); variables.put("reason", "年假"); ProcessInstance instance = runtimeService .startProcessInstanceByKey("leave_approval", variables); // 3. 查询待办:查部门经理拥有的可审批任务 @Autowired private TaskService taskService; List<Task> tasks = taskService.createTaskQuery() .taskAssignee("10001") .processDefinitionKey("leave_approval") .orderByTaskCreateTime().desc() .list(); // 4. 审批通过:完成任务并把审批意见写进流程变量 Map<String, Object> taskVars = new HashMap<>(); taskVars.put("approved", true); taskVars.put("comment", "同意,请人事复核"); taskService.complete(task.getId(), taskVars);

startProcessInstanceByKey用的是ACT_RE_PROCDEF.KEY_,不是刚才 Deployment 的 ID,这点容易搞混。taskAssignee的入参是办理人 ID,对应的是节点定义时指定的办理人值。如果在第 3 步没有接表现层的用户体系,低代码引擎一般会在 TaskListener 里根据节点的assigneeType去查业务用户表替换变量,这样taskAssignee才能精确匹配。complete方法的第二个参数会合并进流程变量,后续排他网关的分支判断(比如approved == true)就是从这批变量里取值,所以网关条件里的变量名必须和这里 put 的 key 完全一致。

4.3 数据库报错三个高频位置与处理方式

搜"flowable 工作流数据库报错"能翻出一堆帖子,实际集中在三个点上。第一是 MySQL 8 以下驱动类名写错:

# 错误信息:ClassNotFoundException com.mysql.jdbc.Driver # 解决方案:driver-class-name 改成 com.mysql.cj.jdbc.Driver

第二是连接串缺少nullCatalogMeansCurrent=true,后果非常隐蔽——Flowable 在初始化时扫描数据库表结构,如果这个参数缺失,MySQL 连接器默认按nullcatalog 扫描元数据,可能把同库其他项目的表误判为需要管理的表,某些极端情况下 DELETE 操作会扩到别的业务表。低代码引擎扩展了 LC_ 表,最容易触发这个问题,所以这个参数必须显式加上。第三是引擎版本升级后ACT_GE_PROPERTY表里的版本号不匹配:

-- 查看当前引擎版本 SELECT NAME_, VALUE_ FROM ACT_GE_PROPERTY WHERE NAME_ = 'schema.version'; -- 正确执行官方升级脚本前,不要手动改 VALUE_

很多时候项目启动失败不是代码问题,而是换了个 Flowable 版本后,旧库里的ACT_GE_PROPERTY.VALUE_还停留在旧版本,引擎启动时做版本校验不通过直接抛异常。解决路径只有一个:先备份,再执行对应版本的flowable.upgrade.sql,不要手工改表。

5. 低代码 Flowable 的进阶边界:监听器绑定、表前缀与若依集成

5.1 用动态 ExecutionListener 解耦业务逻辑

低代码平台不能让用户每次调整业务逻辑都改 Java 代码。我在处理节点前后置动作时,会给 BPMN 节点统一挂一个动态 Listener,通过流程变量指定要调用的 Spring Bean 名称,做到流程配置层面的插件化:

public class DynamicBizListener implements ExecutionListener { @Override public void notify(DelegateExecution execution) { String handlerName = (String) execution.getVariable("_bizHandler"); if (handlerName == null) { return; } ApplicationContext context = SpringUtil.getApplicationContext(); Object handler = context.getBean(handlerName); // 反射调用统一接口 handle(DelegateExecution) ((BizHandler) handler).handle(execution); } }

这个写法的价值在于节点本身固定挂DynamicBizListener,实际执行的逻辑通过设计器属性面板填 Bean 名来切换,比如"发送企微通知"、"更新业务单据状态"都做成独立的BizHandler实现类。事务边界就在handle方法上,和流程引擎共用同一个事务,要么全成要么全回滚。

5.2 表前缀配置与若依集成的两个关键点

多租户或复用数据库时,给 Flowable 表加前缀能避免和现有系统表冲突。配置只在 ProcessEngine 创建时生效,改配置需要重建引擎:

ProcessEngineConfiguration config = ProcessEngineConfiguration .createProcessEngineConfigurationFromResourceDefault(); config.setDatabaseTablePrefix("LC_"); ProcessEngine engine = config.buildProcessEngine();

设置后表名变为LC_ACT_RU_TASK这类形式,建表脚本也会自动带上前缀。还有热门的"若依 flowable 集成"方案,两个坑最值得注意:一是若依自带多数据源路由,Flowable 的 Service 必须和业务数据源走同一个事务管理器,否则出现任务已流转但业务状态没更新的脏数据;二是若依的拦截器会拦截动态表单请求,要在shiroConfig里放行设计器相关的 URL,否则前端能打开画布,但保存节点时直接被拦截,表现成数据写不进去。验证集成的唯一标准是在设计器里走到"导出 BPMN"并成功部署,之后ACT_RE_PROCDEF表里能看到新增记录,流程才算真正可用。

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

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

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

立即咨询