这两年收到的Java开源工作流平台项目源码包特别多,从Gitee到GitHub,从Activiti二次封装到自研引擎,五花八门。但很多朋友拿到源码后的第一反应都是懵的——几十个模块、几百张数据表、一堆看不懂的XML流程定义文件,根本不知道从哪下手。
我自己也接过不少这类项目的二次开发,带过团队把一套开源工作流平台从部署跑到上线,期间踩过的坑不算少。今天这篇就围绕一个典型的Java开源工作流平台(含源码、含后端源码)来聊,分几个层面讲清楚:这类平台到底解决了什么问题、市面上主流的引擎怎么选、拿到源码之后怎么快速跑起来、后端源码结构怎么看、二次开发最常改哪些位置,以及面试里经常被问到的几个相关考点。
1. 没有工作流引擎时,审批系统是怎么被if else逼疯的
先说一个最核心的问题:工作流平台到底在解决什么。
很多刚接触这个领域的人会把工作流引擎等同于“状态机”或者“审批流”,这个理解不能算错,但格局小了。拿最常见的请假审批场景来说,没有工作流引擎的时候,你写代码是这样:
java复制if (leaveDays <= 3) {
// 直接主管审批
if (managerApproved) {
status = APPROVED;
} else {
status = REJECTED;
}
} else if (leaveDays <= 10) {
// 主管审批 + 部门经理审批
...
} else {
// 还需要副总审批
...
}
这种写法在流程分支固定、节点不多的时候完全没问题。但现实中的业务流程远没有这么简单:同一个流程在不同部门走不同分支、某一步需要会签、审批人可以驳回上一步、流程走到一半要加签、组织架构调整后审批链跟着变。
把以上需求全部用if else硬编码在业务代码里,最终结果就是:每次流程改动都要改代码、重新发布,测试一轮回归下来人直接裂开。我见过一个遗留系统,光一个采购审批的状态字段就有二十几个取值,判断逻辑散落在十几个Service方法里,任何人接手都是一种精神折磨。
工作流平台做的事情,本质上是把“流程怎么走”这件事从业务代码里抽离出来,变成一份可以独立设计、独立维护的流程定义文件,用专门引擎去解释执行。这样业务代码只关心“每个节点要做什么事”,而不关心“下一步该轮到谁”。
一个完整的开源工作流平台通常包含这么几块:
- 流程设计器:可视化拖拽,画流程图,导出BPMN2.0标准XML文件。
- 流程引擎核心:解析流程定义、推进流程节点、分配任务、处理条件分支,是整个后端源码最重的部分。
- 流程实例管理:流程发起后实例状态的跟踪、挂起、终止、恢复。
- 任务中心:待办、已办、抄送、我的发起这四大金刚。
- 历史归档:流程流转记录、操作日志、耗时分析,是后续统计优化和审计的底子。
- 扩展集成层:对接组织架构(用户、角色、部门)、表单系统(动态表单绑定)、消息通知。
所以你在市面上看到的Java开源工作流项目,标着“含源码、含后端源码”的,基本就是以上模块的一个整合体,后端源码的价值集中在引擎核心、流程管理API和扩展接口封装这三块。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. Activiti、Flowable、Camunda三巨头选型:别被开源冲昏头
搞Java开源工作流,绕不开三个名字:Activiti、Flowable、Camunda。绝大多数的开源工作流平台项目都是在这三个引擎的基础上做二次封装。拿到一个源码包,先看它底层用的哪个引擎、什么版本,这一步直接决定你后续改造成本和坑的数量。
| 维度 | Activiti | Flowable | Camunda |
|---|---|---|---|
| 起源 | 2010年从JBoss jBPM分离 | Activiti 5.x分支后独立 | 源自Activiti早期团队 |
| 主流版本 | 5.x、6.x、7.x | 6.x、7.x | 7.x、8.x |
| 社区活跃度 | 一般 | 较高 | 高 |
| 云原生支持 | 一般 | 较好 | 极好(Zeebe) |
| BPMN2.0支持 | 完整 | 完整 | 完整 |
| 运维监控 | 基础 | 基础 | Cockpit非常强 |
| 国内资料 | 非常多 | 多 | 中等 |
| 适合场景 | 传统单体应用、快速上手 | 复杂流程、嵌入式集成 | 微服务、高并发、大规模流程调度 |
先说Activiti。这个在国内的普及率极高,很多老项目用的是Activiti 5.x和6.x。优点就是资料真多,搜一个问题能出来一堆博客;缺点是版本历史包袱重,5.x到6.x之间的API变化很多人没跟上,二次开发时网上抄来的代码经常和实际版本对不上。现在阿里等大厂开源的很多平台,底层还能看到Activiti的影子。
Flowable是从Activiti 5.x分叉出去的,跟Activiti 6/7走了完全不同的路线。Flowable在流程引擎的完整度、扩展性、以及CMMN(案例管理)支持上都做得很扎实。企业在做复杂业务流程编排的时候,Flowable的扩展接口设计比Activiti清晰不少。国内不少商业化的低代码平台,底层其实都是基于Flowable在做。
Camunda最大的亮点在运维监控,Cockpit可以把运行中的流程实例、任务分布、节点耗时可视化得明明白白。排查生产环境流程卡住的问题时,Camunda的体验是碾压级的好。但代价是学习曲线陡一些,尤其是涉及它的平台架构时。
回到开源工作流平台这个主题,我拿到源码后通常先做三件事:
- 确认底层引擎:看pom.xml里的依赖,或者源码里的
org.activiti、org.flowable、org.camunda包路径。 - 确认版本:是Activiti 6还是7,是Flowable 6还是7,引擎版本直接决定VACATION的API写法。
- 确认数据库脚本:看有没有对应版本的建表SQL,不同版本的数据表结构和字段差异还挺大。
提示:如果一个开源项目说自己是“自研工作流引擎”,没有基于三巨头,那就要格外谨慎地评估。市面上大多数标榜自研的最后还是绕不开BPMN解析和流程推进这些核心难题,不太可能从零搞得比社区积累十几年的引擎更好。自己写个简单的状态机当审批流没问题,但离“工作流平台”这四个字还差得远。
从实际项目经验来看,如果你是中小企业做内部OA系统,或者要做流程平台给产品接入,选Flowable或者Activiti 6/7都稳;如果团队对运维可视化要求高、流程规模大、以后可能拆微服务,Camunda更值得投入。
3. 把开源源码跑起来:从JDK环境变量到后端启动成功
拿到源码包,第一关永远是“怎么把它跑起来”。根据我最近带团队部署一套Flowable二次封装开源平台的经验,这里面的坑比想象中多。下面按顺序拆解。
3.1 环境准备阶段,最容易栽跟头
先说JDK。大多数开源工作流平台的源码是用JDK 8或JDK 11写的,后端源码要求Java 8是主流。很多新手在“java环境变量配置”这一步就卡住了:明明装了JDK,java -version也正常,但javac就是找不到。
这里有一个关键点:只配置了JAVA_HOME但没把%JAVA_HOME%\bin加进Path,或者Path里配置的顺序有问题。
我的做法是这样的:
code复制JAVA_HOME = D:\Java\jdk1.8.0_202
Path 新增一行 = %JAVA_HOME%\bin
配置完必须新开一个CMD窗口再验证,旧的窗口不会刷新环境变量。然后同时执行:
bash复制java -version
javac -version
mvn -version
三个命令都正常,才说明Java和Maven环境没有问题。
再说Maven仓库。从国内拉依赖,不配阿里云镜像会等到怀疑人生。在~/.m2/settings.xml里加:
xml复制<mirror>
<id>aliyunmaven</id>
<mirrorOf>central</mirrorOf>
<name>阿里云公共仓库</name>
<url>https://maven.aliyun.com/repository/public</url>
</mirror>
很多工程里的依赖还涉及Activiti/Flowable的专属仓库,如果源码的pom.xml里配了flowable-repo之类的仓库地址,就原样保留,别乱删。
3.2 数据库初始化,别上来就执行全部脚本
开源工作流的后端源码通常依赖MySQL,也可能支持Oracle、PostgreSQL。我不建议一上来就执行项目里sql目录下的全部脚本。正确姿势是先看项目的readme或者application.yml里配的数据库名,然后只初始化对应的库和脚本。
这是我见过最典型的失误:有人把项目里所有SQL脚本不分顺序全执行了一遍,结果表结构冲突、外键缺失、甚至数据库都崩了。工作流平台和普通CRUD项目不同,它既有业务表,又有引擎自己的ACT_开头的表(如果是Activiti/Flowable)。
以Flowable为例,引擎表分几类:
ACT_RE_*:流程定义、流程模型等静态资源。ACT_RU_*:运行时数据,待办任务、执行实例、变量。ACT_HI_*:历史数据,历史任务、历史流程实例。ACT_GE_*:通用数据,字节数组、属性。
初始化完成后,建议用数据库连接工具先确认表数量。正常一个Flowable 6.x引擎初始化后有70张左右的表。如果表少了很多,基本就是脚本执行不全,直接启动后端大概率报错。
3.3 后端启动,一次把“启动失败”的锅全背全
源码跑起来最常见的就是启动失败,重点排查这五个位置:
第一,端口冲突。application.yml里默认的8080端口被占用了。后端的启动失败日志里如果出现Port already in use,别去代码里找问题,改端口就行:
yaml复制server:
port: 8081
第二,数据库连接信息不对。注意检查url、username、password这三个,尤其是MySQL 8和MySQL 5的驱动类不一样。MySQL 8要用com.mysql.cj.jdbc.Driver,URL里建议带上useSSL=false&serverTimezone=Asia/Shanghai,否则时区和SSL校验都可能导致连接失败。
第三,Redis连接失败。很多工作流平台会拿Redis做缓存和分布式锁,在源码里看报错时,遇到Cannot connect to Redis或者类似提示,要么本地起一个Redis,要么把配置改到可用的测试环境。倒也不必为了跑起来专门搭一套完整的Redis集群,单机就够了。
第四,Lombok的问题。源码大量使用Lombok简化实体代码,你的IDE如果不支持或者Lombok版本和JDK版本不匹配,就会出现一个很典型的报错:
code复制java: You aren't using a compiler supported by lombok, so lombok will not work with your project.
这个问题的本质是Lombok版本和JDK版本不匹配。JDK 8用1.18.x没问题;JDK 17以上要升到较新的1.18.30+,否则编译直接失败。解决方式:在IDEA里装上Lombok插件,并确认pom.xml里Lombok依赖的版本够新。
第五,NoClassDefFoundError这类错误。如果你在日志里看到类似java.lang.NoClassDefFoundError: java/applet/Applet这种,多半是依赖冲突或者JDK模块化裁剪导致加载不到类。工作流平台依赖树很深,Activiti/Flowable和Spring Boot的版本匹配是个精细活。遇到这种问题,别急着改代码,先看Maven依赖树:
bash复制mvn dependency:tree -Dverbose > dep.txt
然后找出重复的、版本冲突的依赖做排除。工作流相关的问题,十有八九都是依赖冲突。
3.4 验证跑通的标准
后端启动成功后,访问项目的登录页,用源码里自带的默认账号登录。不同的平台默认账号不一样,常见的有admin/admin123、admin/123456。登录进去以后,至少要验证三件事:
- 能新建一个流程定义,或者能看到示例流程的流程图。
- 能发起一个流程实例,流程能走通到第一个审批节点。
- 对应审批人的待办里能看到这条任务。
这三件事通过,才算真正“把项目跑起来了”。
4. 源码目录里的门道:后端核心模块到底在干什么
把项目跑起来只是开始,二次开发之前必须搞懂源码结构。Java开源工作流平台的代码量不小,但模块划分基本有规律可循。以我最近看的这个项目为例(基于Flowable引擎做的BPM平台),它的后端源码结构大致是这样的:
code复制bpm-platform
├── bpm-common // 通用工具类、常量、异常定义
├── bpm-model // 实体类、DTO、VO
├── bpm-service // 业务逻辑层:流程管理、任务管理、表单管理
├── bpm-engine // 流程引擎扩展层:Flowable引擎封装、监听器、事件处理
├── bpm-api // 对外接口层:给前端或其他系统调用的RESTful API
├── bpm-plugins // 插件扩展:消息通知、第三方系统对接
├── bpm-web // 启动模块,放application.yml、启动类
每个模块都有自己不可替代的定位,二次开发时改动最多的通常是bpm-service和bpm-engine。
4.1 Service层是业务逻辑的核心战场
bpm-service层做的事,概括起来就是:把流程引擎的低级API包装成业务语义明确的高级接口。比如,同样是“发起一个请假流程”,直接调Flowable引擎你得分三步走:
- 按照流程定义key启动流程实例(ProcessInstance)。
- 给流程实例设置业务单据编号。
- 把发起人信息作为流程变量塞进去。
而封装后的Service接口大概是:
java复制public interface IProcessService {
// 发起流程
ProcessInstance startProcess(String processDefinitionKey,
String businessKey,
Map<String, Object> variables);
// 审批通过并流转
void approve(String taskId, String userId, Map<String, Object> variables);
// 驳回上一节点
void reject(String taskId, String userId, String comment);
// 我的待办列表
PageResult<TodoTaskVO> myTodo(String userId, int page, int size);
}
看源码时重点看这个层的接口设计。一个成熟的工作流平台的Service层,已经把“流程定义部署、流程实例启动、任务查询、任务审批、驳回、会签、加签、转办、委派、终止”等常用操作都覆盖了。你二次开发时大多数需求,都只是在这个层加方法或者改实现逻辑。
4.2 引擎扩展层是区分项目水平的分水岭
好的开源工作流平台不会把Flowable/Activiti的API直接暴露给上层,而是在bpm-engine模块里做了深度封装。封装的价值主要体现在三个地方。
第一是全局事件监听。直接在Flowable引擎上注册监听器,监听什么?任务创建、任务完成、流程结束这些关键节点。有的事件监听用于数据同步(流程结束后更新业务单据状态),有的事件监听用于消息通知(任务创建后给审批人发消息)。
看这部分源码时,你会发现一个细节:监听器里如果抛出了异常,整个流程推进都会被中断。所以代码里一定会做try-catch,保证非核心逻辑(比如通知失败)不会影响流程主链路。
第二是流程变量的序列化策略。Flowable的流程变量存储涉及Java对象的序列化,很多团队在这块翻车。具体表现是:流程变量里存了个自定义对象,从引擎里取出来时直接ClassCastException。原因是Flowable默认序列化方式是Java内置序列化,对象的类名和serialVersionUID一旦变化,反序列化就炸。
这里就涉及到热搜词里那个典型问题——java中redis使用redistemplate的increment()报错不是integer or out of range。它看起来是Redis用法问题,本质上和流程变量的序列化问题是同一个祖宗:存储层的数据类型和读取时的类型转换对不上。工作流平台里如果同时用了Redis缓存流程实例状态,那么RedisTemplate的序列化器配置(Jackson还是JDK序列化)直接决定了你会不会踩类似的坑。
建议看到RedisTemplate配置的地方,重点检查是不是用的GenericJackson2JsonRedisSerializer,以及缓存对象的类有没有无参构造方法。这两个点能避开后面至少一晚上的排查时间。
第三是流程图的服务端渲染。给前端提供流程图高亮展示的能力,本质上就是解析BPMN XML,根据当前流程实例的位置,把节点和连线的状态算出来,以JSON结构返回给前端。看这块源码能学到不少BPMN模型解析的技巧,比如怎么遍历BpmnModel找到某个用户任务节点,怎么判断两个节点之间是否存在连线。
4.3 Controller层和API设计
工作流平台的RESTful API设计,通常遵循一套固定的资源规划逻辑:
/process-definitions:流程定义管理。/process-instances:流程实例管理。/tasks:任务操作。/forms:表单绑定与数据管理。/histories:历史流程查询。
看Controller层源码,除了CRUD之外,最值得关注的是权限控制是怎么做的。一个安全合规的工作流平台,不可能让人随便调接口审批他人任务,所以Controller层一定会校验“当前登录用户是不是这个任务的候选人”。
这个在源码里通常体现为一段类似这样的代码:
java复制@PostMapping("/approve")
public Result<Void> approve(@RequestBody ApproveRequest request) {
String currentUserId = SecurityUtils.getCurrentUserId();
taskService.approve(request.getTaskId(), currentUserId, request.getComment());
return Result.success();
}
Service里面会去查任务的Assignee和Candidate,匹配不上直接抛业务异常。看源码的时候注意这个校验放在哪一层、什么顺序执行,这能帮你理解整个平台的权限设计思路。
4.4 数据库表设计,藏着平台的设计思想
工作流平台除了引擎自带的ACT表,平台自身的业务表也很有讲究。核心的几张是:
bpm_process_definition_info:流程定义的扩展信息,包括图标、分类、版本号。bpm_process_instance_info:流程实例与业务单据的关联关系。bpm_task_info:任务扩展表,比如任务的紧急程度、截止时间。bpm_form_data:动态表单数据。
这类扩展表的设计思路统一且清晰:引擎表只用标准字段,自定义业务字段全部放扩展表里,通过业务主键把两张表关联起来。这样的好处是:即使底层引擎升级、表结构变化,自己的扩展表基本不用动。
5. 二次开发必动的几个位置:表单、会签、权限、驳回
跑通了源码,看懂了结构,接下来就是真实业务接入。根据我的实践经验,这块有几个改动频率极高的位置,提前掌握能给你节省大量时间。
5.1 动态表单绑定:工作流和业务数据之间的桥梁
纯工作流引擎不管表单,只管流程流转。但在实际系统里,每个审批节点都要看到表单数据,而且表单字段可能随流程版本变化。所以开源工作流平台基本都会实现动态表单方案。
典型的做法是:流程定义关联一个表单定义(JSON Schema格式),表单数据存到独立表中。发起流程时保存表单数据,审批页面动态渲染表单。
源码里表单相关的核心表设计大概是:
sql复制CREATE TABLE bpm_form_definition (
id BIGINT PRIMARY KEY,
form_key VARCHAR(64) UNIQUE,
form_name VARCHAR(255),
form_json TEXT, -- JSON Schema定义
status TINYINT,
create_time DATETIME
);
CREATE TABLE bpm_form_data (
id BIGINT PRIMARY KEY,
process_instance_id VARCHAR(64),
form_key VARCHAR(64),
form_data_json TEXT, -- 表单提交的数据
create_time DATETIME
);
二次开发时最常见的需求是:表单数据要回填到业务系统的订单表、报销单表。这种场景我的做法是在流程完成事件监听器里,把bpm_form_data的数据解析出来,再调用业务系统的Service完成数据落地。注意监听器中做数据同步时考虑事务边界,避免通知类异常影响主流程。
5.2 会签和或签:网上一堆答案,但源码里的实现才靠谱
会签(多个审批人必须都通过,流程才往下走)和或签(任一审批人通过即可)是工作流平台二次开发里被问烂了的需求。
在Flowable/Activiti中,这个特性是通过BPMN的**多实例(Multi-Instance)**来实现的,有两种情况:
- 会签:
loopCardinality或collection定义审批人集合,completionCondition设置为nrOfCompletedInstances == nrOfInstances,也就是全部通过才继续。 - 或签:
completionCondition设置为nrOfCompletedInstances >= 1,也就是有一个通过就继续。
看源码时重点看平台有没有封装多实例的任务分配,以及每个实例的审批结果变量(比如approveFlag)是怎么收集、怎么计算的。
这块很容易踩坑的地方在于:多实例任务会对每个审批人生成一个独立的任务记录,你在“我的待办”里需要根据executionId和multiInstance的序号做去重。如果没有处理好,列表里会出现重复数据。
5.3 驳回和任意跳转:需要你把“为什么”想清楚
驳回是另一个高频定制点。Flowable原生自带的驳回能力比较基础,所以在源码平台里,驳回基本都做了深度定制。
一个成熟的驳回逻辑,至少要支持三种:
- 驳回到上一节点:这是最基础的需求,直接调用
taskService.getTask()拿当前任务,找到上一个节点,用runtimeService.createChangeActivityStateBuilder()做节点跳转。 - 驳回到指定节点:前端传一个目标节点ID,后端做校验后跳转。校验逻辑要处理一个关键问题:如果目标节点和当前节点之间有多条分支,要防止死循环。
- 驳回到发起人:一般用在最终审核不通过时,直接退回到流程发起人节点,附带退回意见。
源码里看到changeActivityState或者moveActivityIdTo相关的一段代码,那基本就是驳回和任意跳转的入口。这部分的两个关键坑项:一是流程变量的历史版本管理,驳回后修改表单数据要生成新版本,不能覆盖历史;二是驳回后的待办任务要清理干净,避免旧的待办任务还挂在用户名下。
5.4 权限体系:授权中心和工作流的接口对接
很多开源平台把用户、角色、部门放在独立的权限中心里,工作流引擎再拉取这些数据来解析“这个节点的审批人是谁”。对接方式一般有两种:
第一种是每个流程节点配置里直接指定角色或用户ID,简单直接。
第二种是支持表达式方式,比如${assignee == 'manager' ? deptManager : hrManager}类似的动态分配。
看源码时重点关注平台对这两种方式的支持程度。如果平台只支持第一种,那你接业务的动态审批人需求就需要二次开发。最常见的改法是提供一个AssigneeProvider接口,业务系统自己实现这个接口,返回节点审批人的用户ID。
比如:
java复制public interface AssigneeProvider {
String getAssignee(String businessKey, String nodeId);
}
流程节点配置里不写死审批人,而是配置成调用这个Provider去动态计算。这样业务规则变了,改Provider实现就行,流程不需要改动。
6. 和Java面试强相关的几个工作流提问点
顺带说一个实际场景:很多读者接触这个源码平台是为了应付新项目,但也有不少是为了面试。工作流是Java后端面试里的高频考点,尤其是在应聘中高级岗位的时候。结合我看过的大量题目和候选人实际表现,下面这几个问题很有代表性。
6.1 “你了解BPMN2.0吗?流程引擎是怎么解析的?”
这个问题考察的是底层原理。BPMN2.0本质是一种XML格式的流程描述语言,引擎启动时做下面这些事:
- 读取XML文件,通过SAX或DOM解析成文档树。
- 把XML里的
process元素以及各个节点(startEvent、userTask、serviceTask、endEvent)映射成Java对象。 - 把连线(
sequenceFlow)的sourceRef和targetRef解析成节点间的关系。 - 构建一个流程定义缓存,以后发起新流程实例时直接从缓存中加载,不用反复解析XML。
面试时能把“解析、建缓存、实例化”这三步讲清楚,基本就能证明你真的看过源码,而不只是背过概念。
6.2 “Activiti/Flowable的表结构为什么要分三类?设计意图是什么?”
面试官要听到的回答是:ACT_RE_*存静态定义,ACT_RU_*存运行态数据,ACT_HI_*存历史数据。这样分开设计的核心原因是性能和生命周期管理:
- 运行态数据只保留正在执行的流程实例,流程一旦结束就迁到历史表,保证运行时表的数据量足够小,任务查询速度快。
- 历史数据只增不改,天然适合归档和审计。
这个设计思路其实和很多系统的冷热数据分离是一个道理。能把这个“为什么”讲透,比背表名加分得多。
6.3 “像Redis、Mq这类组件的异常是否会影响主流程?你怎么处理?”
工作流平台里,流程引擎是“主链路”,Redis缓存、消息通知这类都是“辅助链路”。如果处理不当,Redis宕机可能导致整个流程无法推进。
工作流的监听器设计有一个核心原则:辅助链路的异常绝不能影响主链路的正确性。代码实现上通常是在监听器里加try-catch,或者使用@Async把通知、日志写入等操作异步化。严重依赖Redis的场景还会做多级缓存降级。
这也是在提醒看源码的朋友:平台里涉及到RedisTemplate、KafkaTemplate等处,多看看它们的降级策略。看懂这块,对你以后设计高可用系统非常有用。
6.4 “让你设计一个轻量级工作流引擎,你怎么做?”
这是开放题,面试官想听的是你的设计思路,而不是背诵源码。我的回答框架是:
- 先定义最小组件:流程定义(节点列表+连线列表)、流程实例(当前节点、流程变量)、任务(节点产生的待办)。
- 再定义核心操作:启动(创建实例,推进到第一个节点)、完成(结束当前任务,根据连线找下一节点)、驳回(节点跳转)。
- 最后考虑持久化方案和并发控制(用数据库行锁或者乐观锁,保证同一时刻只有一个线程在处理同一个流程实例)。
能把这个三层结构讲清楚,比把源码背一遍更让面试官觉得你有架构思维。
7. 部署上线前要确认的几件事
开源工作流平台从“跑起来”到“能上线”,中间还隔着一整套工程化的工作。这块不解决好,代码写得再漂亮也会被打回原形。
7.1 流程定义版本管理
流程线上跑着的时候,流程模板不能随便改。所以平台必须要有流程定义的版本概念。发布新版本的流程时候,老版本已经发起的实例继续按老版本走,新发起的使用新版本。看源码时,重点找“版本号”相关的字段和逻辑,通常是根据ACT_RE_PROCDEF表的VERSION_字段来区分。
7.2 操作审计
工作上流转涉及审批责任,每一步操作都得留痕。反例是有的人把Activiti/Flowable的历史表全给清了,上线后被审计问住非常狼狈。生产环境建议历史数据直接归档到独立库或者数据仓库,至少保留数年。
7.3 性能与慢流程
工作流平台整体性能问题不大,最常见的性能瓶颈是待办列表查询。待办任务表的数据量大了以后,查询条件里的assignee字段必须建索引,businessKey也要建索引。源码里如果没建,二次开发时自己补上。
还有一类慢流程的坑:某个流程实例同时产生了几百个多实例子任务,这种特殊场景下,任务列表页要特别优化。分页查的时候尽量只查任务表,不要一次性把所有流程变量查出来拼接。
7.4 部署形态
单体结构直接打jar包部署就行。如果微服务架构,流程引擎得单独拆成一个服务,其他业务模块通过API或者消息队列来调用。注意分布式事务问题:流程发起是主流程,业务单据状态更新是子流程,两边不在同一个数据库事务里。遇到这种情况,常见做法是流程实例启动后再发送事件,业务系统监听事件做数据同步。
8. 关于“开源工作流平台二次开发”的个人心得
最后分享一点个人经验。从源码项目接手到最终上线,我踩过最大的坑是提交前没有充分评估底层引擎的版本升级问题。很多开源平台在介绍里写得天花乱坠,但底层引擎版本很老,一旦线上出问题想升级很被动。所以选型和源码评估阶段,把引擎版本列为第一优先级考察项,其次是看社区活跃度和issue响应速度。
另外一个经验是,二次开发不要什么都去改引擎核心代码。其实大部分需求都可以通过监听器、流程变量、ServiceImpl扩展这几个安全的扩展点来满足。改引擎源码最致命的问题是:以后想升级引擎会和你的改动产生冲突,等于把自己锁死在老版本上。
还有一点就是关于流行的技术栈补充。现在很多工作流平台已经把Redis、消息队列、甚至微服务都整合进去了,开工前先总体看一下pom.xml,做到对技术栈心中有数。很多人一上来就扑到代码细节里,最后整体架构都没搞明白,遇到问题就抓瞎。
工作流这个领域,看上去难度不大,真正深入进去水挺深的。但只要把源码结构、核心引擎原理和二次开发的扩展点这三个层面吃透,无论是自用还是给公司做流程平台,都会顺手很多。希望这篇基于实际项目经验写的拆解,能给正在折腾Java开源工作流平台的你省下几个通宵排查的夜晚。
