Activiti7 监听器


# Activiti7 监听器

# 全局事件监听器(ActivitiEventListener)

# 使用场景

全局事件监听器可以监听整个流程引擎的所有事件,通过配置类全局注册,不需要在流程定义中配置。

常用于全局性的业务处理,如审计日志、统计信息等

# 代码示例

首先创建一个全局事件监听器:

import org.activiti.engine.delegate.event.ActivitiEvent;
import org.activiti.engine.delegate.event.ActivitiEventListener;
import org.activiti.engine.delegate.event.ActivitiEventType;
import org.springframework.stereotype.Component;

@Component
public class GlobalActivitiEventListener implements ActivitiEventListener {
    
    @Autowired
    private YourBusinessService businessService;
    
    @Override
    public void onEvent(ActivitiEvent event) {
        if (event.getType() == ActivitiEventType.TASK_COMPLETED) {
            // 获取任务相关信息
            TaskEntity task = (TaskEntity) ((ActivitiEntityEvent) event).getEntity();
            String taskId = task.getId();
            String processInstanceId = task.getProcessInstanceId();
            String taskName = task.getName();
            
            // 调用业务方法
            businessService.handleTaskCompletion(taskId, processInstanceId, taskName);
        }
    }
    
    @Override
    public boolean isFailOnException() {
        return false;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

在配置类中注册全局事件监听器:

@Configuration
public class ActivitiConfig {
    
    @Bean
    public ProcessEngineConfigurationImpl processEngineConfiguration(GlobalActivitiEventListener eventListener) {
        ProcessEngineConfigurationImpl configuration = new StandaloneProcessEngineConfiguration();
        // ... 其他配置 ...
        
        // 添加全局事件监听器
        configuration.setEventListeners(Collections.singletonList(eventListener));
        
        return configuration;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

这样只需配置一次,不需要修改 BPMN 文件,就可以根据需要灵活地过滤特定任务。

比如监听所有任务节点的完成事件,并对特定任务进行特殊处理,可以在监听器中添加判断逻辑:

@Override
public void onEvent(ActivitiEvent event) {
    if (event.getType() == ActivitiEventType.TASK_COMPLETED) {
        TaskEntity task = (TaskEntity) ((ActivitiEntityEvent) event).getEntity();
        
        // 根据任务定义key进行不同处理
        switch(task.getTaskDefinitionKey()) {
            case "task1":
                businessService.handleTask1Completion(task);
                break;
            case "task2":
                businessService.handleTask2Completion(task);
                break;
            default:
                businessService.handleDefaultTaskCompletion(task);
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 执行监听器(ExecutionListener)

# 使用场景

执行监听器和流程相关,常见业务使用场景:

  • 流程实例相关
    • 流程启动时初始化业务数据
    • 流程结束时更新业务状态
    • 记录流程执行的审计日志
    • 发送流程状态通知
  • 节点执行相关
    • 记录节点执行的时间戳

# 代码示例

在 Activiti 中使用执行监听器最主流的方式是直接实现 ExecutionListener 接口并重写 notify 方法。

notify 方法会在流程实例的以下生命周期事件中被触发:

  • start 事件:
    • 当流程实例、活动(节点)开始时触发
  • end 事件:
    • 当流程实例、活动(节点)结束时触发
  • take 事件:
    • 当流程沿着某条连线流转时触发(当流程从「任务1」完成后,在进入「任务2」之前,正在经过这条连线时触发)

具体的执行时机需要在 BPMN 文件中通过 event 属性指定监听器的事件类型,比如在流程实例启动时触发:

<process id="myProcess">
  <extensionElements>
    <activiti:executionListener event="start"
                                class="com.example.workflow.listener.PiListener">
      <!-- - 通过表达式注入值,这个值会在运行时被解析并注入到监听器实例中 -->
      <activiti:field name="sendType" expression="${someExpression}" />
    </activiti:executionListener>
  </extensionElements>
  <!-- ... -->
</process>
1
2
3
4
5
6
7
8
9
10

相应的监听器代码如下:

package com.example.workflow.listener;

import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.delegate.ExecutionListener;
import org.activiti.engine.delegate.Expression;
import org.springframework.beans.factory.annotation.Autowired;

public class PiListener implements ExecutionListener {
    @Autowired
    // 获取前端页面在 BPMN 中的字段注入
    // "sendType" 这个值保持和前端取得字段名一样,这里表示通知方式(email、sms...)
    private Expression sendType;

    @Override
    public void notify(DelegateExecution execution) {
        // 该方法中的 execution 参数包含了流程执行相关的信息
        System.out.println(execution.getEventName());           // 获取事件名称(start、end等)
        System.out.println(execution.getProcessDefinitionId()); // 获取流程定义ID
        System.out.println(execution.getProcessInstanceId());   // 获取流程实例ID
        System.out.println(execution.getCurrentActivityId());   // 获取当前活动的ID
        System.out.println(execution.getProcessBusinessKey());  // 获取业务关键字
        
        // 统计每个环节(主要是任务)的执行时间
        // 注意,由于上面的 BPMN 只把监听器配置在 <process> 下,因此只在流程实例启动时触发一次
        // 只有节点级别的监听器(配置在具体节点上),才会在节点执行开始和结束时触发
        if("start".equals(execution.getEventName())){
            // 记录节点开始时间(记在业务表里)
        } else if("end".equals(execution.getEventName())){
            // 记录节点结束时间(记在业务表里)
        }
        
        // 获取前端传递过来的 sendType 字段对应的值
        System.out.println("sendType:" + sendType.getValue(execution).toString());
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

# 任务监听器(TaskListener)

# 使用场景

任务监听器和任务相关,常见业务使用场景:

  • 任务分配相关:
    • 动态计算任务处理人
    • 实现任务委派逻辑
  • 任务状态管理:
    • 任务创建时发送通知
    • 任务完成时更新业务数据
    • 记录任务处理时间
    • 监控任务超时情况
  • 任务权限控制:
    • 设置任务的候选人和候选组
    • 控制任务的可见性
    • 实现任务的转办功能
    • 管理任务的优先级

# 代码示例

在 Activiti 中使用任务监听器最主流的方式是直接实现 TaskListener 接口并重写 notify 方法。

notify 方法会在任务的以下生命周期事件中被触发:

  • create 事件:
    • 当任务被创建时触发
    • 此时可以获取到初始的任务信息
    • 适合用于设置初始处理人、发送任务创建通知等
  • assignment 事件:
    • 当任务被分配给某个用户时触发
    • 可以获取到新的处理人信息
    • 适合用于发送任务分配通知、记录分配历史等
  • complete 事件:
    • 当任务完成时触发
    • 可以获取到任务的完成信息
    • 适合用于发送完成通知、更新相关业务状态等
  • delete 事件:
    • 当任务被删除时触发
    • 适合用于清理相关数据、记录删除原因等

具体的执行时机需要在 BPMN 文件中通过 event 属性指定监听器的事件类型,比如在任务创建时触发:

<userTask id="userTask1" name="用户任务1">
    <extensionElements>
        <activiti:taskListener event="create" 
            class="com.example.workflow.listener.TkListener" />
    </extensionElements>
</userTask>
1
2
3
4
5
6

相应的监听器代码如下:

package com.example.workflow.listener;

import org.activiti.engine.delegate.DelegateTask;
import org.activiti.engine.delegate.TaskListener;

public class TkListener implements TaskListener {
    @Override
    public void notify(DelegateTask delegateTask) {
        // 该方法中的 delegateTask 参数包含了当前任务的所有信息,可以通过它获取和设置任务相关的属性和变量
        System.out.println("执行人:" + delegateTask.getAssignee());
        
        // ...
        // 根据用户名查询用户电话并调用发送短信接口
        // ...
        
        // ...
        // 通过一系列业务逻辑计算出当前任务的执行人并动态设置,比如maomao
        delegateTask.setAssignee("maomao");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 主要区别

  • 执行监听器关注流程整体的执行过程和状态变化
  • 任务监听器专注于人工任务节点的生命周期管理
  • 执行监听器更适合处理业务数据和系统集成
  • 任务监听器更适合处理人员分配和任务管理

# 最佳实践

  • 监听器中的逻辑要简单高效
  • 复杂的业务逻辑建议放在 Service 中实现
// 不推荐的写法 - 在监听器中实现复杂业务逻辑
public class TaskAssignmentListener implements TaskListener {
    @Override
    public void notify(DelegateTask delegateTask) {
        // 直接在监听器中实现复杂的业务逻辑
        List<User> users = getUsersByDepartment();
        calculateWorkload();
        updateUserStatus();
        sendNotifications();
        // ... 更多复杂逻辑
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
// 推荐的写法 - 监听器调用Service方法
@Component
public class TaskAssignmentListener implements TaskListener {
    @Autowired
    private TaskAssignmentService taskAssignmentService;
    
    @Override
    public void notify(DelegateTask delegateTask) {
        // 监听器只负责调用Service方法
        String assignee = taskAssignmentService.calculateAssignee(delegateTask);
        delegateTask.setAssignee(assignee);
    }
}

// Service层实现复杂业务逻辑
@Service
public class TaskAssignmentService {
    @Autowired
    private UserRepository userRepository;
    @Autowired
    private WorkloadService workloadService;
    
    public String calculateAssignee(DelegateTask task) {
        // 在Service中实现复杂的业务逻辑
        List<User> users = getUsersByDepartment();
        Map<String, Integer> workloads = workloadService.calculateWorkloads();
        // ... 更多业务逻辑
        return selectOptimalAssignee(users, workloads);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

(完)