别再只会画流程图了!Flowable设计器里任务监听器和多实例的高级玩法详解
Flowable高阶实战:任务监听器与多实例的深度应用
当你在Flowable流程设计器中熟练地拖拽节点、连接线条时,是否曾思考过如何让静态的流程图具备动态智能?本文将带你突破基础流程设计的局限,探索任务监听器和多实例这两项高阶特性的实战应用场景。
1. 任务监听器的动态魔法
任务监听器是Flowable中最强大的扩展点之一,它允许我们在任务生命周期的关键节点注入自定义逻辑。不同于固定分配审批人的简单方式,监听器能实现真正的动态流程控制。
1.1 四大事件类型解析
Flowable提供了四种核心监听事件,每种都对应特定的业务场景:
create:任务创建时触发
public class DynamicAssigneeListener implements TaskListener { @Override public void notify(DelegateTask task) { String department = (String) task.getVariable("dept"); String assignee = userService.findDeptManager(department); task.setAssignee(assignee); } }提示:此监听器适合需要根据运行时数据动态指定处理人的场景,如跨部门协作流程
assignment:任务被签收时执行
public class AssignmentAlertListener implements TaskListener { @Override public void notify(DelegateTask task) { String message = "任务"+task.getName()+"已被"+task.getAssignee()+"签收"; notificationService.send(message); } }complete:任务完成时触发业务逻辑
public class PostTaskActionListener implements TaskListener { @Override public void notify(DelegateTask task) { if("财务审批".equals(task.getName())){ accountingService.createVoucher( task.getVariable("amount"), task.getVariable("voucherType") ); } } }delete:任务删除前执行清理操作
1.2 实战配置技巧
在设计器中配置监听器时,有几个关键细节需要注意:
类路径配置:
- 确保监听器类位于项目的classpath中
- 使用全限定类名(包括包名)
变量访问:
// 获取流程变量 Object value = task.getVariable("varName"); // 设置流程变量 task.setVariable("result", approvalResult);异常处理:
- 监听器中抛出异常会导致流程中断
- 建议在监听器内部捕获处理业务异常
性能考量:
- 避免在监听器中执行耗时操作
- 对于需要长时间处理的任务,建议使用ServiceTask
2. 多实例模式的进阶应用
多实例特性允许单个节点在运行时生成多个任务实例,这为会签、循环审批等复杂场景提供了优雅的解决方案。
2.1 串行与并行模式对比
| 特性 | 串行(Sequential) | 并行(Parallel) |
|---|---|---|
| 执行方式 | 顺序执行 | 同时执行 |
| 适用场景 | 层级审批 | 部门会签 |
| 资源消耗 | 较低 | 较高 |
| 完成条件 | 所有实例完成 | 所有实例完成 |
| 变量隔离 | 支持 | 支持 |
2.2 动态基数配置
多实例的基数(循环次数)可以通过表达式动态确定:
<userTask id="multiInstanceTask" name="部门会签"> <multiInstanceLoopCharacteristics isSequential="false"> <loopCardinality>${deptCount}</loopCardinality> </multiInstanceLoopCharacteristics> </userTask>更复杂的场景可以使用集合变量:
<multiInstanceLoopCharacteristics isSequential="false" flowable:collection="${approvers}" flowable:elementVariable="approver"> </multiInstanceLoopCharacteristics>对应的Java代码设置变量:
List<String> approvers = Arrays.asList("user1","user2","user3"); runtimeService.setVariable(processInstanceId, "approvers", approvers);2.3 完成条件定制
默认情况下,所有实例完成后多实例节点才会继续流转。我们可以通过completionCondition定制完成规则:
<multiInstanceLoopCharacteristics> <completionCondition>${nrOfCompletedInstances/nrOfInstances >= 0.6}</completionCondition> </multiInstanceLoopCharacteristics>这个配置表示当60%的实例完成时即可继续流程,适合"多数同意"的业务场景。
3. 组合应用实战案例
让我们通过一个采购审批流程,展示如何组合使用任务监听器和多实例。
3.1 场景需求
- 采购金额≤1万:部门经理审批
- 1万<金额≤5万:部门会签(3个相关科室)
- 金额>5万:需要财务总监和总经理串行审批
3.2 流程实现
节点设计:
- 开始节点:提交采购申请
- 网关:根据金额路由
- 多实例会签任务(并行)
- 串行审批任务
- 结束节点
关键配置:
会签节点的监听器配置:
public class DeptAuditorListener implements TaskListener { @Override public void notify(DelegateTask task) { String deptId = (String) task.getVariable("applyDept"); List<String> auditors = deptService.getAuditors(deptId); task.setVariable("auditors", auditors); } }XML配置片段:
<userTask id="deptAudit" name="部门会签"> <extensionElements> <flowable:taskListener event="create" class="com.example.DeptAuditorListener"/> </extensionElements> <multiInstanceLoopCharacteristics isSequential="false" flowable:collection="${auditors}" flowable:elementVariable="auditor"> <completionCondition>${nrOfCompletedInstances >= 2}</completionCondition> </multiInstanceLoopCharacteristics> </userTask>4. 性能优化与调试技巧
当流程复杂度增加时,需要特别注意性能问题和调试方法。
4.1 性能优化建议
监听器优化:
- 避免在监听器中执行数据库长事务
- 考虑使用异步监听器
多实例优化:
<multiInstanceLoopCharacteristics isSequential="false" flowable:async="true" flowable:exclusive="false"> </multiInstanceLoopCharacteristics>批量操作:
// 批量完成任务 List<Task> tasks = taskService.createTaskQuery() .processInstanceId(processInstanceId) .list(); for(Task task : tasks){ taskService.complete(task.getId()); }
4.2 调试技巧
历史记录查询:
HistoricActivityInstanceQuery query = historyService .createHistoricActivityInstanceQuery() .processInstanceId(processInstanceId);变量跟踪:
Map<String, Object> variables = runtimeService.getVariables(executionId);事件日志:
SELECT * FROM ACT_RU_EVENT_SUBSCR WHERE PROC_INST_ID_ = '流程实例ID';可视化跟踪:
ProcessDiagramGenerator diagramGenerator = processEngine.getProcessEngineConfiguration() .getProcessDiagramGenerator(); InputStream diagram = diagramGenerator.generateDiagram( bpmnModel, "png", runtimeService.getActiveActivityIds(processInstanceId) );
