简介:这份资源是 Activiti6 工作流引擎的中文用户指南文档,面向需要基于 BPMN 2.0 标准设计、部署与执行业务流程的 Java 开发者、架构师及流程平台运维人员。内容覆盖系统要求、快速入门、ProcessEngine 与数据库配置、Job Executor、邮件服务器与历史记录配置、Process Engine API 与查询、Spring 与 Spring Boot 集成、BAR 部署与版本控制,以及 BPMN 2.0 构造中的各类事件、边界事件、中间捕获与抛出事件等,目录结构完整,适合从入门到进阶系统学习。资源包内共 1 个 docx 文件,大小约 4.22MB,为纯文档型资料,便于离线查阅与检索。目前已有 1816 人学习下载,读者可借助它快速掌握流程引擎配置、API 调用、Spring 整合与 BPMN 元素用法,减少查阅英文原版的时间成本,适合作为日常开发与排错时的案头参考。
1. 从一份离线文档说起:Activiti6 中文用户指南到底能帮你省下多少时间
如果你正在做企业审批流、工单流转或者 OA 系统,大概率绕不开 Activiti6 这个开源工作流引擎。它基于 BPMN 2.0 标准,能设计、部署、执行整套业务流程,Java 生态里算是老牌选手了。但真正上手时你会发现,官方英文文档散落在各个版本之间,搜索引擎翻出来的教程又经常对不上 6.0.0 的 API——尤其是 Job Executor 从 6.0.0 才开始独立配置,很多老文章还在讲 5.x 的写法,照着抄直接翻车。
这份《Activiti6 中文 用户指南.docx》就是针对 6.0.0 版本整理的中文参考,覆盖了从环境搭建、ProcessEngine 配置、数据库选型,到 BPMN 2.0 构造、Spring 集成、REST API 的完整链路。它适合两类人:一是刚接触工作流引擎、需要一份能对着敲的入门手册的开发者;二是已经在用 Activiti,但被某个事件边界或监听器配置卡住、需要快速查参数的老手。下面我按实际落地顺序,把这份文档里最值得先啃的部分拆开讲。
2. 环境搭建与 ProcessEngine 初始化:从零跑通第一条流程
2.1 JDK、IDE 与依赖版本怎么选
文档里写得很明确:JDK 7+ 就能跑,但实际项目里我建议直接上 JDK 8 或 11,因为 Activiti6 内部大量用了 Java 8 的 Lambda 和 Stream,JDK 7 虽然能编译,但某些表达式解析会出玄学问题。IDE 用 Eclipse 或 IntelliJ IDEA 都行,文档里提到的 Eclipse Designer 插件是 12 章的内容,如果你不打算在 Eclipse 里画流程图,用 IDEA 配合 bpmn-js 在线编辑器也完全够。
Maven 依赖是第一步。Activiti6 的核心包是activiti-engine,如果你要用 Spring 集成再加activiti-spring,要用 REST API 再加activiti-rest。注意版本号必须统一,6.0.0 就是 6.0.0,混用 6.0.0 和 6.1.0 的包会出现ProcessEngineConfiguration类找不到的报错。
<!-- pom.xml 核心依赖,版本必须锁死 --> <dependency> <groupId>org.activiti</groupId> <artifactId>activiti-engine</artifactId> <version>6.0.0</version> </dependency> <dependency> <groupId>org.activiti</groupId> <artifactId>activiti-spring</artifactId> <version>6.0.0</version> </dependency> <!-- 默认 H2 内存数据库,生产环境换成 MySQL/Oracle 驱动 --> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <version>1.4.190</version> </dependency>逻辑说明:activiti-engine是核心,包含流程解析、执行、任务管理;activiti-spring只在用 Spring 管理事务时才需要。H2 依赖是为了让默认配置能直接跑起来,文档 2.3 节提到的“内存数据库”就是它。参数上,H2 的版本不要低于 1.4.190,否则 Activiti 建表时ACT_GE_PROPERTY表的字段长度会报错。
2.2 用代码创建 ProcessEngine 并验证建表
文档 3.1 节给了两种创建方式:独立运行用ProcessEngineConfiguration.createStandaloneProcessEngineConfiguration(),Web 环境用createStandaloneInMemProcessEngineConfiguration()。我一般先用独立模式验证数据库连通性,确认表能建出来再切到 Spring 配置。
// 独立模式创建 ProcessEngine,自动建表 ProcessEngineConfiguration cfg = ProcessEngineConfiguration .createStandaloneProcessEngineConfiguration() .setJdbcUrl("jdbc:h2:mem:activiti;DB_CLOSE_DELAY=1000") .setJdbcUsername("sa") .setJdbcPassword("") .setJdbcDriver("org.h2.Driver") .setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE); ProcessEngine processEngine = cfg.buildProcessEngine(); System.out.println("ProcessEngine 创建成功:" + processEngine.getName());逻辑说明:setDatabaseSchemaUpdate(DB_SCHEMA_UPDATE_TRUE)是关键参数,它让 Activiti 启动时自动检查并创建 25 张核心表(ACT_RE_*、ACT_RU_*、ACT_HI_*、ACT_ID_*等)。如果设成DB_SCHEMA_UPDATE_FALSE,表不存在就直接抛异常。DB_CLOSE_DELAY=1000是 H2 内存库的参数,保证 JVM 不退出时数据库连接不断开,否则建完表一关连接数据就没了。
跑完这段代码,控制台会打印建表日志,最后输出ProcessEngine 创建成功:default。这时候你用 H2 的 Web Console 或者命令行去查,能看到ACT_RE_DEPLOYMENT、ACT_RU_EXECUTION这些表已经就位。文档 3.7 节专门列了表名说明,比如ACT_HI_*是历史表,ACT_RU_*是运行时表,排查问题时先看这两类表的数据量就能判断流程卡在哪。
2.3 数据库连接池与 JNDI 的取舍
文档 3.3 和 3.4 节讲了数据库配置和 JNDI 数据源。实际项目里,如果你在 Tomcat 或 WildFly 这类容器里跑,用 JNDI 拿数据源是标准做法,配置写在context.xml或standalone.xml里,Activiti 的ProcessEngineConfiguration只需要设setDataSourceJndiName("java:comp/env/jdbc/activiti")。但如果你用 Spring Boot,直接配spring.datasource更省事,Activiti 的 Spring Boot Starter 会自动接管。
这里有个坑:JNDI 模式下,setDatabaseSchemaUpdate依然要设成TRUE或者手动执行建表 SQL,否则第一次部署流程时ACT_RE_DEPLOYMENT表不存在,部署直接失败。我一般会在测试环境开TRUE,生产环境用 Flyway 或 Liquibase 管理表结构,把DB_SCHEMA_UPDATE设成FALSE,避免引擎启动时误改表。
3. BPMN 2.0 核心构造:事件、网关与任务的实际配置
3.1 计时器边界事件与错误边界事件的参数差异
文档第 8 章把 BPMN 2.0 构造拆得很细,其中边界事件是审批流里用得最多的。计时器边界事件挂在用户任务上,用来做超时自动提醒或自动通过;错误边界事件挂在服务任务上,用来捕获 Java 代码抛出的异常并走补偿逻辑。
<!-- 计时器边界事件:用户任务 30 分钟未处理则触发提醒 --> <boundaryEvent id="timerBoundary" attachedToRef="userTask1" cancelActivity="false"> <timerEventDefinition> <timeDuration>PT30M</timeDuration> </timerEventDefinition> </boundaryEvent> <!-- 错误边界事件:捕获服务任务抛出的 BPMN 错误 --> <boundaryEvent id="errorBoundary" attachedToRef="serviceTask1"> <errorEventDefinition errorRef="myErrorCode" /> </boundaryEvent>逻辑说明:cancelActivity="false"表示计时器触发后不取消原任务,只发提醒;设成true则原任务被取消,流程走边界事件的出口。timeDuration用 ISO 8601 格式,PT30M是 30 分钟,P1D是 1 天。错误边界事件的errorRef必须和 Java 代码里throw new BpmnError("myErrorCode")的编码一致,否则捕获不到,流程会直接挂掉。
文档 8.2.18 节专门提了“边界事件的已知问题”:如果边界事件挂在多实例任务上,计时器只会对第一个实例生效。这个坑我在一个工单系统里踩过,后来改成在流程变量里记录每个实例的创建时间,用中间捕获事件轮询才绕过去。
3.2 排他网关与并行网关的表达式写法
网关是流程分支的核心。排他网关(Exclusive Gateway)按条件选一条路走,并行网关(Parallel Gateway)把所有出口都走一遍。文档 8.4 节给了 XML 表示,但条件表达式的变量引用方式容易写错。
<!-- 排他网关:根据金额决定审批级别 --> <exclusiveGateway id="decisionGateway" /> <sequenceFlow id="flow1" sourceRef="decisionGateway" targetRef="managerApprove"> <conditionExpression xsi:type="tFormalExpression"> <![CDATA[${amount <= 5000}]]> </conditionExpression> </sequenceFlow> <sequenceFlow id="flow2" sourceRef="decisionGateway" targetRef="directorApprove"> <conditionExpression xsi:type="tFormalExpression"> <![CDATA[${amount > 5000}]]> </conditionExpression> </sequenceFlow>逻辑说明:${amount <= 5000}里的amount是流程变量,启动流程实例时通过runtimeService.startProcessInstanceByKey("myProcess", variables)传入。注意 XML 里<和>必须用 CDATA 包起来,否则解析器会报错。排他网关如果没有匹配的条件,会抛ActivitiException: No outgoing sequence flow found,所以最好设一条默认流,用default="flow1"属性指定。
并行网关不需要条件表达式,所有出口都会生成并发执行分支。但要注意:并行网关的汇聚点会等待所有分支到达才继续,如果某个分支因为异常没走到汇聚点,流程就卡住了。文档 8.7 节的“异步延续”和“独占作业”就是解决这类并发问题的,把服务任务设成activiti:async="true"可以让 Job Executor 异步重试,避免线程阻塞。
3.3 用户任务分配与任务监听器的组合用法
用户任务的分配方式文档 8.5.1 节列了三种:直接指定assignee、用candidateUsers或candidateGroups做候选、通过任务监听器动态分配。实际项目里最灵活的是第三种,比如根据流程变量里的部门 ID 查数据库拿到审批人。
// 任务监听器:动态设置审批人 public class AssigneeTaskListener implements TaskListener { @Override public void notify(DelegateTask delegateTask) { String deptId = (String) delegateTask.getVariable("deptId"); // 模拟从数据库或远程接口查审批人 String approver = lookupApproverByDept(deptId); delegateTask.setAssignee(approver); // 同时添加候选组,方便转办 delegateTask.addCandidateGroup("dept_" + deptId); } }逻辑说明:DelegateTask提供了getVariable、setAssignee、addCandidateGroup等方法,可以在任务创建、分配、完成等事件节点触发。监听器类需要在 BPMN XML 里注册:
<userTask id="userTask1" name="部门审批" activiti:assignee="${approver}"> <extensionElements> <activiti:taskListener event="create" class="com.example.AssigneeTaskListener" /> </extensionElements> </userTask>参数上,event可以是create、assignment、complete、delete。注意监听器里不要做耗时操作,因为它在事务里同步执行,查数据库太慢会拖长整个流程的响应时间。我一般把远程调用放到异步服务任务里,监听器只做本地变量赋值。
4. Spring 集成与 REST API:把流程引擎嵌进现有系统
4.1 ProcessEngineFactoryBean 与事务管理器的绑定
文档第 5 章讲 Spring 集成,核心是ProcessEngineFactoryBean。它把ProcessEngineConfiguration包装成 Spring Bean,让流程引擎的创建和销毁交给容器管理。配置类里最关键的是SpringProcessEngineConfiguration,它比独立模式多了一个transactionManager属性。
@Configuration public class ActivitiConfig { @Bean public ProcessEngineFactoryBean processEngine(DataSource dataSource, PlatformTransactionManager txManager) { SpringProcessEngineConfiguration cfg = new SpringProcessEngineConfiguration(); cfg.setDataSource(dataSource); cfg.setTransactionManager(txManager); cfg.setDatabaseSchemaUpdate("true"); cfg.setAsyncExecutorActivate(true); // 6.0.0 新增,激活 Job Executor ProcessEngineFactoryBean factoryBean = new ProcessEngineFactoryBean(); factoryBean.setProcessEngineConfiguration(cfg); return factoryBean; } }逻辑说明:setAsyncExecutorActivate(true)是 6.0.0 和 5.x 最大的区别之一。5.x 的 Job Executor 默认关闭,6.0.0 如果不显式激活,所有async="true"的服务任务都不会执行,流程会一直卡在ACT_RU_JOB表里。transactionManager必须和dataSource用同一个事务管理器,否则流程操作和业务操作不在一个事务里,回滚会出问题。
文档 5.7 节的 Spring Boot 集成更简单,加activiti-spring-boot-starter-basic依赖,然后在application.yml里配spring.activiti.database-schema-update: true和spring.activiti.async-executor-activate: true就行。但要注意 Spring Boot 2.x 和 Activiti6 的兼容性,文档 5.7.1 节明确说了只支持 Spring Boot 1.5.x 和 2.0.x,用 2.1 以上版本会报NoSuchMethodError。
4.2 REST API 的分页查询与变量过滤
文档第 13 章的 REST API 是给前端或外部系统调用的。部署后默认路径是/activiti-rest/service/,比如查流程定义列表用GET /repository/process-definitions,启动流程实例用POST /runtime/process-instances。实际用的时候,分页和变量过滤是两个高频需求。
# 分页查询流程定义,按版本倒序 curl -u admin:test "http://localhost:8080/activiti-rest/service/repository/process-definitions?start=0&size=10&sort=version&order=desc" # 按流程变量过滤流程实例 curl -u admin:test -X POST "http://localhost:8080/activiti-rest/service/query/process-instances" \ -H "Content-Type: application/json" \ -d '{ "processDefinitionKey": "leaveProcess", "variables": [ {"name": "applyUser", "value": "zhangsan", "operation": "equals"} ] }'逻辑说明:start和size是分页参数,sort指定排序字段,order是asc或desc。变量过滤的operation支持equals、notEquals、like、greaterThan等,但注意like只对字符串类型有效,数字类型用greaterThan或lessThan。文档 13.1.6 节还提到 JSON 查询变量格式,如果变量是日期类型,值要传 ISO 8601 字符串,否则解析失败。
REST API 默认用 Basic Auth,用户名密码在activiti-rest/WEB-INF/classes/activiti-rest.properties里配。生产环境记得改掉默认的admin/test,并且把application.path从/activiti-rest改成自己的路径,避免和其他服务冲突。
5. 避坑与排查:那些文档没写但一定会遇到的问题
5.1 现象:流程部署后查不到流程定义,ACT_RE_PROCDEF表为空
原因:BPMN 文件里的process标签id属性重复,或者isExecutable="false"。Activiti 部署时不会报错,但会跳过不可执行的流程定义。
解决:检查 BPMN XML 的<process id="xxx" isExecutable="true">,确保id全局唯一。部署后查ACT_RE_PROCDEF表的KEY_和VERSION_字段,确认版本号递增。
5.2 现象:异步服务任务不执行,ACT_RU_JOB表里堆了一堆记录
原因:asyncExecutorActivate没设成true,或者 Job Executor 的线程池被占满。
解决:Spring 配置里加cfg.setAsyncExecutorActivate(true),Spring Boot 里加spring.activiti.async-executor-activate: true。如果已经开了还不执行,查ACT_RU_JOB的RETRIES_字段,如果小于 1 说明重试次数耗尽,需要手动重置或查日志找根因。
5.3 现象:排他网关报No outgoing sequence flow found
原因:所有条件表达式都不满足,且没有配置默认流。
解决:在排他网关上加default="flowId"属性,指向一条无条件流。或者检查变量类型,比如${amount > 5000}里amount是字符串"6000",比较时会按字典序而不是数值,导致条件判断错误。用${Integer.parseInt(amount) > 5000}强制转换。
5.4 现象:历史表数据量暴涨,查询越来越慢
原因:历史记录级别设成了full,每个流程实例的变量、任务、活动都记明细。
解决:文档 3.12 节和历史配置章节提到,historyLevel可以设成none、activity、audit、full。生产环境一般用audit,只记流程实例和任务,不记变量明细。在ProcessEngineConfiguration里设setHistoryLevel(HistoryLevel.AUDIT),已经跑起来的库改配置后只对新数据生效,老数据需要手动归档。
5.5 现象:Spring Boot 启动报Cannot determine embedded database driver class
原因:没配数据源,Spring Boot 自动配置想用内嵌 H2 但找不到驱动。
解决:加 H2 依赖,或者在application.yml里显式配spring.datasource.url、username、password。如果用的是 MySQL,驱动类名是com.mysql.cj.jdbc.Driver,URL 后面加?useSSL=false&serverTimezone=UTC,否则时区不对会导致流程时间戳差 8 小时。
6. 进阶技巧:用历史查询做流程审计与性能调优
文档第 11 章的历史查询是很多人忽略的部分,但它在实际运维里价值很高。比如HistoricProcessInstanceQuery可以按开始时间、结束时间、持续时间过滤,用来找出耗时最长的流程实例;HistoricTaskInstanceQuery可以按任务负责人和完成时间统计每个人的工作量。
// 查询最近 7 天超时未完成的流程实例 List<HistoricProcessInstance> overdueList = historyService .createHistoricProcessInstanceQuery() .startedAfter(DateUtils.addDays(new Date(), -7)) .unfinished() .orderByProcessInstanceDuration().desc() .listPage(0, 20); for (HistoricProcessInstance hpi : overdueList) { System.out.println("流程实例:" + hpi.getId() + ",定义:" + hpi.getProcessDefinitionKey() + ",已耗时:" + hpi.getDurationInMillis() / 1000 / 60 + " 分钟"); }逻辑说明:unfinished()过滤未结束的实例,orderByProcessInstanceDuration().desc()按持续时间倒序,listPage(0, 20)取前 20 条。getDurationInMillis()返回毫秒数,除以 60000 得到分钟。这个查询在ACT_HI_PROCINST表上执行,如果数据量大,建议在START_TIME_和END_TIME_字段上加索引。
另一个技巧是用HistoricVariableInstanceQuery查某个变量的历史值变化。比如审批金额被修改过几次,每次的值和修改时间都能查到。文档 11.1.2 节给了 API 签名,但没提性能问题:ACT_HI_VARINST表默认没有变量名索引,如果变量多、查询频繁,手动加一个NAME_字段的索引能把查询从秒级降到毫秒级。
我自己的习惯是:每次上线新流程前,先用历史查询跑一遍测试数据,确认ACT_HI_ACTINST表里没有异常跳转,再检查ACT_RU_JOB表没有堆积的异步任务。这套检查走完,基本能避开 80% 的线上问题。从那以后我每次部署新流程定义,都强制走一遍历史查询验证,再也没出现过流程卡死到第二天才发现的情况。希望帮到你。
本文还有配套的精品资源,点击获取