Spring Boot集成bpmn-js与Activiti工作流引擎实战指南

📅 2026/7/22 1:51:36
Spring Boot集成bpmn-js与Activiti工作流引擎实战指南
1. 先搞清楚集成 bpmn-js 要解决什么实际问题如果你正在用 Spring Boot 开发一个需要流程审批、工单流转或者自动化任务编排的系统那你大概率绕不开工作流引擎。Activiti、Flowable、Camunda 这些主流引擎帮你解决了流程定义、任务执行、历史追踪这些后端核心逻辑但用户怎么画流程图呢总不能让他们手写 XML 吧。这就是 bpmn-js 这类流程设计器要解决的问题给用户一个直观、可拖拽的 Web 界面来绘制符合 BPMN 2.0 标准的流程图并让 Spring Boot 后端能无缝接收和解析这些图。很多人一上来就找各种开源设计器但容易忽略一个关键点设计器生成的模型通常是 XML 或 JSON必须能被你选用的工作流引擎如 Activiti 7正确部署和执行。如果模型不兼容前端画得再漂亮也是白搭。bpmn-js 之所以是 Spring Boot 集成工作流时的首选前端组件就是因为它严格遵循 BPMN 2.0 规范与 Activiti、Flowable、Camunda 等引擎“说同一种语言”集成成本最低。所以这篇文章的下半部分我们不谈概念直接上手。我会带你走通从 bpmn-js 编辑器获取流程图数据到 Spring Boot 后端接收、部署、并启动一个流程实例的完整闭环。你会看到如何从前端拿到 XML如何通过接口传给后端后端又如何处理。这个过程里路径配置、依赖冲突、模型校验、任务查询这些最容易卡住新手的坑我都会一一拆解。2. 环境与项目准备别在依赖和配置上栽跟头在开始写代码之前先把环境理清楚。很多集成失败的问题源头都在这里。2.1 后端 Spring Boot 项目基础框架假设你已经有一个基础的 Spring Boot 项目。这里的关键是工作流引擎的选择和版本。以目前较新的 Activiti 7 为例你的pom.xml里至少需要这些核心依赖dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.activiti/groupId artifactIdactiviti-spring-boot-starter/artifactId version7.1.0.M6/version !-- 注意版本建议与Spring Boot版本匹配 -- /dependency dependency groupIdcom.h2database/groupId artifactIdh2/artifactId scoperuntime/scope !-- 用于本地测试生产环境换MySQL/PostgreSQL -- /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency版本注意点Activiti 7.x 与 Spring Boot 2.x 和 3.x 的兼容性不同。如果你用 Spring Boot 2.7.x可以用7.1.0.M6如果用 Spring Boot 3.x需要找更高版本或社区适配版。最稳妥的方式是去官方仓库查看 release note。数据库方面本地开发用 H2 内存数据库最快避免一开始就陷入数据库连接问题。在application.yml中配置spring: datasource: url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY-1 driver-class-name: org.h2.Driver username: sa password: h2: console: enabled: true # 开启H2控制台方便查看自动生成的表 activiti: database-schema-update: true # 自动创建/更新表结构 db-history-used: true # 使用历史表 history-level: audit # 历史记录级别配置database-schema-update: true后项目启动时 Activiti 会自动在数据库中创建所需的几十张表。第一次启动后访问http://localhost:8080/h2-console用上面的配置登录就能看到ACT_RE_*资源表、ACT_RU_*运行时表、ACT_HI_*历史表等这证明引擎初始化成功了。2.2 前端 bpmn-js 集成准备前端部分你可以新建一个 Vue 或 React 项目也可以直接在现有 Spring Boot 项目的resources/static目录下写静态页面。为了演示简洁我们采用后者用一个纯 HTMLJS 的页面来集成 bpmn-js。首先在resources/static下创建editor.html。你需要通过 CDN 或本地引入 bpmn-js 的库。这里用 CDN 方式避免构建工具带来的复杂度!DOCTYPE html html langzh-CN head meta charsetUTF-8 title流程设计器/title link relstylesheet hrefhttps://unpkg.com/bpmn-js14.0.0/dist/assets/diagram-js.css / link relstylesheet hrefhttps://unpkg.com/bpmn-js14.0.0/dist/assets/bpmn-font/css/bpmn.css / style #canvas { height: 600px; border: 1px solid #ccc; } .controls { margin: 10px 0; } /style /head body div classcontrols button onclicksaveDiagram()保存流程图/button button onclickdeployDiagram()部署到后端/button /div div idcanvas/div script srchttps://unpkg.com/bpmn-js14.0.0/dist/bpmn-modeler.development.js/script script // 初始化 bpmn-js 建模器 const bpmnModeler new BpmnJS({ container: #canvas }); // 创建一个空的流程图 async function createNewDiagram() { try { const result await bpmnModeler.createDiagram(); console.log(Diagram created); } catch (err) { console.error(Could not create diagram, err); } } // 保存当前图为 XML async function saveDiagram() { try { const { xml } await bpmnModeler.saveXML({ format: true }); console.log(BPMN XML:, xml); // 这里可以弹窗显示或允许下载 alert(XML已生成请查看控制台 (F12)); } catch (err) { console.error(Could not save XML, err); } } // 部署到 Spring Boot 后端 async function deployDiagram() { try { const { xml } await bpmnModeler.saveXML({ format: true }); const response await fetch(/api/process/deploy, { method: POST, headers: { Content-Type: application/json, }, body: JSON.stringify({ bpmnXml: xml, processName: MyTestProcess }) }); const result await response.json(); alert(部署结果: ${result.success ? 成功 : 失败}, 流程定义ID: ${result.processDefinitionId}); } catch (err) { console.error(部署失败, err); alert(部署失败请检查后端接口和网络); } } // 页面加载后初始化一个空图 window.onload createNewDiagram; /script /body /html这个页面做了几件事引入了 bpmn-js 的 CSS 和 JS。初始化了一个建模器BpmnJS它提供了完整的绘图界面。提供了两个按钮一个将当前图保存为格式化的 BPMN 2.0 XML在控制台查看另一个将这个 XML 通过fetchAPI 发送到我们即将创建的 Spring Boot 后端接口。现在启动你的 Spring Boot 应用访问http://localhost:8080/editor.html你应该能看到一个空的流程图绘制界面。可以尝试拖拽左侧的“任务”、“网关”、“事件”等到画布上连上连线。点击“保存流程图”在浏览器控制台F12里就能看到生成的 XML。这是集成的第一步也是验证前端环境是否正常的关键。3. 后端接口开发接收、部署与启动流程前端能生成 XML 了现在需要 Spring Boot 后端提供一个接口来接收它并交给 Activiti 引擎处理。3.1 创建流程部署控制器在 Spring Boot 项目中创建一个ProcessControllerpackage com.example.workflow.controller; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.activiti.api.process.model.ProcessDefinition; import org.activiti.api.process.runtime.ProcessRuntime; import org.activiti.engine.RepositoryService; import org.activiti.engine.repository.Deployment; import org.activiti.engine.repository.ProcessDefinitionQuery; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import java.util.HashMap; import java.util.Map; Slf4j RestController RequestMapping(/api/process) RequiredArgsConstructor public class ProcessController { // 仓库服务用于部署流程定义 private final RepositoryService repositoryService; // 流程运行时服务用于启动流程实例Activiti 7 推荐方式 private final ProcessRuntime processRuntime; PostMapping(/deploy) public ResponseEntityMapString, Object deployProcess(RequestBody MapString, String request) { MapString, Object response new HashMap(); try { String bpmnXml request.get(bpmnXml); String processName request.get(processName); if (bpmnXml null || bpmnXml.trim().isEmpty()) { response.put(success, false); response.put(message, BPMN XML 内容为空); return ResponseEntity.badRequest().body(response); } // 关键步骤部署 BPMN XML Deployment deployment repositoryService.createDeployment() .name(processName _Deployment) .addString(processName .bpmn20.xml, bpmnXml) // 资源名称必须以.bpmn20.xml结尾 .deploy(); log.info(流程部署成功部署ID: {}, 部署名称: {}, deployment.getId(), deployment.getName()); // 查询部署后生成的流程定义 ProcessDefinitionQuery query repositoryService.createProcessDefinitionQuery() .deploymentId(deployment.getId()); org.activiti.engine.repository.ProcessDefinition processDefinition query.singleResult(); response.put(success, true); response.put(message, 流程部署成功); response.put(deploymentId, deployment.getId()); response.put(processDefinitionId, processDefinition.getId()); response.put(processDefinitionKey, processDefinition.getKey()); return ResponseEntity.ok(response); } catch (Exception e) { log.error(流程部署失败, e); response.put(success, false); response.put(message, 流程部署失败: e.getMessage()); return ResponseEntity.internalServerError().body(response); } } }这个deploy接口是集成的核心。它接收前端传来的 BPMN XML 字符串和一个流程名称。注意addString方法它直接将 XML 字符串作为部署资源。资源名称的后缀必须是.bpmn20.xml或.bpmn这是 Activiti 引擎识别 BPMN 2.0 文件的约定如果写错引擎不会将其识别为流程定义文件。部署成功后通过RepositoryService查询到刚部署的流程定义将其 ID 和 Key 返回给前端。这个processDefinitionId和processDefinitionKey是后续启动流程实例、查询任务的关键。3.2 启动流程实例并查询任务部署只是把流程“图纸”存到了数据库ACT_RE_PROCDEF表。要让流程真正跑起来需要根据这个“图纸”启动一个流程实例。在ProcessController中增加启动和查询任务的接口PostMapping(/start) public ResponseEntityMapString, Object startProcessInstance(RequestBody MapString, Object request) { MapString, Object response new HashMap(); try { String processDefinitionKey (String) request.get(processDefinitionKey); // 可以传递业务变量例如发起人、表单数据等 MapString, Object variables (MapString, Object) request.get(variables); // 使用 ProcessRuntime (Activiti 7 风格) 启动流程实例 org.activiti.api.process.model.ProcessInstance processInstance processRuntime.start(ProcessPayloadBuilder .start() .withProcessDefinitionKey(processDefinitionKey) .withVariables(variables ! null ? variables : new HashMap()) .withName(流程实例_ System.currentTimeMillis()) .build()); log.info(流程实例启动成功实例ID: {}, processInstance.getId()); response.put(success, true); response.put(message, 流程实例启动成功); response.put(processInstanceId, processInstance.getId()); response.put(processDefinitionId, processInstance.getProcessDefinitionId()); return ResponseEntity.ok(response); } catch (Exception e) { log.error(启动流程实例失败, e); response.put(success, false); response.put(message, 启动流程实例失败: e.getMessage()); return ResponseEntity.internalServerError().body(response); } } GetMapping(/tasks/{assignee}) public ResponseEntityMapString, Object getTasksByAssignee(PathVariable String assignee) { MapString, Object response new HashMap(); try { // 使用 TaskRuntime (Activiti 7 风格) 查询任务 ListTask tasks taskRuntime.tasks(Pageable.of(0, 100), TaskPayloadBuilder .tasks() .withAssignee(assignee) .build()) .getContent(); ListMapString, String taskList tasks.stream().map(task - { MapString, String taskInfo new HashMap(); taskInfo.put(taskId, task.getId()); taskInfo.put(taskName, task.getName()); taskInfo.put(processInstanceId, task.getProcessInstanceId()); return taskInfo; }).collect(Collectors.toList()); response.put(success, true); response.put(tasks, taskList); return ResponseEntity.ok(response); } catch (Exception e) { log.error(查询任务失败, e); response.put(success, false); response.put(message, 查询任务失败: e.getMessage()); return ResponseEntity.internalServerError().body(response); } }这里引入了ProcessRuntime和TaskRuntime这是 Activiti 7 更推荐的与 Spring Security 集成更友好的 API。启动流程时你需要传入之前部署返回的processDefinitionKey。查询任务时传入任务处理人assignee。为了让这些接口生效你需要在启动类或配置类上确保 Activiti 的 API 自动配置生效并且可能需要一个简单的安全配置因为 Activiti 7 的 Runtime API 默认需要安全上下文。对于测试可以创建一个最简单的安全配置来放行所有请求package com.example.workflow.config; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .anyRequest().permitAll() // 测试期间允许所有请求生产环境必须修改 .and() .csrf().disable(); // 禁用CSRF以便测试POST请求 } }重要提醒这个安全配置仅用于本地开发和测试绝对不要用于生产环境。生产环境必须配置严格的权限控制和用户体系。4. 联调测试与常见问题排查环境、前端、后端都准备好了现在把它们串起来完成一次从画图到流程运行的完整测试。4.1 端到端测试步骤启动后端确保 Spring Boot 应用成功启动无报错H2 控制台可访问。访问设计器打开浏览器访问http://localhost:8080/editor.html。绘制简单流程在画布上拖拽一个“开始事件”Start Event连接一个“用户任务”User Task再连接一个“结束事件”End Event。在“用户任务”上双击将其名称改为“提交审批”在右侧属性面板如果没有需要引入 properties-panel 模块找到“Assignee”办理人填写zhangsan。部署流程点击页面上的“部署到后端”按钮。观察浏览器网络请求F12 - Network应该看到一个 POST 请求发送到/api/process/deploy并且返回成功的 JSON其中包含processDefinitionKey通常是流程图中“开始事件”的 ID如Process_1。启动流程实例我们可以用 Postman 或另一个简单的 HTML 页面来调用启动接口。这里用命令行curl模拟curl -X POST http://localhost:8080/api/process/start \ -H Content-Type: application/json \ -d { processDefinitionKey: Process_1, variables: {applicant: 李四, amount: 10000} }成功后会返回processInstanceId。查询任务查询分配给zhangsan的任务curl http://localhost:8080/api/process/tasks/zhangsan你应该能看到一个任务列表包含刚才创建的“提交审批”任务。验证数据库访问http://localhost:8080/h2-console查看ACT_RU_TASK运行时任务表和ACT_RU_EXECUTION运行时执行流表应该能看到对应的记录。4.2 集成过程中最容易遇到的五个坑前端 XML 保存格式错误bpmnModeler.saveXML()返回的 XML 字符串是标准的 BPMN 2.0。但如果你的流程图里包含了自定义属性或非标准元素在部署时 Activiti 可能解析失败。排查时先把前端生成的 XML 保存到一个.bpmn20.xml文件里然后用 Activiti 提供的BPMN20.xsd模式文件或在线校验工具校验一下 XML 的合法性。后端部署时资源名后缀缺失这是最常被忽略的一点。在repositoryService.createDeployment().addString(...)时资源名第二个参数必须以.bpmn20.xml或.bpmn结尾。如果写成了myProcess.xml或没写后缀Activiti 不会把它当作流程定义文件部署看似成功但查询不到流程定义。Activiti 版本与 Spring Boot 版本冲突表现为启动时出现ClassNotFoundException、MethodNotFoundException或 Bean 创建失败。务必检查 Maven 中央仓库或 Activiti 官方 GitHub找到与你 Spring Boot 主版本匹配的 Activiti Starter 版本。对于 Spring Boot 2.7.x7.1.0.M6相对稳定Spring Boot 3.x 需要寻找更高版本或社区维护的适配版本。流程启动失败提示“未找到流程定义”可能的原因有部署失败但未报错检查部署日志。启动时使用的processDefinitionKey不对。这个 Key 是流程图中“开始事件”所属流程的 ID默认可能是Process_1但如果你在 bpmn-js 中修改了流程 ID就需要用修改后的值。部署接口返回的processDefinitionKey是最准确的。流程定义被挂起。可以检查ACT_RE_PROCDEF表的SUSPENSION_STATE_字段1表示激活2表示挂起。使用ProcessRuntime/TaskRuntime时出现安全上下文Security Context错误Activiti 7 的这些新 API 默认会尝试从 Spring Security 上下文中获取当前用户。如果你没做登录认证就会报错。测试时可以像前面一样配置一个全放行的安全配置。但在正式开发中你需要集成你的用户系统如从数据库或 LDAP 加载用户并确保在调用这些 API 前当前线程的安全上下文中存在有效的 Authentication 对象。5. 进阶自定义、扩展与生产化考量当基础集成跑通后你会面临更实际的需求样式定制、添加自定义属性、处理复杂网关、接入业务表单、以及为生产环境做准备。5.1 前端 bpmn-js 的定制与扩展默认的 bpmn-js 建模器可能不符合你的 UI 风格或者你需要隐藏一些复杂元素如复杂事件、补偿。你可以通过配置additionalModules来引入或排除模块。例如创建一个自定义模块来修改颜色和样式// 自定义样式模块 const customStylesModule { __init__: [customStyles], customStyles: [type, function(customStyles) { // 这里可以覆盖默认的样式配置 }] }; // 初始化时注入 const bpmnModeler new BpmnJS({ container: #canvas, additionalModules: [ customStylesModule // 可以排除默认模块例如禁用某些调色板 // { paletteProvider: [value, null] } // 这会隐藏左侧调色板 ] });更常见的需求是添加自定义属性。比如在用户任务上增加一个“审批角色”字段。这需要同时修改前端建模器使用bpmn-js-properties-panel扩展和后端 Java 实体使用 Activiti 的extensionElements。这是一个相对进阶的话题核心思路是前端定义属性面板的配置。将自定义属性写入 BPMN XML 的extensionElements中。后端在流程运行时通过TaskService.getVariable()或解析extensionElements来获取这些属性。5.2 后端流程的监听与业务集成工作流引擎的价值在于驱动业务。当流程到达“提交审批”任务时你需要通知实际的审批人张三。这可以通过事件监听器来实现。创建一个任务创建监听器package com.example.workflow.listener; import lombok.extern.slf4j.Slf4j; import org.activiti.engine.delegate.DelegateTask; import org.activiti.engine.delegate.TaskListener; import org.springframework.stereotype.Component; Slf4j Component public class MyTaskCreatedListener implements TaskListener { Override public void notify(DelegateTask delegateTask) { String taskName delegateTask.getName(); String assignee delegateTask.getAssignee(); String processInstanceId delegateTask.getProcessInstanceId(); log.info(任务已创建: [{}], 办理人: [{}], 流程实例ID: [{}], taskName, assignee, processInstanceId); // 在这里集成你的业务通知系统 // 例如发送邮件、微信消息、系统通知给 assignee // notificationService.sendTo(assignee, 您有一个待办任务: taskName); } }然后在流程定义中bpmn-js 里给用户任务添加这个监听器。在用户任务的属性面板中找到“监听器”Listeners配置添加一个“执行监听器”Execution Listener或“任务监听器”Task Listener指定事件类型如 create和实现类com.example.workflow.listener.MyTaskCreatedListener。这样每当任务创建时你的业务代码就会被触发。5.3 生产环境部署要点更换数据库将 H2 内存数据库换成 MySQL、PostgreSQL 或 Oracle。修改application.yml中的数据源配置并手动执行 Activiti 提供的数据库脚本位于引擎jar包的org/activiti/db/create目录下来初始化表结构或者继续使用database-schema-update: true让引擎自动管理对于中小项目可行但严格规范的项目建议手动初始化。配置连接池使用 HikariCP 等高性能连接池。异步执行器对于耗时任务使用 Activiti 的异步执行器Async Executor避免阻塞流程引擎线程。历史数据清理ACT_HI_*历史表会随着时间增长。需要制定归档或清理策略可以通过配置activiti.history-level如设置为audit或none来减少历史数据或编写定时任务清理。API 安全移除全放行的安全配置集成你的用户认证与授权体系确保/api/process/start、/api/process/tasks/{assignee}等接口只能被合法用户访问。前端构建将包含 bpmn-js 的前端页面如 Vue/React 项目独立构建部署到 Nginx 或与 Spring Boot 打包在一起作为静态资源。确保路由配置正确避免刷新页面 404。6. 总结从集成到上线的关键检查清单走完整个流程后你会发现 Spring Boot 集成 bpmn-js 和工作流引擎技术难点并不复杂关键在于对细节的把握和流程的闭环。这里给你一个简单的检查清单在你自己项目落地时可以对照[ ]前端bpmn-js 建模器能正常加载和绘图saveXML能生成格式正确的 BPMN 2.0 XML。[ ]通信前端能通过 AJAXfetch/axios将 XML 和必要参数如流程名称成功发送到后端/deploy接口。[ ]后端部署RepositoryService.deploy()成功无异常能在ACT_RE_PROCDEF表中查到新的流程定义且返回正确的processDefinitionId和Key。[ ]后端启动使用ProcessRuntime或RuntimeService能通过processDefinitionKey成功启动流程实例在ACT_RU_EXECUTION和ACT_RU_TASK表中生成记录。[ ]任务查询能根据办理人assignee查询到对应的待办任务。[ ]业务集成关键节点如任务创建、完成的事件监听器配置正确能触发你的业务通知逻辑。[ ]生产配置数据库已切换为生产库连接池配置妥当API 安全防护已启用历史数据管理策略已确定。我个人更建议在前期验证时不要一上来就追求复杂流程和完美界面。先用最少的元素一个开始事件、一个用户任务、一个结束事件把“画图 - 部署 - 启动 - 查询任务”这个核心链路跑通。链路通了再去叠加自定义属性、复杂网关、子流程、监听器、表单绑定这些进阶功能每一步都做好验证。这样排查问题时范围更小更容易定位。