Spring Boot 3.x集成Flowable 7.x:从零跑通审批流闭环 在公司里做系统凡是涉及到审批、流转、多角色协作的业务最后基本都会走到同一个岔路口自己写状态机还是上工作流引擎。自己写简单业务还行但一旦出现会签、驳回、条件分支、委托转办状态机的代码复杂度会直接失控改一处崩三处。用引擎又担心太重、学习成本高、二次开发困难。这个系列我打算用几篇文章从一个真实的Spring Boot项目出发把Flowable 7.x的集成、设计、部署、发起、完成、网关、会签、驳回、监听器这些环节一个个拆开讲清楚。今天这第一篇先把最基础也最关键的路趟平Spring Boot 3.x集成Flowable 7.x跑通一个包含申请-审批-结束的最小闭环流程。Flowable 7.x是一个从Activiti 5.x分支演进出来的开源工作流引擎BPMN 2.0标准是它的核心表达方式。7.x版本在架构上做了不少调整底层的命令拦截器、事务管理、变量处理都重构过和Spring Boot 3.x的兼容性比老版本好得多。在正式动手之前我先说清楚这篇博文要解决的问题完成Spring Boot 3.x与Flowable 7.x的环境集成、通过IDEA插件绘制一个最简单的BPMN流程、把流程定义部署到引擎、发起一个流程实例、完成用户任务让流程走到结束。这套流程跑通之后后面的网关、会签、驳回、历史查询等高级功能才有基础。如果你是第一次接触Flowable或者之前用过Activiti、现在想迁移到Flowable 7.x这篇文章都适用。项目源码我放在文末按步骤操作即可复现。1. 集成前的版本选型与依赖配置1.1 版本矩阵到底怎么选很多人在Flowable和Spring Boot集成的第一步就栽跟头根因是版本不匹配。Spring Boot 3.x全面拥抱Jakarta EE规范javax包被替换成jakarta包而Flowable从6.4.0版本开始才陆续支持Jakarta命名空间到7.x版本才彻底完成迁移。如果你的项目还在用javax硬上Flowable 7.x会直接编译报错反过来如果你的项目是javax的Spring Boot 2.x千万别强行升级Flowable 7.x老老实实用6.x版本更稳妥。我在实际项目里推荐下面的版本组合这个组合经过生产环境验证稳定性没问题JDK 17Flowable 7.x要求Java 17及以上Spring Boot 3.2.x3.0.x、3.1.x也可以但3.2.x的依赖管理更成熟Flowable 7.0.x以7.0.0为基础版后续补丁版按需升级MySQL 8.xFlowable支持的数据库很多MySQL 8是中小团队最主流的选择有些资料推荐Spring Boot 3.0.0配Flowable 7.0.0但我建议Spring Boot直接用3.2.x。原因很简单Spring Boot 3.0.0有一些早期版本特有的兼容问题比如和MyBatis、Druid等常用组件的整合坑比较多3.2.x在这些方面经历了更多社区验证。Flowable 7.0.0是7.x系列的稳定基线配套的flowable-spring-boot-starter-process依赖能自动适配Spring Boot 3.x。1.2 依赖引入的正确姿势Flowable的Maven依赖有几个不同的starter对应不同的功能很多新手一股脑全部引入导致依赖臃肿、启动缓慢。对于最小闭环流程只需要引入process相关的starter工作流引擎的核心能力都在这里面。dependency groupIdorg.flowable/groupId artifactIdflowable-spring-boot-starter-process/artifactId version7.0.0/version /dependency这个starter会自动传递引入flowable-spring-boot-autoconfigure、flowable-engine、flowable-spring等核心模块。如果你想用Flowable自带的历史数据管理功能比如流程实例的完整生命周期追踪可以加上flowable-spring-boot-starter-rest的REST API支持但最小项目不需要。引入依赖后我再提醒一个高频坑Flowable 7.x底层依赖的Jackson版本在配合Spring Boot 3.2.x时有一个需要关注的细节。Spring Boot 3.2.x默认引入Jackson 2.15.x而Flowable 7.0.0运行时用的是Jackson 2.12.x这两个版本在反序列化配置上有差异。如果你在流程变量里放复杂对象启动时会遇到jackson-module-parameter-names相关的报错。解决方式是在配置文件里显式指定Jackson的ObjectMapper或者给流程变量使用的POJO添加默认构造函数和完整getter/setter避免复杂对象的自定义反序列化。1.3 数据库连接池与事务配置Flowable引擎的每一次操作部署、发起、完成任务都在一个事务里执行对数据库连接池的要求比较高。我自己在配置Druid或HikariCP时会注意两个参数initial-size和max-active。Flowable在发起流程和完成任务的瞬时操作中会同时操作ACT_RU_、ACT_HI_、ACT_GE_*等多张表如果数据库连接数不够高并发场景下很容易报Connection is not available, request timed out。在Spring Boot配置文件里我用的是这样的最小配置spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/flowable_demo?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: yourpassword application: name: flowable-demo flowable: database-schema-update: true async-executor-activate: false配置里的flowable.database-schema-update: true是自动建表开关。第一次启动时Flowable会在你指定的数据库里自动创建ACT_*开头的数据表一共几十张。async-executor-activate: false是关闭异步执行器最小项目不需要异步任务关掉可以避免多余的后台线程干扰调试。2. 基础工程搭建与启动验证2.1 最小可运行项目的目录结构这里我分享一个实际项目的目录结构虽然不是强制规定但按照这个结构组织后续维护会顺很多src/main/java/com/example/flowabledemo/ ├── FlowableDemoApplication.java ├── controller/ │ └── ProcessController.java ├── service/ │ ├── AskLeaveService.java │ └── impl/ │ └── AskLeaveServiceImpl.java └── resources/ ├── application.yml └── processes/ ├── ask-leave.bpmn20.xml └── ask-leave.pngprocesses目录是Flowable自动扫描流程定义文件的位置。Flowable的自动部署机制会在应用启动时扫描classpath下的processes目录将里面的.bpmn20.xml或.bpmn文件解析并自动部署到引擎。这个机制非常方便但也容易在开发阶段因为文件改动被自动部署多次产生多个版本的流程定义。生产环境建议改成手动部署这一点后面细说。2.2 数据库表结构初识启动应用后登录MySQL看一眼会发现多了几十张以ACT_开头的表。按照功能分区大致可以分四类ACT_GE_*通用数据表存储流程定义和资源文件比如ACT_GE_BYTEARRAY存的是BPMN XML的二进制内容。ACT_RE_*流程定义存储表比如ACT_RE_PROCDEF存储流程定义的元数据每个部署的流程在这里都有一行记录。ACT_RU_*运行时数据表比如ACT_RU_TASK存的是当前待办任务流程结束之后这些记录会被清理。ACT_HI_*历史数据表比如ACT_HI_PROCINST存储所有流程实例的历史轨迹即便实例结束也不会删除。第一次启动出表后我的建议是花十来分钟逐张表看一看不用记表结构只要建立运行时表和归档表分离这个概念。你在开发中遇到待办消失了流程记录找不到了这类问题基本就是RUN表和HI表的区别没搞清楚。2.3 启动时的报错排查第一次启动Flowable的时候十有八九会遇到报错。我把自己见过的高频问题整理一下第一个问题是MySQL版本匹配错误。Flowable 7.0.0与MySQL 5.7的兼容性测试没有MySQL 8.0充分如果连接MySQL 5.7可能遇到Unknown character set index for field 255 received from server这类字符集相关错误。建议数据库版本统一用MySQL 8.0或者升级MySQL驱动到mysql-connector-j8.0.33以上版本。第二个问题是数据库驱动冲突。很多老项目会手动引入mysql-connector-java而Spring Boot 3.x的依赖管理默认引入的是com.mysql:mysql-connector-j。两个坐标对应同一个驱动但包名不一致容易引发冲突。建议把项目里老的MySQL驱动坐标统一改成新坐标删除手动版本号。第三个问题是Flyway版本冲突。Flowable内部依赖自己的数据库升级工具如果你在项目里同时集成了Flyway并开启了自动迁移有可能在启动时出现Flowable的schema版本与Flyway的schema版本互相干扰的报错。解决方案是二选一要么Flyway接管数据库版本管理关闭Flowable的database-schema-update要么让Flowable自己管理Flyway只管理业务表。这两种方式我都用过中小项目建议直接用Flowable的database-schema-update自动建表省心。3. 用IDEA插件绘制第一个BPMN流程3.1 流程设计工具的选择画BPMN有两种主流方式。一种是直接用IDEA插件比如Flowable BPMN visualizer、Activiti BPMN visualizer特点是轻量、维护方便、代码库集成好另一种是用Flowable官方提供的Flowable Designer基于Eclipse或Flowable ModelerWeb版功能更强但重一些适合专门的流程设计人员使用。如果你是一名后端开发我强烈建议直接用IDEA插件。原因有二第一流程文件和代码在一个工程里git版本管理方便review代码时能看到流程改动第二插件生成的BPMN XML是可以直接阅读的文本格式出了问题可以手动改XML不用回设计器重新导出。我用的插件是Flowable BPMN visualizerIDEA插件市场搜索Flowable就能找到。安装后在resources/processes目录右键选择New - BPMN File就能开始画流程。3.2 一个请假审批流程的建模过程我们做一个最简单的请假流程员工发起请假申请部门经理审批审批通过则结束审批驳回则退回重新发起。这个流程包含三个核心节点开始事件、用户任务、结束事件。由于流程简单暂不引入网关等后面文章专门讲。在IDEA插件里依次放置下面几个元素开始事件StartEvent流程的入口通常命名为start用户任务UserTask审批节点assignee指定为manager结束事件EndEvent流程出口命名为end连线时从开始事件指向用户任务再从用户任务指向结束事件。插件会根据连线自动生成sequenceFlow。流程保存后再生成BPMN XML文件内容大致如下?xml version1.0 encodingUTF-8? definitions xmlnshttp://www.omg.org/spec/BPMN/20100524/MODEL xmlns:flowablehttp://flowable.org/bpmn targetNamespacehttp://www.flowable.org/processdef process idaskLeave name请假审批流程 isExecutabletrue startEvent idstart name开始 flowable:initiatorinitiator/ sequenceFlow idflow1 sourceRefstart targetRefaskLeaveTask/ userTask idaskLeaveTask name部门经理审批 flowable:assigneemanager/ sequenceFlow idflow2 sourceRefaskLeaveTask targetRefend/ endEvent idend name结束/ /process /definitions流程定义的id属性是引擎中唯一标识name是显示名称isExecutabletrue表示这个定义可以被实例化。用户任务的flowable:assigneemanager指定了任务的处理人此处先用固定值演示等到讲候选人和动态表达式的时候再展开。3.3 设计流程时最容易忽略的三个细节画图的时候有几个坑我挨个说。第一个是节点ID的命名规范。很多人用中文名或随意命名比如节点1节点2。在引擎内部节点ID是流程实例追踪和API调用的关键标识建议用驼峰命名比如askLeaveTask、managerApproveTask一眼能看懂对应哪个业务节点。第二个是流程定义的key。process idaskLeave中的id就是流程定义key发起流程时要用到这个key。如果流程文件被修改并重新部署引擎会生成新的版本号但key不变这是Flowable版本管理的核心机制。不要把key起成随机字符串建议就是业务名称的英文缩写。第三个是开始事件上添加flowable:initiatorinitiator。这个配置能把发起人自动保存到流程变量initiator中后面做权限判断、审批历史展示非常有用。很多人一开始会忽略这个属性等到要展示谁发起的申请时才发现要从业务表反查多走一步弯路。4. 流程定义部署的两种方式4.1 自动部署的机制与适用场景Flowable的自动部署机制就是前文提到的启动时扫描classpath*:/processes/目录下的所有BPMN文件自动完成部署。对于开发环境这个机制非常顺手——改了XML重启应用新版本流程定义就自动生效不用写一行部署代码。自动部署有几个特性需要注意引擎会按照文件内容做MD5校验如果文件内容没变不会重复部署生成新版本。如果文件内容变化会生成新的版本号旧版本仍然保留在ACT_RE_PROCDEF表中。部署资源会写入ACT_GE_BYTEARRAY这个表只增不减长时间开发调试后体积会膨胀。开发阶段用自动部署没问题但生产环境我建议改为手动部署。生产环境里流程定义的变更应该走审批流程通过管理接口或后台页面上传BPMN文件而不是改代码发版。如果你在Spring Boot项目里用自动部署发版后如果流程定义没有问题各环境部署版本会漂移运维排查起来会很痛苦。4.2 手动部署的代码实现手动部署的核心API在RepositoryService中这是Flowable引擎中管理流程定义、模型、部署的前门。代码示例如下Service public class ProcessDeployService { Autowired private RepositoryService repositoryService; public Deployment deployProcess(String bpmnFilePath) { Deployment deployment repositoryService.createDeployment() .name(请假审批流程) .addClasspathResource(bpmnFilePath) .enableDuplicateFiltering() .deploy(); return deployment; } }代码里比较关键的是enableDuplicateFiltering()。这个方法会检查部署资源有没有变化如果BPMN文件内容与上次部署一致则跳过部署。这在重复执行部署操作的场景下非常有用避免每次调用都生成一条新的部署记录和新的流程定义版本。部署完成后可以用createProcessDefinitionQuery()查询已部署的流程定义ListProcessDefinition list repositoryService.createProcessDefinitionQuery() .processDefinitionKey(askLeave) .orderByProcessDefinitionVersion().desc() .list();这个Query API是Flowable的经典用法有条件就用条件查再配合排序和分页。开发过程中我习惯先把所有流程定义列表打出来看一眼确认版本号是否递增、部署时间是否符合预期。4.3 流程定义版本切换的两种思路使用率高的时候会发现同一key的流程定义会有多个版本。Flowable默认情况下启动新的流程实例会使用最新版本的流程定义这是最符合直觉的行为。如果想指定某个特定版本启动流程实例可以在发起时通过processDefinitionId指定而不使用processDefinitionKey。但我不建议在业务代码里硬编码版本号这会造成线上流程走的是旧版本逻辑的隐患。正确做法是让引擎自动选择最新版本把版本差异通过BPMN内部逻辑处理而不是通过发版切换。生产上如果发现流程设计有Bug需要用旧版本跑Flowable也提供了一组流程挂起/激活的API。把新版本挂起发起流程实例时会自动路由到上一个激活版本。这个机制我后面在高阶文章里会详细演示这里先留个印象。5. 发起流程实例与数据关联5.1 用RuntimeService发起第一个流程流程定义部署完成后就可以通过RuntimeService来创建流程实例。这是整个引擎最核心的入口之一负责流程实例的启停、执行流的管理、流程变量的存取。Service public class LeaveProcessService { Autowired private RuntimeService runtimeService; public String startProcess(LeaveForm form) { MapString, Object variables new HashMap(); variables.put(initiator, form.getUserId()); variables.put(leaveDays, form.getLeaveDays()); variables.put(leaveReason, form.getReason()); ProcessInstance processInstance runtimeService .startProcessInstanceByKey(askLeave, form.getBusinessKey(), variables); return processInstance.getId(); } }发起时传了三个参数流程定义key、业务主键businessKey、流程变量variables。businessKey用来关联业务系统的订单号或申请表ID比如请假单号LV202406001。流程实例与业务数据的关联全靠这个字段后续查询某张请假单走到哪一步了就是用businessKey反查流程实例。启动方法执行成功后引擎会做一套完整的操作保存流程实例到ACT_RU_EXECUTION为第一个节点创建执行流如果首节点是用户任务则创建待办任务到ACT_RU_TASK并把流程变量存到ACT_RU_VARIABLE。这些操作在同一个本地事务中完成任何一个环节失败整个流程实例都不会留下碎片数据。5.2 businessKey与流程变量的设计原则关于流程变量我想重点强调一个设计原则流程变量里不要放大数据对象只放流程流转需要的关键信息。流程变量存在于整个流程生命周期中运行时数据表一直保存变量体积过大会拖慢流程流转和查询性能。正确做法是变量只放业务主键、审批人ID、金额等轻量字段完整业务数据留在业务库的表中通过businessKey关联查询。实际项目中我习惯把需要参与条件判断的字段比如金额超过1000走会签、请假超过3天走总监审批放进流程变量其他字段一律不塞。这能避免后面做流程追踪、数据归档时变量表里塞满大字段导致索引失效。5.3 流程实例的查询与状态确认发起流程之后要确认流程是否正确跑到第一个用户任务可以写一个查询接口public ListMapString, Object listRunningProcess() { ListProcessInstance instances runtimeService.createProcessInstanceQuery() .processDefinitionKey(askLeave) .active() .list(); ListMapString, Object result new ArrayList(); for (ProcessInstance instance : instances) { MapString, Object item new HashMap(); item.put(id, instance.getId()); item.put(businessKey, instance.getBusinessKey()); item.put(activityId, instance.getActivityId()); result.add(item); } return result; }activityId表示当前执行流停留的节点ID如果流程刚发起这个值应该是askLeaveTask。看到这个值说明流程已经正确进入了审批节点。如果activityId直接跳到了结束节点说明BPMN里的连线有问题或者用户任务没有正确配置需要回头检查XML文件的targetRef和sourceRef是否对应无误。6. 完成用户任务与流程结束6.1 查询待办任务的两条路径流程走到用户任务后需要有人来处理这个任务。处理任务的第一步是查询待办。查询待办有两种常见方式按办理人查询或按候选人/候选组查询。按办理人查询也就是taskCandidateOrAssigned方法能同时查出分配给某人的任务和该人可以认领的任务代码public ListTask listTodos(String userId) { ListTask tasks taskService.createTaskQuery() .taskCandidateOrAssigned(userId) .orderByTaskCreateTime().desc() .list(); return tasks; }taskCandidateOrAssigned在开发阶段非常实用。你既可以直接指定处理人assignee又可以先把任务挂在候选人名下由多人抢办。用这个方法查待办两种任务都能查到不用写两套查询逻辑。第二种方式是按流程实例查任务。这在调试阶段更常见你知道流程实例ID想看看它当前停在哪、有哪些待办public ListTask listTasksByProcessInstanceId(String processInstanceId) { return taskService.createTaskQuery() .processInstanceId(processInstanceId) .list(); }6.2 认领任务与直接处理任务的区别Flowable对用户任务的处理分两种权限模型直接指定assignee或者设置candidate用户/组让多个人竞争处理。如果BPMN中用户任务指定了flowable:assigneemanager那么任务直接分配给managermanager可以直接complete完成任务。如果BPMN中设置的是flowable:candidateGroupsmanagers那么任务进入候选组组内任何一个用户都可以先claim认领任务认领之后任务assignee变成该用户其他用户不能再处理。两种模型的代码区别// 直接完成任务assignee模式 taskService.complete(taskId, variables); // 认领后完成任务candidate模式 taskService.claim(taskId, userId); taskService.complete(taskId, variables);用哪种模型取决于业务场景。小团队固定审批人用assignee最简单部门多人可审批用candidateGroups更灵活。我见过很多项目用的是assignee模式但assignee模式下如果这个人请假或离职任务就卡住了只能靠管理员强行转派。candidateGroups模式天然具备替代人处理的容错能力。6.3 完成任务时如何传递流程变量与走向配套这个最简单的请假流程完成审批任务时通常要把审批结果作为流程变量传进去为后续条件分支做准备。即使当前流程没有网关我还是建议养成完成任务时传变量的习惯后面扩展条件判断时不用改代码只改流程定义。public void completeTask(String taskId, boolean approved) { MapString, Object variables new HashMap(); variables.put(approved, approved); taskService.complete(taskId, variables); }任务完成之后引擎会沿着当前节点的出口连线向下执行走到下一个节点。如果下一个节点是用户任务则创建下一条待办如果是结束事件整个流程实例执行完成运行时数据表里的任务记录、执行流记录都会清理只保留历史表数据。在高版本Flowable中完成任务时还可以通过Set实现变量局部化区分全局变量和本地变量。比如审批意见这类字段只对当前任务可见不需要传给后面的节点就用本地变量。这个细节在审批流里很实用但用的人不多。有需要的话可以在后续文章里专门讲流程变量作用域。7. 常见问题与排查技巧实录7.1 流程实例发起成功但任务查询为空这个问题在开发阶段出现频率极高。代码里流程实例明明创建成功processInstanceId也有值但按照assignee查任务却查不到。排查思路只有一条查一下当前执行流在哪个节点。先用runtimeService.createProcessInstanceQuery().processInstanceId(xxx).singleResult()看activityId。如果activityId是结束节点的ID说明流程已经直接跑完中间的用户任务没有生成原因是BPMN里用户任务的flowable:assignee可能没配置或者流程定义key用的不是最新版本。如果activityId是用户任务的ID说明任务存在但assignee和你查询的用户不一致用taskService.createTaskQuery().processInstanceId(xxx).list()把该实例下所有任务都拉出来看一眼assignee字段的真实值。另外还要注意一个细节BPMN里flowable:assigneemanager是静态分配实际开发中assignee往往需要动态指定比如按提交人查找他的上级。这时候就要用表达式比如flowable:assignee${managerId}发起流程时把managerId放入流程变量。如果变量没传任务会创建失败或者assignee为空。7.2 修改BPMN后重启流程还是走旧逻辑这个问题通常不是Flowable的问题而是自动部署校验导致。引擎通过资源内容的MD5判断BPMN是否变化。但很多人在IDEA里修改的BPMN文件保存在src/main/resources/processes如果IDE没有触发target目录的资源复制或者target目录还残留旧文件引擎加载的仍然是旧内容。排查方法用repositoryService.createDeploymentQuery().orderByDeploymentTime().desc().list()查看最新的部署记录和部署时间再和你的修改时间对比。如果部署时间没变化说明文件没被重新加载。清理target目录后重新运行即可。7.3 数据库连接不够导致任务卡死Flowable的高并发操作对数据库连接消耗比较大尤其在完成一个任务时引擎会在同一事务里先更新ACT_RU_TASK再查ACT_RU_EXECUTION确定下一步节点接着创建新的待办任务最后写ACT_HI_*历史表一个完整流程操作可能要打开46个数据库连接。如果你的连接池设置在5以下并发量稍涨就会报超时。这个问题的排查方式很朴素打开数据库连接池的监控页面看活跃连接数是否在高点打满。解决方式分两步走第一把连接池maximum-pool-size调到20以上这是针对单实例的合理值第二把flowable.async-executor-activate保持为false不让测试环境跑多余的定时任务线程。7.4 流程表数据量膨胀的处理经验开发调试一段时间后ACT_HI_*表的数据量会涨得很快尤其是一次测试跑几百个流程实例几十张表每张都写数据磁盘和数据库的性能都会受影响。Flowable本身自带历史数据清理机制ProcessEngineConfigurationConfigurator中有历史清理策略配置。开发阶段最简单的做法是定期清空ACT_HI_*表但要小心别把ACT_RU_*也清了否则正在运行的流程会直接崩坏。如果项目已经开始联调环境测试不建议频繁清表。把历史级别history配置为activity而不是full能显著减少变量历史快照的存储量。配置方式flowable: history-level: activityhistory-level有多个可选值none不记录历史、activity记录流程实例与活动实例、audit默认值记录所有流程实例、活动、变量、full记录所有信息包括变量更新细节。如果不需要复杂的审计追溯用audit或activity就够能明显缓解历史表膨胀问题。8. 进一步延展从跑通流程到落地项目到这里Spring Boot 3.x集成Flowable 7.x的核心链路已经完整跑通部署流程定义、发起流程实例、查询待办任务、完成任务。这套代码放在一个真实的请假/报销/审批系统里已经能满足最基本的使用需求。但说实话距离生产可用还有一段路。我在实际落地过程中有几个方向是必须再深入的第一审批人动态化。真实项目的审批人不是静态指定而是根据发起人、部门、金额动态计算。Flowable的flowable:assignee${xxx}表达式配合Spring Bean的解析能力或者通过TaskListener动态设置assignee这两条路线都可以各有优劣。第二条件分支。请假超过3天走总监审批3天以内经理审批就行这就是排他网关的典型场景。只要在BPMN里增加一个exclusiveGateway再配几条条件分支就能实现。今天的流程没有加网关下一篇文章我会专门写条件的各种写法。第三驳回与重新提交。真实审批流绕不开驳回。Flowable原生没有驳回这个原子操作但可以通过runtimeService.createChangeActivityStateBuilder()实现节点跳转或者通过边界事件、补偿事件实现更复杂的驳回逻辑。这个功能是所有工作流项目里最核心、也最容易写错的部分。第四流程追踪与图谱展示。给流程加上高亮当前节点、显示历史轨迹的功能能极大提升系统的可用性。Flowable的HistoricActivityInstance记录表里能找到完整的流转历史但展示层的适配工作还需要不少功夫。最后再分享一个我在方案选型上的个人体会。Flowable和Activiti同源但Flowable的社区活跃度和长期维护的确定性更高7.x版本的API设计也更现代。如果你要在一个新项目里从零接入工作流我建议直接选择Flowable 7.x如果你手里是用了Activiti 5/6的老系统可以评估下迁移成本再决定是否迁移毕竟工作流引擎的替换不比换数据库轻松多少。下一篇文章我会接着写条件网关与动态审批人把流程的活字做出来。先到这里有问题的朋友可以直接留言我看到都会回。