在实际企业级应用开发中业务流程的自动化与可视化设计是提升开发效率和业务响应能力的关键。很多项目在初期使用硬编码处理审批、流转逻辑但随着业务规则频繁变更代码会变得臃肿且难以维护。此时引入一个成熟的工作流引擎将流程逻辑从业务代码中剥离用可视化的方式定义和管理就成为了一个更优的选择。Spring Boot 以其简洁的配置和强大的生态成为集成各类中间件的首选框架。本文将围绕如何在一个 Spring Boot 项目中集成工作流引擎并引入 bpmn-js 这一强大的前端流程设计器构建一个从流程定义、部署到执行的全栈示例。无论你是需要为OA、ERP或CRM系统添加审批流还是希望理解工作流引擎与Spring Boot的整合要点本文都将提供一个清晰的、可复现的实践路径。我们将以 Activiti/Flowable 这类与 Spring Boot 集成度极高的引擎为例分步讲解后端引擎的集成、核心服务的调用以及如何将 bpmn-js 编辑器嵌入前端页面。上篇主要聚焦于后端环境的搭建、引擎配置与核心API的使用确保你能在本地成功运行一个工作流实例。1. 理解工作流引擎与 BPMN 2.0 标准在开始编码之前需要先厘清几个核心概念这决定了后续技术选型和实现方式。1.1 工作流引擎解决了什么问题工作流引擎的核心价值在于流程逻辑与业务逻辑的解耦。想象一个请假审批流程员工提交 - 直属经理审批 - 人事备案。如果不使用引擎你可能会在代码中写满if-else或switch-case来判断当前节点和下一个处理人。当流程步骤增加如加入财务审批、或审批规则变化如金额大于5000需总监审批时你不得不修改代码、重新测试、部署。工作流引擎将这个过程抽象为流程定义使用标准建模语言如 BPMN 2.0绘制流程图定义节点、连线、网关、变量等。流程实例根据定义启动一个具体的流程例如“张三的2023年国庆请假单”。任务流程到达某个用户任务节点时引擎会生成一个待办任务。流转用户完成任务后引擎根据流程图定义自动推动流程到下一个节点。这样业务开发者只需关注“提交请假单”、“审批通过”等业务动作而“下一步是谁”、“流程怎么走”则由引擎负责。流程变更时只需更新流程图定义文件通常无需改动代码。1.2 为什么选择 BPMN 2.0 和 bpmn-jsBPMN 2.0Business Process Model and Notation是一种国际标准的业务流程建模符号。它就像流程图的“通用语言”不同工具绘制的BPMN图可以被任何兼容的引擎解析执行。Activiti、Flowable、Camunda 等主流 Java 工作流引擎都原生支持 BPMN 2.0 XML 文件。bpmn-js是一个基于 JavaScript 的 BPMN 2.0 流程图查看与编辑工具库。它由 Camunda 团队开发维护功能强大社区活跃。将其集成到你的Web应用中可以为用户提供一个类似 Visio 的专业级流程设计器实现流程的可视化创建与修改。这对于需要动态调整流程的业务系统至关重要。1.3 Spring Boot 集成工作流引擎的典型架构一个完整的集成方案通常包含以下层次持久化层引擎需要数据库来存储流程定义、实例、任务、历史等数据。Spring Boot 通过配置数据源让引擎自动创建所需表结构。引擎服务层Spring Boot 通过自动配置或Bean方式将引擎的核心服务如RepositoryService,RuntimeService,TaskService注入到 Spring 容器中。业务应用层你的业务代码Controller, Service通过调用引擎服务层的API来启动流程、查询任务、完成任务等。流程设计器层前端页面集成 bpmn-js提供流程图绘制界面。绘制完成后将生成的 BPMN 2.0 XML 通过接口传递给后端由引擎服务进行部署。本文将首先完成后三层的基础搭建与联通。2. 环境准备与项目初始化我们选择Flowable 6.8.0作为工作流引擎因为它源于 Activiti社区活跃且与 Spring Boot 集成非常顺畅。当然如果你熟悉 Activiti 7整体思路和大部分 API 是相似的。2.1 技术栈与版本说明确保你的开发环境包含以下组件版本兼容性是成功集成的第一步。组件推荐版本说明JDK8 或 11Flowable 6.x 对 JDK 8 支持良好。Maven3.6用于依赖管理。Spring Boot2.7.x选择长期支持版本如 2.7.18。Flowable Spring Boot Starter6.8.0核心依赖提供自动配置。MySQL5.7 或 8.0生产级数据库也可先用 H2 内存数据库学习。IDEIntelliJ IDEA或其他你熟悉的 Java IDE。2.2 使用 Spring Initializr 创建项目通过 start.spring.io 或 IDEA 内置的 Spring Initializr 创建项目。Project: Maven ProjectLanguage: JavaSpring Boot: 2.7.18 (或其他 2.7.x 版本)Project Metadata:Group:com.exampleArtifact:flowable-demoPackaging: JarJava: 8 或 11Dependencies: 添加以下依赖Spring Web(用于提供 REST API)Spring Data JPA(可选用于业务数据操作)MySQL Driver(如果使用 MySQL)Lombok(简化代码可选)生成项目后打开pom.xml文件我们需要手动添加 Flowable 的依赖。2.3 配置 Maven 依赖与数据库在pom.xml的dependencies部分添加 Flowable 的 Spring Boot Starter。注意Flowable Starter 已经包含了其所需的所有模块如 flowable-engine, flowable-spring等无需单独引入。dependency groupIdorg.flowable/groupId artifactIdflowable-spring-boot-starter/artifactId version6.8.0/version /dependency !-- 如果使用MySQL -- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId scoperuntime/scope /dependency !-- 如果只是想快速体验可以使用H2内存数据库 -- !-- dependency groupIdcom.h2database/groupId artifactIdh2/artifactId scoperuntime/scope /dependency --接下来配置application.yml(或application.properties) 来连接数据库并调整 Flowable 的默认行为。Flowable Starter 会自动根据数据源配置在应用启动时检查并创建所需的数据库表约60张表。server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/flowable_db?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai username: root password: yourpassword driver-class-name: com.mysql.cj.jdbc.Driver jpa: hibernate: ddl-auto: update # 用于业务实体非Flowable表 show-sql: true # Flowable 特定配置 flowable: # 是否在启动时自动部署 classpath:/processes/ 下的bpmn文件 async-executor-activate: false # 学习阶段可先关闭异步执行器 database-schema-update: true # true: 启动时检查表结构缺失则创建。生产环境可设为 false db-history-used: true # 启用历史数据记录 history-level: audit # 历史级别: none, activity, audit, full check-process-definitions: true # 检查流程定义关键配置解释database-schema-update: 设置为true时应用启动时会自动执行ACT_开头的表结构创建或更新SQL。首次启动后生产环境建议改为false避免意外修改表结构。history-level: 定义历史数据记录级别。audit是常用级别会记录流程实例、任务、变量等基本信息满足大部分审计需求。async-executor-activate: Flowable 的异步执行器用于定时任务、异步调用等。学习阶段可以先关闭简化问题排查。启动你的 Spring Boot 应用。如果控制台没有报错并且能看到大量创建ACT_前缀表的 SQL 日志说明 Flowable 引擎初始化成功已与你的数据库连接。3. 核心引擎服务与第一个流程定义Flowable 通过一系列 Service API 提供全部功能。理解这些服务是操作引擎的关键。3.1 认识 Flowable 的七大核心服务Spring Boot 会自动将这些服务配置为 Bean你可以直接Autowired注入使用。服务类主要用途常用方法举例RepositoryService流程定义和部署的管理deploy(),createDeployment(),deleteDeployment()RuntimeService流程实例的启动与运行时管理startProcessInstanceByKey(),setVariable(),createProcessInstanceBuilder()TaskService用户任务待办事项的管理createTaskQuery(),complete(),claim(),setAssignee()HistoryService查询历史流程实例、任务、变量等createHistoricProcessInstanceQuery(),createHistoricTaskInstanceQuery()IdentityService用户和组的管理通常与业务系统集成saveUser(),saveGroup(),createMembership()ManagementService引擎管理和维护操作getTableCount(),executeCommand()FormService动态表单服务可选getStartFormData(),submitStartFormData()在接下来的示例中我们将主要使用前四个服务。3.2 创建并部署一个简单的 BPMN 2.0 流程首先我们需要一个流程定义文件。在src/main/resources下创建目录processes。Flowable 默认会扫描该目录下的.bpmn20.xml或.bpmn文件。在processes目录下创建一个文件simple-approval.bpmn20.xml。你可以用任何文本编辑器创建但内容需符合 BPMN 2.0 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 idsimpleApproval name简单审批流程 isExecutabletrue !-- 开始事件 -- startEvent idstartEvent name开始/ !-- 用户任务员工提交申请 -- userTask idsubmitRequest name提交申请 flowable:assignee${applicant} extensionElements flowable:formProperty idreason name请假事由 typestring requiredtrue/ flowable:formProperty iddays name请假天数 typelong requiredtrue/ /extensionElements /userTask !-- 排他网关根据条件决定流向 -- exclusiveGateway iddecisionGateway name审批决策/ !-- 用户任务经理审批 -- userTask idmanagerApprove name经理审批 flowable:assigneemanager/ !-- 服务任务自动执行的操作如发送通知 -- serviceTask idsendNotification name发送通知 flowable:classorg.flowable.engine.impl.bpmn.behavior.ShellActivityBehavior/ !-- 结束事件 -- endEvent idendEvent name结束/ !-- 顺序流连接各个元素 -- sequenceFlow idflow1 sourceRefstartEvent targetRefsubmitRequest/ sequenceFlow idflow2 sourceRefsubmitRequest targetRefdecisionGateway/ !-- 条件顺序流天数3直接经理审批 -- sequenceFlow idflowToManager sourceRefdecisionGateway targetRefmanagerApprove conditionExpression xsi:typetFormalExpression${days 3}/conditionExpression /sequenceFlow !-- 默认顺序流天数3需要发送额外通知 -- sequenceFlow idflowToNotify sourceRefdecisionGateway targetRefsendNotification/ sequenceFlow idflow3 sourceRefmanagerApprove targetRefendEvent/ sequenceFlow idflow4 sourceRefsendNotification targetRefendEvent/ /process /definitions流程解释流程开始后到达submitRequest用户任务。flowable:assignee${applicant}表示该任务的处理人由变量applicant动态指定。任务上有两个表单属性reason和days。任务完成后流程到达排他网关decisionGateway。网关根据days变量的值决定流向如果days 3流向managerApprove任务否则默认流向sendNotification服务任务。服务任务是一个自动节点这里用了一个Shell示例类实际项目中你会替换成自己的Java类。最后流程结束。启动 Spring Boot 应用你会在日志中看到类似Deploying process definition simple-approval的信息说明流程已自动部署成功。3.3 通过 Java 代码验证流程部署与启动创建一个简单的测试 Service 或 CommandLineRunner 来验证引擎是否工作。这里我们创建一个ProcessDemoService并注入核心服务。import lombok.extern.slf4j.Slf4j; import org.flowable.engine.RepositoryService; import org.flowable.engine.RuntimeService; import org.flowable.engine.TaskService; 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.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; import java.util.HashMap; import java.util.List; import java.util.Map; Service Slf4j public class ProcessDemoService { Autowired private RepositoryService repositoryService; Autowired private RuntimeService runtimeService; Autowired private TaskService taskService; PostConstruct // 应用启动后执行仅用于演示 public void initDemo() { try { // 1. 查询已部署的流程定义 ProcessDefinition processDefinition repositoryService.createProcessDefinitionQuery() .processDefinitionKey(simpleApproval) .latestVersion() .singleResult(); log.info(找到流程定义: {}, 版本: {}, processDefinition.getName(), processDefinition.getVersion()); // 2. 准备流程变量启动一个流程实例 MapString, Object variables new HashMap(); variables.put(applicant, zhangsan); // 设置提交人 variables.put(reason, 年假休息); variables.put(days, 2L); // 请假2天 ProcessInstance processInstance runtimeService.startProcessInstanceByKey(simpleApproval, variables); log.info(启动流程实例成功实例ID: {}, processInstance.getId()); // 3. 查询当前用户的待办任务 ListTask tasks taskService.createTaskQuery() .taskAssignee(zhangsan) // 查询zhangsan的任务 .processDefinitionKey(simpleApproval) .list(); log.info(用户 zhangsan 的待办任务数: {}, tasks.size()); for (Task task : tasks) { log.info(任务ID: {}, 任务名称: {}, task.getId(), task.getName()); // 4. 模拟完成任务 MapString, Object taskVariables new HashMap(); taskVariables.put(approvalComment, 同意); taskService.complete(task.getId(), taskVariables); log.info(完成任务: {}, task.getName()); } // 5. 任务完成后流程会流向下一个节点经理审批或发送通知 // 此时可以查询经理的待办任务 ListTask managerTasks taskService.createTaskQuery() .taskAssignee(manager) .list(); log.info(经理的待办任务数: {}, managerTasks.size()); } catch (Exception e) { log.error(流程演示执行失败, e); } } }启动应用观察控制台日志。你应该能看到流程定义被找到、流程实例被启动、任务被查询和完成的记录。这证明你的 Spring Boot 应用已经成功集成了 Flowable 引擎并且可以执行一个完整的 BPMN 流程。4. 构建 RESTful API 供前端调用为了让前端未来将集成 bpmn-js能与引擎交互我们需要封装一组 REST API。这里创建几个基础的控制器。4.1 流程定义与部署 API创建ProcessDefinitionController提供查询和部署流程的接口。import org.flowable.engine.RepositoryService; import org.flowable.engine.repository.Deployment; import org.flowable.engine.repository.ProcessDefinition; import org.flowable.engine.repository.ProcessDefinitionQuery; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.zip.ZipInputStream; RestController RequestMapping(/api/process-definition) public class ProcessDefinitionController { Autowired private RepositoryService repositoryService; // 查询流程定义列表 GetMapping(/list) public ListProcessDefinition list(RequestParam(value key, required false) String key) { ProcessDefinitionQuery query repositoryService.createProcessDefinitionQuery().latestVersion(); if (key ! null !key.isEmpty()) { query.processDefinitionKey(key); } return query.orderByProcessDefinitionVersion().desc().list(); } // 部署一个BPMN XML文件 PostMapping(/deploy) public MapString, Object deployByBpmnFile(RequestParam(file) MultipartFile file) throws IOException { if (file.isEmpty()) { throw new RuntimeException(请选择要部署的BPMN文件); } String fileName file.getOriginalFilename(); Deployment deployment; // 支持上传 .bpmn20.xml 文件 if (fileName ! null fileName.endsWith(.bpmn20.xml)) { deployment repositoryService.createDeployment() .addBytes(fileName, file.getBytes()) .name(fileName) .deploy(); } else { throw new RuntimeException(仅支持 .bpmn20.xml 格式文件); } MapString, Object result new HashMap(); result.put(deploymentId, deployment.getId()); result.put(deploymentName, deployment.getName()); result.put(deploymentTime, deployment.getDeploymentTime()); return result; } // 删除部署级联删除流程实例 DeleteMapping(/deployment/{deploymentId}) public String deleteDeployment(PathVariable String deploymentId) { repositoryService.deleteDeployment(deploymentId, true); // true表示级联删除实例和历史 return 删除部署成功: deploymentId; } }4.2 流程实例与任务 API创建ProcessInstanceController和TaskController处理流程的启动、查询和任务操作。// ProcessInstanceController.java RestController RequestMapping(/api/process-instance) public class ProcessInstanceController { Autowired private RuntimeService runtimeService; Autowired private HistoryService historyService; // 根据流程定义Key启动一个实例 PostMapping(/start/{processDefinitionKey}) public MapString, Object startInstance(PathVariable String processDefinitionKey, RequestBody MapString, Object variables) { ProcessInstance instance runtimeService.startProcessInstanceByKey(processDefinitionKey, variables); MapString, Object result new HashMap(); result.put(processInstanceId, instance.getId()); result.put(processDefinitionId, instance.getProcessDefinitionId()); result.put(businessKey, instance.getBusinessKey()); return result; } // 查询运行中的流程实例 GetMapping(/running) public ListProcessInstance getRunningInstances(RequestParam(value key, required false) String key) { if (key ! null) { return runtimeService.createProcessInstanceQuery().processDefinitionKey(key).list(); } return runtimeService.createProcessInstanceQuery().list(); } }// TaskController.java RestController RequestMapping(/api/task) public class TaskController { Autowired private TaskService taskService; // 查询用户待办任务 GetMapping(/todo) public ListTask getTodoTasks(RequestParam String assignee) { return taskService.createTaskQuery() .taskAssignee(assignee) .orderByTaskCreateTime().desc() .list(); } // 完成任务 PostMapping(/complete/{taskId}) public String completeTask(PathVariable String taskId, RequestBody(required false) MapString, Object variables) { taskService.complete(taskId, variables); return 任务完成: taskId; } // 签收任务将组任务或未分配任务分配给具体用户 PostMapping(/claim/{taskId}) public String claimTask(PathVariable String taskId, RequestParam String userId) { taskService.claim(taskId, userId); return 任务签收成功; } }现在你可以使用 Postman 或 curl 测试这些 APIGET /api/process-definition/list查看已部署的流程。POST /api/process-instance/start/simpleApproval携带 JSON 变量{applicant:lisi, days:5}启动一个新流程。GET /api/task/todo?assigneemanager查看经理的待办任务。POST /api/task/complete/{taskId}完成任务。5. 常见问题与排查路径集成过程中你可能会遇到以下典型问题。这里提供排查思路。5.1 数据库连接与表创建失败现象应用启动失败报错Table ACT_GE_PROPERTY doesnt exist或数据源连接错误。排查检查数据库连接确认application.yml中的url,username,password正确数据库服务已启动。检查权限确保数据库用户有创建表的权限。检查配置确认flowable.database-schema-update设置为true首次启动。查看日志搜索Creating tables for Flowable相关日志看是否执行了建表语句。如果没有可能是依赖冲突或配置未生效。5.2 流程定义文件部署失败现象控制台没有Deploying process definition日志或者通过 API 部署时报 XML 解析错误。排查文件位置确保.bpmn20.xml文件放在src/main/resources/processes/目录下。文件格式用文本编辑器打开 XML 文件检查根标签definitions和process的isExecutable属性是否为true。非可执行的流程不会被部署。XML 语法检查是否有未闭合的标签、错误的命名空间引用。可以尝试在在线 BPMN 验证工具中检查。手动部署通过上面编写的POST /api/process-definition/deploy接口上传文件观察更详细的错误信息。5.3 启动流程实例时找不到定义现象调用runtimeService.startProcessInstanceByKey(myProcess)时抛出异常no processes deployed with key myProcess。排查确认 Key检查startProcessInstanceByKey方法传入的 key 是否与 BPMN 文件中process id...的属性完全一致大小写敏感。确认版本流程定义可能有多个版本。startProcessInstanceByKey默认使用最新版本。如果你禁用了旧版本请确保有至少一个版本是激活状态。查询验证先通过repositoryService.createProcessDefinitionQuery().processDefinitionKey(myProcess).list()查询确认是否存在。5.4 任务查询不到或分配人不正确现象通过taskService.createTaskQuery().taskAssignee(zhangsan).list()查询不到预期任务。排查检查 Assignee确认任务节点的flowable:assignee属性值。如果是表达式${applicant}则在启动流程实例时必须传入名为applicant的变量。检查流程状态流程可能已经结束或者卡在网关、服务任务等非用户任务节点。使用runtimeService.createExecutionQuery()查看当前流程实例的执行流在哪里。检查历史通过historyService.createHistoricTaskInstanceQuery()查询该任务是否已经完成。变量类型注意流程变量类型。例如BPMN 中定义的days是long类型在 Java 代码中传入Integer可能导致表达式评估问题。5.5 流程变量设置与获取问题现象在流程中设置的变量在后续节点中获取不到或值为空。排查变量作用域流程变量有作用域概念流程实例、执行流、任务。通常通过runtimeService.setVariable()设置的是流程实例级变量在整个实例中可用。taskService.setVariableLocal()设置的是任务局部变量。序列化存储复杂对象如自定义实体作为变量时该对象必须实现Serializable接口。建议存储基本类型、String、Map 等。表达式语法在 BPMN XML 的条件表达式${days 3}中变量days必须在当前作用域内存在且类型可比较。6. 生产环境配置与最佳实践建议将工作流引擎用于学习演示和用于生产环境关注点有很大不同。以下是一些关键实践建议。6.1 数据库与连接池配置专用数据库为工作流引擎使用独立的数据库或 schema避免与业务表混在一起便于管理和备份。连接池调优Flowable 引擎在执行过程中会频繁访问数据库。务必配置合适的数据库连接池如 HikariCP并设置合理的maximumPoolSize、connectionTimeout等参数。关闭自动建表首次启动后将flowable.database-schema-update设置为false。表结构的变更应通过数据库迁移工具如 Flyway, Liquibase管理。spring: datasource: hikari: maximum-pool-size: 20 connection-timeout: 30000 idle-timeout: 600000 max-lifetime: 1800000 flowable: database-schema-update: false # 生产环境务必关闭6.2 历史数据与性能权衡历史级别选择flowable.history-level默认为audit记录核心数据。如果对审计要求极高可设为full但这会产生大量历史数据影响性能。如果只关心当前状态可设为activity。历史数据清理Flowable 提供了HistoryService的 API 来删除历史数据。建议编写定时任务定期清理超过一定时间的已完成流程实例历史数据避免表无限膨胀。6.3 异步执行器与事务管理启用异步执行器在生产环境中应将flowable.async-executor-activate设为true。这会将一些耗时操作如定时器事件、异步调用放入后台线程池执行避免阻塞主流程线程。理解事务边界Flowable 的TaskService.complete()、RuntimeService.startProcessInstanceByKey()等方法通常在一个数据库事务中执行。如果你的业务逻辑在“任务完成监听器”中且非常耗时需要考虑将其异步化避免长事务拖垮数据库。6.4 流程设计规范流程 Key 命名使用有业务意义的、稳定的 Key如leave_approval不要使用版本号或易变信息。简化流程模型避免创建过于复杂、嵌套过深的流程图。复杂的网关逻辑尽量用清晰的命名和注释说明。变量设计规划好流程变量的名称和类型形成文档。避免滥用变量特别是大对象。服务任务实现服务任务中引用的 Java 类Delegate应设计为无状态的、可重入的并做好异常处理。异常应转换为 BPMN 错误事件由流程模型处理。至此你已经完成了 Spring Boot 集成工作流引擎的后端部分。我们搭建了基础环境部署并执行了一个简单的 BPMN 流程提供了供前端调用的 REST API并讨论了常见问题和生产实践。在下篇中我们将聚焦于前端详细介绍如何集成 bpmn-js 流程编辑器实现流程图的在线绘制、预览、部署并构建一个完整的流程管理前端界面最终形成一个前后端分离的可视化工作流应用。