简介:《Activiti6 中文用户指南》面向使用 Activiti6 进行业务流程开发、配置与集成的 Java 开发者、架构师及流程设计人员,围绕开源工作流引擎的 BPMN 2.0 建模、流程部署与执行展开,帮助读者理解从环境准备到引擎配置、API 调用和 Spring 集成的整体脉络。压缩包仅含 1 个 docx 文档,大小约 4.22MB,以中文版用户指南形式组织内容,目录覆盖快速入门、ProcessEngine 与数据库配置、Job Executor、邮件服务、查询 API、变量与表达式、单元测试、BAR 部署及 BPMN 2.0 事件构造等模块,便于按主题检索和系统查阅。已有 1816 人学习,反映其在工作流学习群体中具有一定参考价值。读者可据此建立 Activiti6 的核心概念框架,掌握流程引擎配置、服务调用、Spring 整合和常见 BPMN 元素的用法,也能在流程建模、部署与排错时作为案头手册使用。
1. 先搞懂一件事:Activiti6 到底是个什么东西
很多人第一次听到 Activiti6,第一反应是“这是个画流程图的软件吧”。不完全是。画图那部分是给人看的,真正干活的是图背后那台“发动机”——你把请假、报销、工单这些业务的流转规则写成流程图,交给它去跑,它就负责记录“现在走到哪一步”“该谁处理”“超时了要不要自动升级”。这份《Activiti6 中文 用户指南》做的事情很朴素:把官方那套英文文档整本翻成能读懂的中文,从装环境、建数据库、写第一个流程,一直讲到 BPMN 2.0 里的网关、事件、子流程和 REST 接口。它适合两类人:一类是刚被安排接手审批流需求的 Java 新手,一类是想搞清楚“工作流引擎在系统里到底占什么位置”的旁观者。不需要你先懂 BPMN,但得会一点 Java。
2. 为什么新手值得学:一份中文用户指南能省掉多少弯路
先别急着敲代码。零基础学一样东西,最怕的不是难,是不知道它为什么存在。这一章把“为什么”讲透,顺带告诉你拿到这份指南之后第一周该怎么翻,别一上来就从第 1 页啃到最后一页。
2.1 业务流程为什么需要一个引擎去跑
假设你接了个请假审批的需求,最直接的做法是写一个 status 字段:0 是待提交、1 是主管审批中、2 是人事审批中、3 是已通过、4 是已驳回。代码里到处是if (status == 1) { ... },看起来还行。三个月后产品说,要加一级“总监审批”,还要支持“三天以内主管批完就行,超过三天必须总监批”。你打开代码,发现这个判断散落在七个类里,改一处漏一处,上线后状态机直接歪掉。
工作流引擎干的就是把这堆 if-else 抽出来的活。你把流转规则画成一张 BPMN 图,引擎按图跑;流程走到哪、谁在处理、每一步花了多久,它自己记在数据库里。加一级审批,改图不改代码;想知道某个单子卡在谁那儿,查一下就行。这就是“业务逻辑”和“流程逻辑”分开的价值,也是新手最该先建立的一个认知:引擎不是帮你画图,是帮你把“状态”这件事管起来。
顺带说一句,这类系统的玄学之处在于,流程跑通的时候你感觉不到它存在,一旦出问题——任务没了、实例卡住、历史查不到——你就得知道它内部有哪些部件。这也是为什么后面几章要讲那四个 Service,而不是只教你点按钮。
2.2 英文文档卡住的地方,中文指南正好补上
官方文档写得很全,但对新手不友好。它的组织方式是“构造参考”,一个事件定义能写三页,术语密度高:ProcessEngine、Execution、Task、Gateway、Boundary Event、Compensation……英文原文一句话套三四个从句,读三行就得回头再读一遍。很多人不是学不会,是卡在“这个单词我认识,但这句话在说什么”上,卡着卡着就放弃了。
这份中文指南的价值不在于“翻译得优雅”,而在于它的目录结构和官方完全对齐。第 1 章简介、第 2 章入门、第 3 章配置、第 4 章 API、第 7 章 BPMN 简介、第 8 章 BPMN 构造、第 13 章 REST API——你遇到具体问题的时候,可以按目录直接定位到某一节,不用在英文页面里靠搜索框碰运气。比如你在配数据库时卡住了,翻到 3.3 数据库配置和 3.5 支持的数据库,两页就能对上号。
提示:别把它当小说从头读到尾。它是手册,按需查、按需读,读的时候手上开着 IDE 效果最好。
2.3 三个最典型的使用场景,看看有没有你的
第一种是内部审批类:请假、报销、采购申请、用章申请。特征是流程相对固定、参与人多、需要留痕。这类场景用 Activiti 几乎是标准答案,因为你不用自己设计状态机和审批记录表,引擎自带。
第二种是工单类:客服工单、故障处理单、内容审核单。特征是流程里会有人工分派、超时提醒、升级转派。指南第 8 章里的计时器边界事件、用户任务的候选组分配,基本就是为这类需求准备的。
第三种是带定时或异步动作的业务:合同到期前三天提醒、订单超过 24 小时未支付自动取消、批量任务分批处理。这类需求看着和“工作流”没关系,但只要涉及“到点触发”和“失败重试”,引擎的 Job Executor(指南 3.9 节)就能派上用场。
三种场景的共同点是:流程会变、参与方多、要能查历史。如果你的需求三个月内不会改、只有一个人操作、也不需要审计,那老实说用状态字段更省事,别为了用而用。
2.4 拿到指南后的第一周怎么读
给一个具体安排,避免你打开文档就迷路。
第一步,花半小时读第 1 章和第 2 章。第 1 章告诉你需要 JDK 7+ 和一个 IDE,第 2 章的“一分钟版”教你把 war 包丢进 Tomcat 跑起来。这一步的目标只有一个:在浏览器里看到登录页,用默认账号进去,建立“这东西长什么样”的印象。
第二步,花两小时读第 7 章的 BPMN 2.0 简介和那个 10 分钟教程。不要跳,那个教程把“画图 → 部署 → 启动 → 领取任务 → 完成任务 → 结束”整条链路走了一遍,是整份文档里性价比最高的一段。
第三步,回头读第 3 章配置和第 4 章 API。这时候你已经见过界面了,再看 ProcessEngineConfiguration、数据库表名、四个 Service 的职责,脑子里有画面,不至于全是抽象名词。
第四步,等你能跑通一个自己的流程,再去翻第 8 章的构造部分。网关、事件、子流程这些是“用得上再学”的东西,一上来全看只会劝退。
3. 第一步怎么迈:先把界面跑起来,看见流程长什么样
理论说够了,动手。这一章的目标很明确:让你的电脑上出现一个能登录的工作流界面,然后手动发布一个流程,再用代码跑一遍。整个过程不需要你理解 BPMN 语法,先有体感。
3.1 三样东西备齐:JDK、Tomcat、一个顺手的 IDE
指南 1.4 节写得很清楚,最低要求是 JDK 7+,实际建议直接上 JDK 8,兼容性最稳。IDE 用 Eclipse 或 IntelliJ IDEA 都行,指南第 12 章专门讲了 Eclipse 设计器的安装,如果你习惯 Eclipse,可以顺手装上,画图会方便一些;用 IDEA 的话,BPMN 文件当普通 XML 编辑也能改,只是没有图形化拖拽。
第三样是 Tomcat。指南里提到“任何 Web 容器都可以,因为我们只依赖 servlet 功能,但主要在 Tomcat 上测试”,所以别折腾别的,Tomcat 8 或 9 随便挑一个下载解压就行,不需要安装。另外建议装个 Maven,后面写 Java 代码拉依赖会省很多事。
有个容易忽略的点:这三样东西的版本要对得上。JDK 8 配 Tomcat 9 没问题,但如果你机器上装的是 JDK 17,跑老版本的 Tomcat 可能会因为模块访问限制报一堆看不懂的错。新手阶段别追新,稳一点。
3.2 第一个动作:让 activiti-app 在浏览器里出现登录页
这是整份指南里最简单也最有成就感的一步。把下载到的activiti-app.war复制到 Tomcat 的webapps目录,然后启动 Tomcat。以 Linux 或 macOS 为例:
# 假设 Tomcat 解压在 /opt/tomcat,war 包下载在 ~/Downloads cp ~/Downloads/activiti-app.war /opt/tomcat/webapps/ # 启动 Tomcat,Windows 下用 bin\startup.bat,macOS/Linux 用 startup.sh /opt/tomcat/bin/startup.sh # 想看启动日志,另开一个终端跟一下 tail -f /opt/tomcat/logs/catalina.out第一行是把 war 包丢进 webapps,Tomcat 启动时会自动解压部署,你不用手动解压。第三行是跟日志,新手一定要养成这个习惯:war 包部署失败不会弹窗告诉你,只会安静地在日志里写一行异常,不看日志就只能对着 404 干瞪眼。
Tomcat 起来之后,浏览器打开http://localhost:8080/activiti-app,用账号admin、密码test登录。能进去,说明环境通了。
这里要提醒一句:这个演示应用默认用的是内存数据库 H2,意思是 Tomcat 一关,你画的流程、跑的单子全没了。用来熟悉功能完全够,但别拿它存正式数据。想换成 MySQL 之类的持久库,去看指南 3.3 到 3.6 那几节,配置数据源、建表、表名说明都在里面。
3.3 在界面上画一个最简单的流程并发布
登录进去之后,找到流程建模的入口,新建一个流程。只用三个元素:一个开始事件、一个用户任务、一个结束事件,中间用顺序流连起来。用户任务上填个名字叫“主管审批”,指派人在演示环境里可以直接写admin。
保存,然后发布。发布这个动作在引擎里叫“部署”(deploy),它会把你的流程图打包成一个流程定义存进数据库,同时分配一个版本号。同一张图你改一次再发布一次,版本号就加一,老版本不会消失,已经在跑的实例还按老版本走。这个设计后面第 4 章还会讲,现在有个印象就行。
发布完之后,去流程列表里启动一个实例,然后你会看到一条待办任务落在那里。点进去、填个意见、提交,流程走到结束事件,实例状态变成已完成。整个过程不到五分钟,但你已经把工作流最核心的六件事走了一遍:建模、部署、启动、产生任务、完成任务、结束。
3.4 用代码跑通第二次:部署、启动、完成任务
界面上点按钮的感觉有了,现在换成代码。因为真实项目里,流程的启动和任务的完成都是业务代码触发的,不可能让人去点。
下面这段代码用内存数据库建了一个引擎,部署流程、启动实例、查任务、完成任务一气呵成。先看 BPMN 文件长什么样:
<?xml version="1.0" encoding="UTF-8"?> <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:activiti="http://activiti.org/bpmn" targetNamespace="http://demo.example/leave"> <!-- process 的 id 就是后面启动时用的 key,别和 name 搞混 --> <process id="leaveProcess" name="请假流程" isExecutable="true"> <startEvent id="startEvent"/> <sequenceFlow id="flow1" sourceRef="startEvent" targetRef="approveTask"/> <!-- 用户任务:assignee 指定处理人,实际项目里常用变量或候选组代替写死 --> <userTask id="approveTask" name="主管审批" activiti:assignee="A同学"/> <sequenceFlow id="flow2" sourceRef="approveTask" targetRef="endEvent"/> <endEvent id="endEvent"/> </process> </definitions>把它存成leave.bpmn20.xml,放到src/main/resources目录下。文件名后缀bpmn20.xml是约定,引擎部署时会按这个后缀识别流程文件。
然后是 Java 代码:
// 1. 建引擎。用内存库,重启数据就没了,适合练手 ProcessEngineConfiguration cfg = ProcessEngineConfiguration .createStandaloneInMemProcessEngineConfiguration() .setJdbcUrl("jdbc:h2:mem:activiti-demo;DB_CLOSE_DELAY=1000") .setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE); ProcessEngine engine = cfg.buildProcessEngine(); // 2. 拿到后面要用的服务 RepositoryService repositoryService = engine.getRepositoryService(); RuntimeService runtimeService = engine.getRuntimeService(); TaskService taskService = engine.getTaskService(); // 3. 部署:把 bpmn 文件发布成一个流程定义 repositoryService.createDeployment() .addClasspathResource("leave.bpmn20.xml") .name("请假流程") .deploy(); // 4. 启动实例,业务变量一起带进去 Map<String, Object> vars = new HashMap<>(); vars.put("days", 3); vars.put("applicant", "A同学"); ProcessInstance pi = runtimeService.startProcessInstanceByKey("leaveProcess", vars); // 5. 查出当前待办任务 Task task = taskService.createTaskQuery() .processInstanceId(pi.getId()) .singleResult(); // 6. 完成任务,流程继续往下走到结束事件 taskService.complete(task.getId());逐段解释一下。第一段是配置,createStandaloneInMemProcessEngineConfiguration会给你一套开箱即用的配置,连数据库表都自动建好,DB_SCHEMA_UPDATE_TRUE就是让它自动建表的意思,新手阶段别改成 false,否则会报找不到表。第二段拿服务,RepositoryService 管部署和流程定义的查询,RuntimeService 管实例的启动、暂停、删除,TaskService 管任务的查询和完成,这三个是使用频率最高的。
第三段部署,addClasspathResource从 classpath 里找文件,路径写相对于resources的位置;name是给这次部署起的人话名字,方便在后台看。第四段启动,startProcessInstanceByKey里的leaveProcess就是 XML 里process标签的 id,不是 name,这里新手最容易写错。传进去的vars会成为流程变量,后面可以在表达式、监听器、网关条件里用。
第五段查任务,processInstanceId限定在当前实例范围内,singleResult表示只取一条,如果按条件查出多条它会抛异常,查询条件写不严谨就会在这儿翻车,所以正式代码里更常用list()加Page。第六段完成任务,任务一完成,引擎立刻沿着顺序流把执行推进到下一个节点,这里是结束事件,实例随之结束。
跑完这段代码,你已经完成了一次完整的流程生命周期。接下来该做的不是继续加功能,而是去数据库里看一眼——表名以ACT_开头,ACT_RU_是运行时表,ACT_HI_是历史表,流程一结束,运行时表里的记录就清空了,历史表里还留着痕迹。这个认知对排查问题极其重要。
4. 概念别硬背:用一次请假申请串起核心 API
上一章是照着做,这一章是搞懂为什么这么做。工作流的术语看着吓人,其实就那么几个,用一次请假流程全串起来,比你抄十遍定义管用。
4.1 ProcessEngine 与它手下的四个 Service
ProcessEngine 是整个引擎的门面,也是重量级对象,全局建一个就够了,反复建会拖垮应用。它本身不干活,活是手下几个 Service 干的,记住职责分工比记住类名重要:
| Service | 管什么 | 典型用法 |
|---|---|---|
| RepositoryService | 流程定义的部署、查询、挂起 | 上线新版本流程图 |
| RuntimeService | 流程实例的启动、变量、删除 | 用户提交一张请假单 |
| TaskService | 待办任务的查询、领取、完成 | 主管点“同意” |
| HistoryService | 已结束实例、历史任务、审计数据 | 查某张单子走了几天 |
| ManagementService | 作业、定时器、引擎元数据 | 排查卡住的异步任务 |
新手常见的迷路方式是:想查历史却去调 RuntimeService,结果查不到东西,因为实例一结束运行时数据就清了。判断方法很简单:这个数据在流程结束后还需要吗?需要就用 HistoryService。
4.2 流程定义是模板,流程实例是复印件
这两个词是新手混得最厉害的一对。打个比方:流程定义是 Word 模板文件,流程实例是有人照着模板新建出来的一份文档。你可以用同一个模板建一百份文档,每份的内容互不影响。
放到引擎里:leaveProcess这个定义只有一个,但张三、李四各提交一次请假,就是两个实例,各自有独立的 ID、独立的变量、独立的执行路径。改流程定义不会影响已经跑起来的实例,因为引擎给每个实例记住的是它启动时那个版本的 ID。
版本这个设计,指南 6.3 节叫“流程定义的版本控制”。实际用起来是这样的:你把“三天以内直接主管批”改成“五天以内”,重新部署,版本号从 1 变成 2。这时候库里还有三个实例跑在版本 1 上,它们继续按老规则走完,新提交的单子才用版本 2。这个行为能救命——你不可能要求线上所有在途单子停下来等你改图。
提示:部署时如果不指定 key,引擎会用流程图的 id 作为 key。同一个 key 反复部署就是版本递增;key 不同则是两个不相干的流程。
4.3 变量、任务与历史:三条一定会用到的线
变量是流程和业务代码之间传数据的通道。上面代码里塞进去的days、applicant,可以在网关的条件表达式里直接用,比如${days > 3}就走总监审批分支。变量分两种作用域:流程实例级和任务级,任务级的变量只在那个任务里可见,任务完成后就没了。指南第 4.5 和 4.6 节还讲了“瞬态变量”,那种不落库、只在当前事务里有效的类型,适合传一些不想留痕的临时数据。
任务是给人的。用户任务会生成一条待办记录,谁处理、什么时候到期、有哪些候选人,都写在那条记录上。查任务时掌握三个维度就够了:按处理人查(taskAssignee)、按候选组查(taskCandidateGroup)、按流程实例查(processInstanceId)。这三个覆盖了八成场景。
历史是给系统和审计看的。历史级别有 none、activity、audit、full 四档,配得越高记录越细、占的空间越大。新手开发阶段建议开full,出问题时能查到底;上线前再按实际需要降下来。指南第 11 章整章在讲历史查询,HistoricProcessInstanceQuery查实例、HistoricTaskInstanceQuery查任务、HistoricActivityInstanceQuery查每个节点的停留时间,做“这个单子卡了多久”这类统计全靠它们。
4.4 一个最小可运行的 Java 示例(含配置)
下面这段代码比上一章稍微完整一点,加了变量查询和历史查询,你可以直接拿去改。
// 启动实例时带上天数,后面网关可能用到 Map<String, Object> vars = new HashMap<>(); vars.put("days", 5); ProcessInstance pi = runtimeService.startProcessInstanceByKey("leaveProcess", vars); // 查这个实例的所有流程变量 Map<String, Object> allVars = runtimeService.getVariables(pi.getId()); System.out.println("申请天数:" + allVars.get("days")); // 查当前待办,按处理人过滤 List<Task> todo = taskService.createTaskQuery() .taskAssignee("A同学") .orderByTaskCreateTime().desc() .list(); // 完成任务时顺带写入审批意见,作为任务级变量 Map<String, Object> taskVars = new HashMap<>(); taskVars.put("approved", true); taskVars.put("comment", "同意,注意交接"); taskService.complete(todo.get(0).getId(), taskVars); // 实例结束后查历史,确认它确实走完了 HistoricProcessInstance hpi = engine.getHistoryService() .createHistoricProcessInstanceQuery() .processInstanceId(pi.getId()) .singleResult(); System.out.println("结束时间:" + hpi.getEndTime());几个要点说明。getVariables拿的是实例级变量,返回的是 Map,取出来是 Object,要自己转型,别直接强转成 Integer 又怪它报 ClassCastException。orderByTaskCreateTime().desc()是按创建时间倒序,做待办列表几乎必加,否则顺序不可控。complete的第二个参数是任务级变量,任务完成后这些变量会合并到实例作用域——这一点容易误解,以为任务级变量会消失,实际上合并后实例里也能查到,只是作用域标记不同。
最后查历史那段,如果hpi是 null,说明实例还没结束或者历史级别配成了 none。这两个原因排查起来完全不一样,先看引擎配置,再看实例状态,别一上来就怀疑引擎坏了。
第一次跑这段代码的时候,我卡在singleResult上——当时库里残留了两个同名流程定义,查出来两条直接抛异常。血泪经验:只要用singleResult,前面一定得有条件能保证唯一,否则老老实实用list()然后判断 size。
5. 新手最容易翻车的五个地方:避坑与排查
这一章全是踩过的坑。每条按“现象 → 原因 → 解决”写,遇到问题照着对号入座,比漫无目的地搜错误信息快得多。
5.1 坑一:Tomcat 起来却打不开页面
现象:startup.sh执行完没报错,浏览器访问 8080 一直转圈或者 404。
原因通常有两个。一是 war 包还在解压,大包解压要几十秒,日志里能看到Deploying web application archive之后还在动,这时候访问当然 404;二是 8080 端口被别的程序占了,Tomcat 启动时会报Address already in use,但一闪而过,不翻日志根本看不见。
解决:先tail -f logs/catalina.out盯着日志,等到出现Server startup in xxx ms再访问。端口冲突就改conf/server.xml里的 Connector 端口,或者先把占用端口的进程关掉。顺带提一句,war 包名决定了访问路径,activiti-app.war对应/activiti-app,重命名了路径也会变,别自己改完又找不到。
5.2 坑二:换成 MySQL 后连不上,报驱动或方言错误
现象:按指南配了 MySQL,启动时抛Could not find driver或者Unknown database。
原因:activiti-app.war里默认只带了 H2 的驱动,你要连 MySQL,得把mysql-connector-java的 jar 放进webapps/activiti-app/WEB-INF/lib下,或者干脆在 Maven 项目里加依赖。另外数据库本身得先建好,引擎只会建表不会建库,jdbc:mysql://localhost:3306/activiti里的activiti库不存在,一样连不上。
解决:确认三件事——驱动 jar 在不在 classpath、数据库有没有提前创建、连接 URL 里的时区和编码参数有没有带(serverTimezone=UTC&characterEncoding=utf8)。指南 3.5 节列了支持的数据库清单,版本对不上也可能出方言问题,用清单里的版本最省心。
5.3 坑三:部署成功了,启动实例却报找不到流程定义
现象:deploy()没抛异常,紧接着startProcessInstanceByKey("leaveProcess")抛Cannot find process definition。
原因:key 写错了。startProcessInstanceByKey的参数对应的是 BPMN 文件里process标签的id属性,不是name,也不是文件名。很多人把文件名leave.bpmn20.xml当成 key,或者把中文 name 填进去,自然找不到。
解决:部署成功后立刻查一下,repositoryService.createProcessDefinitionQuery().list()把 key 打出来看一眼。另外注意流程定义可能被挂起了,挂起状态下启动同样失败,ProcessDefinition.isSuspended()能看出来。这个坑很傻,但栽进去的人特别多。
5.4 坑四:任务列表永远是空的
现象:实例启动成功,taskService.createTaskQuery().taskAssignee("A同学").list()返回空列表,可明明流程图里有用户任务。
原因:三种可能。第一,BPMN 里assignee拼错了或者压根没写,任务生成时处理人为 null;第二,用的是候选组而不是处理人,这时候应该查taskCandidateGroup或者taskCandidateUser;第三,任务生成在别的流程实例里,你的查询条件没限制实例,但碰巧那两个实例还没启动。
解决:先用taskService.createTaskQuery().list()不带任何条件查全量,看看到底有没有任务、assignee字段是什么值。有值但查不到,说明条件写错;没值,回去看 BPMN 配置。指南 8.5.1 节用户任务那部分把 assignee、candidateUsers、candidateGroups 讲得很细,值得单独读一遍。
5.5 坑五:Spring Boot 里开发环境好好的,打成 jar 就找不到流程文件
现象:IDE 里跑单元测试一切正常,mvn package之后启动,报resource not found: xxx.bpmn20.xml。
原因:addClasspathResource在 fat jar 里的资源加载路径和开发时不一样,用相对路径或者file:前缀去读,在 jar 包里根本不存在那个文件系统路径。
解决:统一用 classpath 相对路径,把 BPMN 文件放在src/main/resources/processes/下,代码里写addClasspathResource("processes/xxx.bpmn20.xml"),别加前导斜杠,也别用绝对路径。如果一定要用通配符批量部署,用addClasspathResource配合PathMatchingResourcePatternResolver,指南 5.4 节自动资源部署那段有现成写法。
6. 下一步学什么:从能跑通到能用上的进阶路线
跑通第一个例子之后最容易犯的错,是急着去啃第 8 章那几百页的 BPMN 构造。那部分内容密度极高,网关、事件、子流程、补偿,每个都能单独写一章,你现在没有对应的业务场景,看完就忘,还容易产生“我是不是学不会”的错觉。
我的建议是按场景驱动,用到什么学什么。给一条具体路线:
| 阶段 | 该看指南哪部分 | 达成目标 |
|---|---|---|
| 第一周 | 第 2 章入门、第 7 章 10 分钟教程 | 能独立部署并跑通一个流程 |
| 第二周 | 第 4 章 API、第 3 章配置 | 会用代码启动实例、查任务、配 MySQL |
| 第三周 | 第 8.4 节网关、第 8.5 节任务 | 能写出带分支和会签的流程 |
| 第四周 | 第 11 章历史、第 13 章 REST API | 能做流程监控页、给前端提供接口 |
第三周的“会签”是个分水岭。指南 8.5.14 节的多实例(multi-instance)讲的正是这个:一个任务同时派给多个人,全部完成才往下走,或者任意一个完成就往下走。这个特性在审批场景里出现频率极高,值得专门花半天做个小 demo。
再往后有两个方向可以选。偏业务的,去啃第 8.2 节的事件定义,计时器事件做超时提醒、错误事件做异常兜底、信号事件做跨流程通知,这三样基本覆盖了所有“流程要自动做点什么”的需求。偏架构的,去读第 5 章 Spring 集成和第 13 章 REST API,前者解决引擎怎么嵌进现有系统,后者解决前端怎么调。
还有一个特别实用但经常被忽略的技巧:善用单元测试。指南 4.8 节专门讲了流程的单元测试写法,配合内存数据库,你可以给每个流程定义写一个测试,断言它走完之后的节点顺序和历史记录。流程这种东西改起来牵一发动全身,有测试兜底和没测试兜底,心态完全不一样。
最后提醒一句方向性的问题:不要试图把工作流引擎当成万能状态机去套所有业务。它擅长的是“有人参与、有先后顺序、要留痕”的流程;纯粹的订单状态流转、秒杀库存扣减这类高频短链路,用引擎反而拖慢性能。判断标准很朴素——这条链路上有没有“人点一下”的动作,有就考虑,没有就别硬上。
我第一次接手流程需求的时候,图省事直接在生产库上画图调试,改一次发布一次,版本号一路涨到 17,最后自己都分不清哪个是当前在用的。从那以后我每次接新流程,都强制先在本地用内存库把最小 demo 跑一遍,确认节点、变量、分支全对,再动正式环境的配置。这个习惯帮我省了不止一次后悔药。希望帮到你。
本文还有配套的精品资源,点击获取