工作流框架中的Listener的用法赏析ExecutionListener和TaskListener org.activiti.engine.delegate.ExecutionListener;这个接口什么时候触发org.activiti.engine.delegate.ExecutionListener是 Activiti 工作流引擎中用于监听执行实例Execution生命周期事件的接口。触发时机该接口在以下执行实例状态变化时触发事件类型触发时机start流程实例或执行分支开始时end流程实例或执行分支结束时take顺序流Sequence Flow被经过时配置方式1. 在 BPMN 流程定义中配置xmlprocess idmyProcess nameMy Process !-- 在流程级别监听(流程全局级别作用范围) -- extensionElements activiti:executionListener eventstart classcom.example.MyExecutionListener / activiti:executionListener eventend classcom.example.MyEndListener / /extensionElements !-- 在节点Activity级别监听 -- userTask idtask1 name审批任务 extensionElements activiti:executionListener eventstart classcom.example.TaskStartListener / activiti:executionListener eventend classcom.example.TaskEndListener / /extensionElements /userTask !-- 在顺序流上监听event 固定为 take -- sequenceFlow idflow1 sourceRefstart targetReftask1 extensionElements activiti:executionListener eventtake classcom.example.FlowListener / /extensionElements /sequenceFlow /process2. 使用表达式或委托表达式xml!-- 使用表达式 -- activiti:executionListener eventstart expression${myBean.onStart(execution)} / !-- 使用 Spring 委托表达式 -- activiti:executionListener eventstart delegateExpression${myExecutionListener} /Java 实现示例javaimport org.activiti.engine.delegate.ExecutionListener; import org.activiti.engine.delegate.DelegateExecution; public class MyExecutionListener implements ExecutionListener { Override public void notify(DelegateExecution execution) throws Exception { String eventName execution.getEventName(); switch (eventName) { case start: System.out.println(流程/节点开始流程实例ID: execution.getProcessInstanceId()); // 可以设置流程变量 execution.setVariable(startTime, new Date()); break; case end: System.out.println(流程/节点结束); break; case take: System.out.println(经过顺序流: execution.getCurrentTransitionId()); break; } } }与 TaskListener 的区别表格特性ExecutionListenerTaskListener监听对象执行实例Execution用户任务User Task事件类型start,end,takecreate,assignment,complete,delete适用场景流程/节点/顺序流级别任务级别如任务创建、分配、完成获取任务信息无法直接获取可以获取DelegateTask 对象用户任务对象典型应用场景流程启动时初始化变量— 在流程start事件中设置业务初始数据节点进入/离开时记录日志— 审计追踪顺序流经过时做条件判断— 动态路由流程结束时发送通知— 在流程end事件中触发邮件/消息自动任务触发— 在 ServiceTask 的start事件中执行业务逻辑注意事项execution参数包含当前执行上下文可通过它获取/设置流程变量、流程实例ID 等监听器中抛出的异常会导致流程执行回滚如果配置在顺序流上event 必须是take不能是start或endTaskListener必须嫁接挂载到UserTask任务类型对象上才能起到作用任务事件监听器TaskListener就是基于UserTask用户任务对象存活的它必须与UserTask绑定它的功能就是监听用户任务UserTask对象在流程中的事件状态所以TaskListener需要绑定到UserTask上去使用这个绑定过程可以在.BPMN流程定义文件中通过标签元素来书写声明式定义————————————————TaskListener是 Activiti/Flowable 工作流引擎中用于监听用户任务User Task生命周期事件的接口。它与ExecutionListener不同专门处理任务级别的状态变化。javapackage org.activiti.engine.delegate; public interface TaskListener { // 事件类型常量 String EVENTNAME_CREATE create; // 任务创建事件 String EVENTNAME_ASSIGNMENT assignment; // 任务分配事件分配给人 String EVENTNAME_COMPLETE complete; // 任务完成事件 String EVENTNAME_DELETE delete; // 任务删除事件 String EVENTNAME_ALL_EVENTS all; // 所有事件部分版本支持 void notify(DelegateTask delegateTask);//事件处理回调方法 }与 ExecutionListener 的核心区别表格特性TaskListenerExecutionListener监听对象用户任务User Task执行实例Execution参数类型DelegateTaskUserTask的顶级父接口DelegateExecutionExecution的顶级父接口事件类型create, assignment, complete, deletestart, end, take配置位置及作用域用户任务节点内流程/节点/顺序流获取任务信息✅ 任务办理人、候选人等❌ 无任务概念设置任务变量✅ 支持✅ 支持使用方式方式一BPMN XML 中配置最常用xml复制userTask idapproveTask name经理审批 extensionElements !-- 任务创建时触发 -- activiti:taskListener eventcreate classcom.example.listener.TaskCreateListener / !-- 任务分配时触发 -- activiti:taskListener eventassignment classcom.example.listener.TaskAssignListener / !-- 任务完成时触发 -- activiti:taskListener eventcomplete classcom.example.listener.TaskCompleteListener / !-- 任务删除时触发 -- activiti:taskListener eventdelete classcom.example.listener.TaskDeleteListener / /extensionElements /userTask方式二使用表达式Expression方式xmluserTask idtask1 name审批 extensionElements !-- 调用 Spring Bean 的方法 -- activiti:taskListener eventcreate expression${taskHandler.onCreate(task)} / !-- 使用委托表达式 -- activiti:taskListener eventcomplete delegateExpression${myTaskListener} / /extensionElements /userTask方式三Java 代码动态添加java// 获取任务服务 TaskService taskService processEngine.getTaskService(); // 查询任务 Task task taskService.createTaskQuery() .taskId(taskId) .singleResult(); // 动态添加监听器较少使用通常静态配置 // 注意Activiti 原生不支持运行时动态添加Flowable工作流框架部分支持完整 Java 实现示例1. 任务创建监听器自动设置候选人/发送通知javaimport org.activiti.engine.delegate.TaskListener; import org.activiti.engine.delegate.DelegateTask; import org.springframework.stereotype.Component; public class TaskCreateListener implements TaskListener { Override public void notify(DelegateTask delegateTask) { // 获取任务信息 String taskId delegateTask.getId(); String taskName delegateTask.getName(); String processInstanceId delegateTask.getProcessInstanceId(); String executionId delegateTask.getExecutionId(); System.out.println(任务创建: taskName [ID taskId ]); // 1. 动态设置候选人如果流程变量中有部门信息 String deptId (String) delegateTask.getVariable(deptId); if (sales.equals(deptId)) { delegateTask.addCandidateGroup(sales_manager); // 添加候选组 } else { delegateTask.addCandidateGroup(general_manager); } // 2. 设置任务到期时间T3天 Calendar cal Calendar.getInstance(); cal.add(Calendar.DAY_OF_MONTH, 3); delegateTask.setDueDate(cal.getTime()); // 3. 设置优先级 delegateTask.setPriority(50); // 4. 发送待办通知调用消息服务 // sendNotification(delegateTask); } }2. 任务分配监听器记录分配日志javapublic class TaskAssignListener implements TaskListener { Override public void notify(DelegateTask delegateTask) { String assignee delegateTask.getAssignee(); String taskName delegateTask.getName(); System.out.println(任务分配: taskName - assignee); // 记录分配历史到自定义表 // 或发送邮件/短信通知办理人 } }3. 任务完成监听器校验/记录javapublic class TaskCompleteListener implements TaskListener { Override public void notify(DelegateTask delegateTask) { String taskId delegateTask.getId(); String assignee delegateTask.getAssignee(); // 获取任务完成时填写的表单数据任务及流程变量的载体 MapString, Object variables delegateTask.getVariables(); String approveResult (String) variables.get(approveResult); System.out.println(任务完成: taskId 由 assignee 完成); System.out.println(审批结果: approveResult); // 1. 校验必填变量 if (approveResult null) { throw new RuntimeException(审批结果不能为空); } // 2. 根据审批结果设置流程变量 if (reject.equals(approveResult)) { delegateTask.setVariable(needReSubmit, true); } // 3. 记录操作日志到审计表 // auditLogService.record(delegateTask); } }4. 任务删除监听器清理工作javapublic class TaskDeleteListener implements TaskListener { Override public void notify(DelegateTask delegateTask) { System.out.println(任务删除: delegateTask.getName()); // 清理相关资源 // 取消定时提醒等 } }事件触发顺序详解假设一个用户任务从创建到完成的完整流程流程到达 UserTask ↓ ExecutionListener start 触发如果有 ↓ TaskListener create 触发 ← 任务实例创建 ↓ TaskListener assignment 触发 ← 设置办理人/候选人时 ↓ 用户完成任务taskService.complete ↓ TaskListener complete 触发 ← 任务完成前 ↓ ExecutionListener end 触发如果有 ↓ 流程继续流转Spring 集成配置推荐Component(myTaskListener) public class SpringTaskListener implements TaskListener { Autowired private NotificationService notificationService; Autowired private AuditLogService auditLogService; Override public void notify(DelegateTask delegateTask) { String eventName delegateTask.getEventName(); switch (eventName) { case create: // 发送待办通知 notificationService.sendTodo(delegateTask); break; case assignment: // 记录分配 auditLogService.logAssign(delegateTask); break; case complete: // 记录完成 auditLogService.logComplete(delegateTask); break; } } }BPMN 中引用xmlactiviti:taskListener eventcreate delegateExpression${myTaskListener} /Flowable 与 Activiti 的差异表格复制特性Activiti 5/6Flowable 6包路径org.activiti...org.flowable...额外事件无timeout任务超时子任务监听有限支持更完善Flowable 的timeout事件示例xml复制flowable:taskListener eventtimeout classcom.example.TimeoutHandler /常见应用场景表格复制场景使用事件实现自动分配办理人create根据业务规则动态设置assignee发送待办邮件create/assignment调用邮件服务记录操作日志complete写入审计表任务超时提醒timeout(Flowable)定时任务处理任务转办记录assignment记录转办历史完成前校验complete校验表单数据完整性DelegateTask API 与监听器一、DelegateTask 完整 API 列表java// 任务基本信息 String getId(); // 任务唯一ID String getName(); // 任务名称 void setName(String name); // 动态修改任务名称 String getDescription(); // 任务描述 void setDescription(String description); // 设置描述 String getAssignee(); // 当前办理人 void setAssignee(String assignee); // 设置办理人直接指派 String getOwner(); // 任务所有者 void setOwner(String owner); // 设置所有者 Date getCreateTime(); // 创建时间 Date getDueDate(); // 到期时间 void setDueDate(Date dueDate); // 设置到期时间 int getPriority(); // 优先级默认50 void setPriority(int priority); // 设置优先级 String getTaskDefinitionKey(); // 任务定义KeyBPMN中的id String getFormKey(); // 表单Key // 候选人/候选组管理 void addCandidateUser(String userId); // 添加候选用户 void addCandidateGroup(String groupId); // 添加候选组 void deleteCandidateUser(String userId); // 删除候选用户 void deleteCandidateGroup(String groupId); // 删除候选组 SetIdentityLink getCandidates(); // 获取所有候选人/组 // 流程关联信息 String getExecutionId(); // 执行实例ID String getProcessInstanceId(); // 流程实例ID String getProcessDefinitionId(); // 流程定义ID String getProcessDefinitionKey(); // 流程定义Key String getEventName(); // 当前触发的事件名create/assignment/complete/delete // 变量操作 Object getVariable(String variableName); T T getVariable(String variableName, ClassT variableClass); MapString, Object getVariables(); // 获取所有流程变量 MapString, Object getVariablesLocal(); // 获取本地变量 void setVariable(String variableName, Object value); void setVariableLocal(String variableName, Object value); // 仅当前任务可见 void setVariables(MapString, Object variables); void setVariablesLocal(MapString, Object variables); boolean hasVariable(String variableName); void removeVariable(String variableName); void removeVariableLocal(String variableName); // 委托/代理相关 DelegationState getDelegationState(); // 委托状态PENDING/RESOLVED void setDelegationState(DelegationState delegationState); // 其他 String getCategory(); // 任务分类 void setCategory(String category); // 设置分类 DelegateExecution getExecution(); // 获取关联的 Execution 对象二、TaskListener 与 ExecutionListener 配合使用触发顺序关键当一个流程到达UserTask节点时事件触发顺序如下流程流转到达 UserTask 节点 │ ▼ ┌─────────────────────────────────────┐ │ ExecutionListener eventstart │ ← 第1触发执行实例进入节点 │ 节点级别的 ExecutionListener │ └─────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────┐ │ TaskListener eventcreate │ ← 第2触发任务实例创建 │ 此时任务已存在但可能未分配人 │ └─────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────┐ │ TaskListener eventassignment │ ← 第3触发设置办理人/候选人时 │ 如果创建时就指定了assignee │ │ 则 create 之后立即触发 assignment│ └─────────────────────────────────────┘ │ ▼ 用户处理任务中... │ ▼ 用户调用 taskService.complete() ┌─────────────────────────────────────┐ │ TaskListener eventcomplete │ ← 第4触发任务完成前 │ 此时任务还未销毁变量可读写 │ └─────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────┐ │ ExecutionListener eventend │ ← 第5触发执行实例离开节点 │ 节点级别的 ExecutionListener │ └─────────────────────────────────────┘ │ ▼ 流程继续向下流转SequenceFlow 的 take 事件完整 BPMN 配合示例xml?xml version1.0 encodingUTF-8? definitions xmlnshttp://www.omg.org/spec/BPMN/20100524/MODEL xmlns:activitihttp://activiti.org/bpmn targetNamespacehttp://example.org/leave process idleaveApproval name请假审批流程 startEvent idstart name开始 / sequenceFlow idflow1 sourceRefstart targetRefapplyTask / !-- 申请人填写 -- userTask idapplyTask name填写请假单 extensionElements activiti:executionListener eventstart classcom.example.listener.ApplyExecutionStartListener / activiti:taskListener eventcreate classcom.example.listener.ApplyTaskCreateListener / activiti:taskListener eventcomplete classcom.example.listener.ApplyTaskCompleteListener / activiti:executionListener eventend classcom.example.listener.ApplyExecutionEndListener / /extensionElements /userTask sequenceFlow idflow2 sourceRefapplyTask targetRefmanagerTask / !-- 经理审批 -- userTask idmanagerTask name经理审批 extensionElements activiti:executionListener eventstart classcom.example.listener.ManagerExecutionStartListener / activiti:taskListener eventcreate classcom.example.listener.ManagerTaskCreateListener / activiti:taskListener eventassignment classcom.example.listener.ManagerTaskAssignListener / activiti:taskListener eventcomplete classcom.example.listener.ManagerTaskCompleteListener / activiti:executionListener eventend classcom.example.listener.ManagerExecutionEndListener / /extensionElements /userTask sequenceFlow idflow3 sourceRefmanagerTask targetRefend / endEvent idend name结束 / /process /definitionsJava 实现各监听器分工协作java// // 1. 节点进入时的 ExecutionListenerstart // 职责初始化节点级上下文设置节点变量 // public class ManagerExecutionStartListener implements ExecutionListener { Override public void notify(DelegateExecution execution) { System.out.println([ExecutionListener:start] 进入经理审批节点); System.out.println( → ExecutionId: execution.getId()); // 设置节点级变量Execution 级别对该执行实例下的所有任务可见 execution.setVariable(enterManagerNodeTime, new Date()); execution.setVariable(currentNode, managerTask); } } // // 2. 任务创建时的 TaskListenercreate // 职责动态设置候选人、到期时间、优先级 // public class ManagerTaskCreateListener implements TaskListener { Override public void notify(DelegateTask delegateTask) { System.out.println([TaskListener:create] 经理审批任务已创建); System.out.println( → TaskId: delegateTask.getId()); // 从流程变量中获取申请人部门 String dept (String) delegateTask.getVariable(applyDept); // 根据部门动态设置候选审批组 if (tech.equals(dept)) { delegateTask.addCandidateGroup(tech_manager); } else { delegateTask.addCandidateGroup(general_manager); } // 设置任务2天内必须处理 Calendar cal Calendar.getInstance(); cal.add(Calendar.DAY_OF_MONTH, 2); delegateTask.setDueDate(cal.getTime()); // 设置高优先级请假审批紧急 delegateTask.setPriority(80); } } // // 3. 任务分配时的 TaskListenerassignment // 职责通知具体的办理人 // public class ManagerTaskAssignListener implements TaskListener { Override public void notify(DelegateTask delegateTask) { String assignee delegateTask.getAssignee(); String taskName delegateTask.getName(); System.out.println([TaskListener:assignment] 任务已分配给: assignee); // 发送邮件/钉钉/企业微信通知 // notificationService.sendToUser(assignee, 您有新的待办任务: taskName); } } // // 4. 任务完成时的 TaskListenercomplete // 职责校验表单、记录审批意见、设置后续变量 // public class ManagerTaskCompleteListener implements TaskListener { Override public void notify(DelegateTask delegateTask) { String assignee delegateTask.getAssignee(); // 获取用户提交的任务变量complete时传入的变量 Boolean approved (Boolean) delegateTask.getVariable(approved); String comment (String) delegateTask.getVariable(comment); System.out.println([TaskListener:complete] assignee 完成任务); System.out.println( → 审批结果: (approved ? 同意 : 拒绝)); System.out.println( → 审批意见: comment); // 校验必须填写审批意见 if (comment null || comment.trim().isEmpty()) { throw new RuntimeException(审批意见不能为空); } // 设置流程变量供后续节点使用 delegateTask.setVariable(managerApproved, approved); delegateTask.setVariable(managerComment, comment); delegateTask.setVariable(managerCompleteTime, new Date()); } } // // 5. 节点离开时的 ExecutionListenerend // 职责清理节点级资源记录节点耗时 // public class ManagerExecutionEndListener implements ExecutionListener { Override public void notify(DelegateExecution execution) { Date enterTime (Date) execution.getVariable(enterManagerNodeTime); long duration System.currentTimeMillis() - enterTime.getTime(); System.out.println([ExecutionListener:end] 离开经理审批节点); System.out.println( → 节点耗时: duration ms); // 记录到审计表 // auditService.recordNodeDuration(execution.getProcessInstanceId(), managerTask, duration); } }控制台输出示例plain复制[ExecutionListener:start] 进入经理审批节点 → ExecutionId: 2501 [TaskListener:create] 经理审批任务已创建 → TaskId: 2504 [TaskListener:assignment] 任务已分配给: zhangsan [TaskListener:complete] zhangsan 完成任务 → 审批结果: 同意 → 审批意见: 同意请假注意交接工作 [ExecutionListener:end] 离开经理审批节点 → 节点耗时: 3600000ms两者配合使用的设计建议表格复制职责使用 ExecutionListener使用 TaskListener节点级变量初始化✅start❌任务候选人/组设置❌✅create任务到期时间/优先级❌✅create发送待办通知给具体人❌✅assignment表单数据校验❌✅complete记录操作日志✅end✅complete节点耗时统计✅startend❌流程变量传递✅✅核心原则与任务办理人相关的操作分配、通知、表单用TaskListener与节点执行逻辑相关的初始化、统计、清理用ExecutionListener。————————————————————篇外话题DelegateTask是一个接口interface不是类。具体定义Activiti5.x / 6.xjava复制package org.activiti.engine.delegate; public interface DelegateTask extends org.activiti.engine.task.Task { // 额外的方法setAssignee, setOwner, setDueDate, setPriority 等 // 以及变量操作相关方法 }Flowable6.xjava复制package org.flowable.engine.delegate; public interface DelegateTask extends org.flowable.task.api.Task { // 类似的方法扩展 }为什么设计成接口引擎内部实现Activiti/Flowable 引擎内部有具体的实现类如TaskEntity但对外暴露的是DelegateTask接口隐藏内部实现细节。解耦你在写TaskListener时只需要面向接口编程javapublic class MyListener implements TaskListener { Override public void notify(DelegateTask delegateTask) { // 参数类型是接口 delegateTask.setAssignee(zhangsan); } }实际运行时引擎传入的是内部实现类的实例。扩展性接口可以方便地在不同版本间扩展而不破坏用户代码。继承关系plain复制Task (接口定义基础getter) ↑ DelegateTask (接口扩展setter和变量操作) ↑ TaskEntity (引擎内部实现类用户不直接使用)所以你在TaskListener中接收到的delegateTask参数实际类型是引擎内部的实现类但你在代码中只能看到接口定义的方法。