
如果你正在开发一个需要流程审批、任务流转或自动化业务逻辑的Java应用比如OA系统、工单系统或CRM那么“工作流引擎”这个概念你一定不陌生。但很多开发者一听到要集成工作流第一反应往往是“复杂”、“配置繁琐”、“学习曲线陡峭”。特别是当你面对Activiti、Flowable这类功能强大的引擎时光是理解BPMN 2.0规范、部署流程定义、处理任务节点就足以让人望而却步。这篇文章要解决的正是这个痛点。我们不讲那些深奥的引擎内核原理而是聚焦于一个更实际、更“接地气”的问题如何在一个标准的SpringBoot项目中快速、优雅地集成一个工作流引擎并为其配备一个现代化的、可拖拽的流程设计器让业务人员也能参与流程设计我的核心判断是集成工作流引擎的关键不在于把引擎的所有API都学一遍而在于理清“引擎核心服务”与“前端流程设计器”之间的协作关系并搭建一条从设计、部署到运行的无缝管道。很多教程只讲后端或只讲前端导致开发者无法串联起完整闭环。本文将用上下两篇的篇幅彻底打通这条路径。本篇上篇将专注于后端引擎的集成与核心服务搭建下篇则会深入前端bpmn-js编辑器的集成与前后端联调。读完本文你将能独立完成以下工作在SpringBoot 2.7项目中集成Activiti/Flowable工作流引擎。理解并配置引擎的核心服务RepositoryService, RuntimeService, TaskService等。掌握流程定义部署、流程实例启动、任务查询与完成等核心API的使用。为后续接入前端可视化编辑器bpmn-js准备好坚实的后端API基础。我们将使用SpringBoot 2.7.18、Flowable 6.8.0与Activiti同源更活跃和MySQL 5.7进行演示。整个项目会从零开始构建确保每一步都可复现。1. 工作流引擎集成为何是SpringBoot项目的“效率倍增器”在深入代码之前我们先明确一个观念工作流引擎不是“另一个需要集成的框架”而是一个业务逻辑的编排与执行中枢。想象一下请假流程员工提交 → 经理审批 → HR备案。如果没有引擎你需要手动在代码里写死状态流转if (status “SUBMITTED”) { … }每增加一个审批节点或分支条件就要修改代码、重新测试、发布上线。工作流引擎将这种“状态流转逻辑”从业务代码中抽离出来用可视化的BPMN流程图来定义。它的价值体现在解耦与灵活性业务流程变更如增加一个会签节点只需修改流程图无需改动核心业务代码。可视化与可维护性业务人员可以通过设计器理解流程降低技术沟通成本。可追溯性引擎自动记录流程实例的完整执行路径和任务历史便于审计和排查问题。高复用性一套引擎可以支撑公司内数十种不同的业务流程。对于SpringBoot开发者而言集成工作流引擎意味着为你的应用增加了一个强大的“业务流程管理层”。而我们要做的就是让这层管理变得简单、可控。2. 核心概念扫盲BPMN、Flowable与bpmn-js在动手之前快速厘清几个关键术语避免后续混淆。BPMN 2.0 (Business Process Model and Notation)这是业务流程建模的全球标准可以理解为流程图的“语法”。它定义了一套图形元素如开始事件、用户任务、网关、结束事件和XML格式用于描述业务流程。工作流引擎如Flowable的核心功能之一就是解析和执行BPMN 2.0格式的流程图。Flowable 工作流引擎一个轻量级、高性能的Java工作流和业务流程管理引擎。它源于Activiti目前社区更活跃。它提供了完整的API来部署BPMN流程定义、启动流程实例、管理用户任务、处理历史数据等。我们将用它作为后端引擎。bpmn-js一个基于JavaScript的BPMN 2.0流程图查看器和编辑器库。它提供了强大的可视化交互能力允许用户在浏览器中拖拽元素、设计流程并生成或解析标准的BPMN 2.0 XML。它是实现“用户友好流程设计器”的前端技术基石。三者关系简图[bpmn-js 前端设计器] --(生成/编辑)-- [BPMN 2.0 XML] | | |(上传/下载) |(部署) V V [SpringBoot 后端] --(调用)-- [Flowable 引擎] --(解析执行)-- [数据库]简单说用户用bpmn-js画图生成BPMN XML后端接收XML通过Flowable引擎部署引擎根据XML定义在运行时驱动流程流转并将状态持久化到数据库。3. 环境准备构建你的SpringBoot Flowable项目骨架我们使用IDEA进行开发确保你的环境已安装JDK 8、Maven 3.6和MySQL 5.7。3.1 使用Spring Initializr创建项目通过 https://start.spring.io 或IDEA内置的Spring Initializr创建项目选择以下依赖Project: MavenLanguage: JavaSpring Boot: 2.7.18 (选择长期支持版本)Dependencies:Spring Web(提供REST API能力)Spring Data JPA(简化数据库操作Flowable会自动创建表)MySQL Driver(数据库驱动)Lombok(简化Java Bean编写可选但推荐)生成项目后解压并用IDEA打开。3.2 添加Flowable依赖打开项目的pom.xml文件在dependencies部分添加Flowable Spring Boot Starter依赖。注意Spring Boot 2.7.x 对应 Flowable 6.x 版本。!-- pom.xml -- dependencies !-- Spring Boot Starters (Initializr已生成) -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-jpa/artifactId /dependency dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId scoperuntime/scope /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency !-- Flowable 核心依赖 -- dependency groupIdorg.flowable/groupId artifactIdflowable-spring-boot-starter/artifactId version6.8.0/version /dependency !-- 可选Flowable UI相关模块包含Modeler可用于基础设计器 -- !-- 本篇我们先聚焦核心引擎下篇再深度集成bpmn-js此处可先不加 -- !-- dependency groupIdorg.flowable/groupId artifactIdflowable-ui-modeler-rest/artifactId version6.8.0/version /dependency -- /dependencies3.3 配置数据库连接在src/main/resources/application.yml(或application.properties) 中配置MySQL数据库连接。Flowable启动时会自动检测数据源并创建所需的表结构。# src/main/resources/application.yml spring: datasource: url: jdbc:mysql://localhost:3306/flowable_demo?useUnicodetruecharacterEncodingutf-8useSSLfalseserverTimezoneAsia/Shanghai username: root password: your_password driver-class-name: com.mysql.cj.jdbc.Driver jpa: hibernate: ddl-auto: update # 设置为update让JPA自动更新表结构生产环境建议用validate或none show-sql: true # 开发时显示SQL便于调试 # Flowable 特定配置 flowable: # 是否在启动时自动创建/更新数据库表结构默认true database-schema-update: true # 是否异步执行历史数据记录默认true性能更好 async-executor-activate: true # 关闭Flowable自带的HTTP基本认证如果不需要其自带REST API rest-api-enabled: false重要提醒请提前在MySQL中创建名为flowable_demo的数据库。启动应用后Flowable会自动创建近60张表表名以ACT_开头如ACT_RE_PROCDEF流程定义表ACT_RU_TASK运行时任务表等。4. 核心服务解析理解Flowable的“五脏六腑”Flowable引擎通过一系列Service接口提供所有功能。理解这些服务是进行任何操作的基础。它们都是线程安全的可以通过依赖注入轻松获取。服务名主要职责关键操作举例RepositoryService流程定义和部署单元的管理。deploy()部署流程createDeployment()创建部署deleteDeployment()删除部署。RuntimeService流程实例Process Instance的启动与管理。startProcessInstanceByKey()启动流程deleteProcessInstance()删除实例。TaskService用户任务User Task的查询与操作。createTaskQuery()查询任务complete()完成任务claim()认领任务。HistoryService查询历史数据如已完成的流程实例、任务、活动。createHistoricProcessInstanceQuery()查询历史流程实例。IdentityService管理用户和组引擎内置的身份体系通常与业务系统集成。createUserQuery()查询用户。FormService处理动态表单可选。getStartFormData()获取启动表单数据。ManagementService元数据管理和引擎维护。getTableCount()获取表数据量。在我们的SpringBoot项目中这些服务已经由Flowable自动配置为Spring Bean你可以直接Autowired注入使用。5. 核心API实战从部署流程到完成任务现在我们创建一个简单的请假流程并通过代码演示整个生命周期。首先我们需要一个BPMN XML文件。为了方便我们先在src/main/resources/processes目录下创建一个最简单的请假流程。5.1 创建BPMN流程定义文件在src/main/resources下新建目录processes然后创建文件leave-application.bpmn20.xml。内容如下?xml version1.0 encodingUTF-8? definitions xmlnshttp://www.omg.org/spec/BPMN/20100524/MODEL xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xmlns:flowablehttp://flowable.org/bpmn targetNamespacehttp://www.flowable.org/processdef !-- 定义一个流程id是引擎使用的keyname是显示名称 -- process idleaveApplication name请假申请流程 isExecutabletrue !-- 开始事件 -- startEvent idstartEvent name开始申请/ !-- 用户任务员工提交申请 -- userTask idsubmitLeaveRequest name提交请假申请 flowable:assignee${applicant} documentation员工填写请假单并提交/documentation /userTask !-- 顺序流从开始事件到提交任务 -- sequenceFlow idflow1 sourceRefstartEvent targetRefsubmitLeaveRequest/ !-- 用户任务经理审批 -- userTask idmanagerApprove name经理审批 flowable:assignee${manager} documentation经理审批员工的请假申请/documentation /userTask !-- 顺序流从提交到审批 -- sequenceFlow idflow2 sourceRefsubmitLeaveRequest targetRefmanagerApprove/ !-- 排他网关根据审批结果决定流向 -- exclusiveGateway iddecisionGateway name审批结果?/ sequenceFlow idflow3 sourceRefmanagerApprove targetRefdecisionGateway/ !-- 网关出口1批准 -- sequenceFlow idflowApprove sourceRefdecisionGateway targetRefapproveEnd !-- 条件表达式当变量 approved 为 true 时走此路径 -- conditionExpression xsi:typetFormalExpression${approved true}/conditionExpression /sequenceFlow !-- 网关出口2拒绝 -- sequenceFlow idflowReject sourceRefdecisionGateway targetRefrejectEnd conditionExpression xsi:typetFormalExpression${approved false}/conditionExpression /sequenceFlow !-- 结束事件批准 -- endEvent idapproveEnd name请假批准/ !-- 结束事件拒绝 -- endEvent idrejectEnd name请假拒绝/ /process /definitions这个流程定义了开始 → 员工提交 → 经理审批 → (批准/拒绝) → 结束。注意flowable:assignee${applicant}这是一个表达式意味着任务的办理人将在启动流程时通过变量applicant动态指定。5.2 编写Service层封装流程操作创建一个Service类来封装对Flowable引擎的调用这是更清晰的工程实践。// src/main/java/com/example/demo/service/FlowableService.java package com.example.demo.service; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.flowable.engine.*; import org.flowable.engine.repository.Deployment; import org.flowable.engine.repository.ProcessDefinition; import org.flowable.engine.runtime.ProcessInstance; import org.flowable.task.api.Task; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.ResourceUtils; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.util.HashMap; import java.util.List; import java.util.Map; Service Slf4j RequiredArgsConstructor public class FlowableService { // 注入Flowable的核心服务 private final RepositoryService repositoryService; private final RuntimeService runtimeService; private final TaskService taskService; private final HistoryService historyService; /** * 部署流程定义从classpath下的bpmn文件 */ public Deployment deployProcessFromClasspath(String bpmnFilePath) { try { // 构建部署 Deployment deployment repositoryService.createDeployment() .addClasspathResource(bpmnFilePath) // 从类路径加载文件 .name(请假流程部署) .deploy(); log.info(流程部署成功部署ID: {}, 部署名称: {}, deployment.getId(), deployment.getName()); // 查询部署后的流程定义 ProcessDefinition processDefinition repositoryService.createProcessDefinitionQuery() .deploymentId(deployment.getId()) .singleResult(); log.info(流程定义ID: {}, Key: {}, 版本: {}, processDefinition.getId(), processDefinition.getKey(), processDefinition.getVersion()); return deployment; } catch (Exception e) { log.error(流程部署失败, e); throw new RuntimeException(流程部署失败, e); } } /** * 启动一个流程实例 * param processDefinitionKey 流程定义Key即BPMN中process的id * param variables 启动变量用于指定办理人、表单数据等 * return 流程实例ID */ Transactional public String startProcessInstance(String processDefinitionKey, MapString, Object variables) { ProcessInstance processInstance runtimeService.startProcessInstanceByKey(processDefinitionKey, variables); log.info(流程实例启动成功实例ID: {}, 定义ID: {}, processInstance.getId(), processInstance.getProcessDefinitionId()); return processInstance.getId(); } /** * 查询某个用户的待办任务 * param assignee 任务办理人 * return 任务列表 */ public ListTask getTasksByAssignee(String assignee) { return taskService.createTaskQuery() .taskAssignee(assignee) // 指定办理人 .orderByTaskCreateTime().desc() // 按创建时间倒序 .list(); } /** * 完成任务 * param taskId 任务ID * param variables 完成任务时设置的变量如审批意见、审批结果 */ Transactional public void completeTask(String taskId, MapString, Object variables) { if (variables ! null !variables.isEmpty()) { taskService.complete(taskId, variables); } else { taskService.complete(taskId); } log.info(任务 {} 已完成, taskId); } /** * 查询流程定义列表 */ public ListProcessDefinition getProcessDefinitionList() { return repositoryService.createProcessDefinitionQuery() .latestVersion() // 只查最新版本 .orderByProcessDefinitionKey().asc() .list(); } }5.3 编写Controller层提供REST API创建Controller对外提供HTTP接口方便我们测试和后续前端调用。// src/main/java/com/example/demo/controller/ProcessController.java package com.example.demo.controller; import com.example.demo.service.FlowableService; import lombok.RequiredArgsConstructor; import org.flowable.engine.repository.Deployment; import org.flowable.engine.repository.ProcessDefinition; import org.flowable.task.api.Task; import org.springframework.web.bind.annotation.*; import java.util.HashMap; import java.util.List; import java.util.Map; RestController RequestMapping(/api/process) RequiredArgsConstructor public class ProcessController { private final FlowableService flowableService; /** * 部署请假流程 */ PostMapping(/deploy/leave) public String deployLeaveProcess() { Deployment deployment flowableService.deployProcessFromClasspath(processes/leave-application.bpmn20.xml); return 部署成功部署ID: deployment.getId(); } /** * 启动一个请假流程实例 * param applicant 申请人员工 * param manager 审批人经理 * return 流程实例ID */ PostMapping(/start/leave) public String startLeaveProcess(RequestParam String applicant, RequestParam String manager) { MapString, Object variables new HashMap(); variables.put(applicant, applicant); // 对应BPMN中的 ${applicant} variables.put(manager, manager); // 对应BPMN中的 ${manager} // 可以添加更多业务变量如请假天数、原因等 variables.put(leaveDays, 3); variables.put(reason, 回家探亲); return flowableService.startProcessInstance(leaveApplication, variables); } /** * 获取用户的待办任务 * param assignee 用户ID * return 任务列表 */ GetMapping(/tasks) public ListTask getTasks(RequestParam String assignee) { return flowableService.getTasksByAssignee(assignee); } /** * 完成任务例如经理审批 * param taskId 任务ID * param approved 是否批准 * param comment 审批意见 */ PostMapping(/task/complete) public String completeTask(RequestParam String taskId, RequestParam boolean approved, RequestParam(required false) String comment) { MapString, Object variables new HashMap(); variables.put(approved, approved); // 这个变量会被排他网关的条件表达式使用 if (comment ! null) { // 可以添加评论到任务 // flowableService.getTaskService().addComment(taskId, null, comment); } flowableService.completeTask(taskId, variables); return 任务处理完成审批结果: (approved ? 批准 : 拒绝); } /** * 查询所有流程定义 */ GetMapping(/definitions) public ListProcessDefinition getProcessDefinitions() { return flowableService.getProcessDefinitionList(); } }5.4 编写启动类与配置确保主应用类正常并配置JPA实体扫描虽然Flowable表不是JPA实体但此配置无害。// src/main/java/com/example/demo/DemoApplication.java package com.example.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }6. 运行与验证通过API测试完整流程现在启动你的SpringBoot应用。观察控制台日志你应该能看到Flowable自动创建数据库表的SQL语句。6.1 使用Postman或Curl测试API我们按顺序调用API模拟一次完整的请假流程。步骤1部署流程定义POST http://localhost:8080/api/process/deploy/leave响应部署成功部署ID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx步骤2启动一个流程实例员工zhangsan提交经理lisi审批POST http://localhost:8080/api/process/start/leave?applicantzhangsanmanagerlisi响应一个流程实例ID如leaveApplication:1:yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy步骤3查询经理lisi的待办任务GET http://localhost:8080/api/process/tasks?assigneelisi响应一个JSON数组包含任务详情。你会看到任务ID、名称“经理审批”、创建时间等。记录下这个taskId。步骤4经理lisi审批任务批准POST http://localhost:8080/api/process/task/complete?taskId【上一步获取的taskId】approvedtruecomment同意响应任务处理完成审批结果: 批准步骤5验证流程结束再次查询经理lisi的待办任务GET/api/process/tasks?assigneelisi列表应该为空。同时你可以通过Flowable的HistoryService查询历史记录我们在Service层未实现此方法你可以自行扩展确认流程实例已到达“请假批准”的结束事件。6.2 数据库表观察打开你的MySQL数据库flowable_demo观察几个关键表的变化ACT_RE_PROCDEF流程定义表存放了leaveApplication的定义信息。ACT_RU_EXECUTION运行时流程执行实例表流程实例运行期间会有记录。ACT_RU_TASK运行时任务表当任务处于待办状态时如经理审批中会有一条记录。任务完成后记录消失。ACT_HI_PROCINST历史流程实例表流程实例结束后会移至此表。ACT_HI_TASKINST历史任务实例表任务完成后会移至此表。通过数据库你可以直观地看到引擎是如何持久化流程状态的。7. 常见问题与排查思路在集成和测试过程中你可能会遇到以下问题问题现象可能原因排查方式解决方案启动应用时报错Table ‘ACT_GE_PROPERTY’ doesn‘t exist1. 数据库连接失败。2.flowable.database-schema-update设置为false且表未创建。1. 检查application.yml中的数据库URL、用户名、密码。2. 检查数据库是否可连接。3. 检查Flowable配置。1. 修正数据库连接配置。2. 确保flowable.database-schema-update为true默认值。部署流程时失败报XML解析错误1. BPMN XML文件格式错误不符合BPMN 2.0规范。2. 文件路径错误addClasspathResource找不到文件。1. 检查BPMN XML语法可使用在线BPMN验证工具。2. 确认文件是否在src/main/resources/processes/目录下且文件名正确。1. 修正XML文件。2. 使用ResourceUtils.getFile(“classpath:processes/xxx.bpmn20.xml”)先测试文件是否能加载。启动流程实例失败提示No processes deployed with key ‘xxx’流程定义Key错误或该Key的流程未部署。1. 调用/api/process/definitions接口查看已部署流程的Key列表。2. 确认startProcessInstanceByKey方法传入的Key与BPMN中process id...的值完全一致。使用正确的流程定义Key或先部署流程。查询不到用户的待办任务1. 任务办理人 (assignee) 不匹配。2. 流程实例未运行到用户任务节点。3. 任务已被完成。1. 检查启动流程时设置的applicant和manager变量值。2. 查询ACT_RU_TASK表看是否有对应ASSIGNEE_字段的记录。3. 检查流程实例当前活动节点可通过RuntimeService.getActiveActivityIds(processInstanceId)。确保查询时使用的assignee与流程变量中设置的值一致。完成任务后流程没有流向正确的结束事件排他网关的条件表达式 (${approved true}) 未正确匹配。1. 检查completeTask时传入的variablesMap中approved变量的值是否为Boolean类型。2. 查看历史活动记录 (ACT_HI_ACTINST)看流程走了哪条路径。确保传入的变量类型和条件表达式期望的类型一致。在Java中boolean和Boolean在表达式解析时可能有差异建议使用Boolean.TRUE/Boolean.FALSE。事务回滚问题在Service方法中同时操作业务表和Flowable表但事务未统一管理。检查是否在Service方法上添加了Transactional注解。确保Flowable操作和你的业务DAO操作在同一个事务中。在整合业务逻辑的Service方法上使用Transactional。Spring Boot已为Flowable配置了事务管理器。8. 最佳实践与工程建议在将Flowable集成到真实项目前请考虑以下建议流程定义管理策略版本控制Flowable自动管理流程定义版本。每次部署相同Key的流程都会生成新版本。启动流程实例时默认使用最新版本。你可以通过ProcessDefinitionQuery.processDefinitionVersion()指定版本启动。资源存储除了Classpath还可以从文件系统、数据库BLOB字段或Git仓库加载BPMN文件。对于动态上传流程的场景建议将上传的BPMN XML文件存储到数据库或文件服务器并通过DeploymentBuilder.addInputStream()部署。变量使用规范慎用全局变量流程变量在整个实例生命周期内有效。避免存放过大的对象建议只存ID在业务系统中查询详情。变量序列化Flowable默认使用JVM序列化存储对象变量。对于复杂对象确保其实现了Serializable接口。更好的做法是使用VariableType自定义序列化或存储为JSON字符串。用户身份集成Flowable自带的IdentityService通常过于简单。生产环境中你需要将其与公司的用户体系如LDAP、OAUTH2、自建用户表集成。可以通过实现FlowableIdentityProvider接口或更简单地在任务查询时将Flowable的assignee映射为你业务系统的用户ID。高并发与性能异步执行器确保flowable.async-executor-activatetrue默认让历史数据记录等操作异步执行提升主流程性能。数据库连接池Spring Boot已自动配置HikariCP。确保连接池参数如maximumPoolSize根据实际负载调整。历史数据归档Flowable的历史表会随着时间急剧增长。需要制定归档或清理策略可以使用HistoryService按条件删除历史数据或定期将数据转移到历史库。API设计建议封装与防腐如本文所示不要在前端Controller直接调用Flowable的各种Service。应封装一层业务性的ProcessService对外提供如submitLeaveApplication,approveTask等语义清晰的接口内部再调用Flowable API。这有助于隔离引擎变化。统一响应体为所有流程操作API设计统一的成功/失败响应格式包含业务状态码和信息。至此你已经成功在SpringBoot项目中集成了Flowable工作流引擎并实现了流程部署、启动、任务查询与完成的核心闭环。后端引擎已经就绪它就像一台安装好的发动机等待着驾驶舱前端设计器的指令。在下篇中我们将聚焦于如何集成功能强大的bpmn-js流程编辑器构建一个完整的、可拖拽设计流程、并能够与刚搭建好的后端API进行实时部署与测试的前端界面。你将看到BPMN XML如何在前端与后端之间流畅传输最终形成一个企业级可用的流程管理平台雏形。建议将本篇代码运行起来熟悉每个API的调用和数据库表的变化这是理解工作流引擎运行机制的最佳方式。有任何问题欢迎在评论区交流。