0%

spring事件监听

Spring 事件监听机制:从原理到实战

Spring 事件监听机制是基于观察者模式的一种实现,用于组件间的解耦通信。通过事件发布者(Publisher)、事件(Event)和事件监听器(Listener)三者的配合,实现了 “发布 - 订阅” 模式,让组件无需直接依赖即可完成交互。本文将详细解析 Spring 事件监听的核心原理、内置事件及自定义事件的实现方式。

Spring 事件监听的核心组件

Spring 事件监听机制包含三个核心角色,它们协同工作完成事件的发布与处理:

组件 作用 核心接口 / 类
事件(Event) 传递的消息载体,封装了需要传递的数据 ApplicationEvent(所有事件的父类)
事件发布者(Publisher) 负责发布事件到容器中 ApplicationEventPublisher(发布接口)
事件监听器(Listener) 监听指定类型的事件,当事件发布时执行回调逻辑 ApplicationListener<E>(监听接口)

三者关系如下:

  1. 事件发布者通过 publishEvent() 方法发布事件;
  2. Spring 容器将事件传递给所有订阅该事件的监听器;
  3. 监听器通过 onApplicationEvent() 方法处理事件。

Spring 内置事件

Spring 框架自带了 5 种标准事件,用于监听容器生命周期和 Web 请求等场景,这些事件均继承自 ApplicationEvent

内置事件类型 触发时机 典型应用场景
ContextRefreshedEvent ApplicationContext 初始化或刷新时(如调用 refresh() 方法) 容器启动完成后初始化缓存、加载字典数据
ContextStartedEvent 容器调用 start() 方法启动 / 重启时 启动后台任务、定时任务
ContextStoppedEvent 容器调用 stop() 方法停止时 暂停后台任务、释放临时资源
ContextClosedEvent 容器调用 close() 方法关闭时(单例 Bean 会被销毁) 释放数据库连接、关闭线程池
RequestHandledEvent Web 应用中,HTTP 请求处理完成时(仅在 Spring MVC 环境下有效) 记录请求日志、统计接口响应时间

注意:Spring 事件处理默认是单线程同步执行的,事件发布后,发布者会阻塞直到所有监听器处理完成。

自定义事件监听实战

除了内置事件,Spring 允许开发者自定义事件和监听器,实现业务场景下的解耦通信(如用户注册后发送短信、记录日志等)。

1. 步骤 1:定义自定义事件(继承 ApplicationEvent)

事件类需继承 ApplicationEvent,并封装需要传递的数据(如用户信息、操作类型等):

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
import org.springframework.context.ApplicationEvent;

/**
* 自定义用户事件(如用户注册、登录事件)
*/
public class UserEvent extends ApplicationEvent {
// 事件相关数据:用户名
private String username;
// 事件类型:如 REGISTER(注册)、LOGIN(登录)
private String eventType;

// 构造方法(必须调用父类构造器,传入事件源)
public UserEvent(Object source, String username, String eventType) {
super(source); // source:事件源(如触发事件的对象)
this.username = username;
this.eventType = eventType;
}

// Getter 方法(供监听器获取数据)
public String getUsername() {
return username;
}

public String getEventType() {
return eventType;
}
}

2. 步骤 2:定义事件监听器(实现 ApplicationListener)

监听器需实现 ApplicationListener<E> 接口,并指定监听的事件类型(泛型参数),在 onApplicationEvent() 方法中编写事件处理逻辑:

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
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

/**
* 用户事件监听器:处理用户注册/登录事件
*/
@Component // 注册为 Spring 组件,让容器管理
public class UserEventListener implements ApplicationListener<UserEvent> {

/**
* 事件处理逻辑:当 UserEvent 发布时触发
*/
@Override
public void onApplicationEvent(UserEvent event) {
// 从事件中获取数据
String username = event.getUsername();
String eventType = event.getEventType();

// 根据事件类型处理业务
if ("REGISTER".equals(eventType)) {
System.out.println("【用户事件监听】用户 " + username + " 注册成功,发送欢迎短信...");
} else if ("LOGIN".equals(eventType)) {
System.out.println("【用户事件监听】用户 " + username + " 登录成功,记录登录日志...");
}
}
}

3. 步骤 3:发布事件(通过 ApplicationEventPublisher)

事件发布者需要通过 ApplicationEventPublisher 接口的 publishEvent() 方法发布事件。Spring 容器本身实现了该接口,因此可以直接注入使用:

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
36
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;

/**
* 用户服务:负责用户注册/登录,并发布事件
*/
@Service
public class UserService {

// 注入事件发布器(Spring 容器自带实现)
@Autowired
private ApplicationEventPublisher eventPublisher;

/**
* 用户注册
*/
public void register(String username) {
// 1. 执行注册业务逻辑(如保存用户到数据库)
System.out.println("用户 " + username + " 注册成功");

// 2. 发布用户注册事件
eventPublisher.publishEvent(new UserEvent(this, username, "REGISTER"));
}

/**
* 用户登录
*/
public void login(String username) {
// 1. 执行登录业务逻辑(如验证密码)
System.out.println("用户 " + username + " 登录成功");

// 2. 发布用户登录事件
eventPublisher.publishEvent(new UserEvent(this, username, "LOGIN"));
}
}

4. 步骤 4:配置与测试

(1)配置 Spring 容器

通过注解配置扫描组件,确保监听器、服务类被 Spring 管理:

1
2
3
4
5
6
7
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan(basePackages = "com.example.event") // 扫描事件相关组件
public class EventConfig {
}
(2)测试事件发布与监听
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class EventTest {
public static void main(String[] args) {
// 初始化 Spring 容器
ApplicationContext context = new AnnotationConfigApplicationContext(EventConfig.class);

// 获取用户服务
UserService userService = context.getBean(UserService.class);

// 执行注册和登录操作(会发布事件)
userService.register("张三");
userService.login("张三");
}
}
(3)输出结果
1
2
3
4
用户 张三 注册成功
【用户事件监听】用户 张三 注册成功,发送欢迎短信...
用户 张三 登录成功
【用户事件监听】用户 张三 登录成功,记录登录日志...

从结果可见:当 UserService 发布 UserEvent 后,UserEventListener 自动捕获事件并执行了处理逻辑,两者通过事件实现了解耦(UserService 无需依赖 UserEventListener)。

事件监听的进阶用法

1. 注解式监听器(@EventListener)

Spring 4.2+ 支持通过 @EventListener 注解定义监听器,无需实现 ApplicationListener 接口,更简洁:

1
2
3
4
5
6
7
8
9
10
11
12
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

@Component
public class AnnotationUserListener {

// 注解指定监听的事件类型(参数为事件对象)
@EventListener
public void handleUserEvent(UserEvent event) {
System.out.println("【注解式监听】用户 " + event.getUsername() + " 触发 " + event.getEventType() + " 事件");
}
}

效果:与实现接口的方式完全一致,且支持更灵活的配置(如条件过滤)。

2. 异步事件监听

默认情况下,事件处理是同步的(发布者阻塞直到所有监听器完成)。若需异步处理(不阻塞发布者),可通过 @Async 注解实现:

(1)开启异步支持
1
2
3
4
5
6
7
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableAsync // 开启异步功能
public class AsyncConfig {
}
(2)异步处理事件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Component
public class AsyncUserListener {

// @Async 标记该方法异步执行
@Async
@EventListener
public void handleAsyncEvent(UserEvent event) {
try {
// 模拟耗时操作
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("【异步监听】用户 " + event.getUsername() + " 事件处理完成");
}
}

测试结果:发布者无需等待异步监听器完成,直接执行后续逻辑,适合耗时操作(如发送邮件、生成报表)。

3. 事件过滤(条件监听)

通过 @EventListenercondition 属性,可根据事件数据过滤是否处理事件:

1
2
3
4
5
6
7
8
9
@Component
public class FilterUserListener {

// 仅处理用户名长度 > 2 的注册事件
@EventListener(condition = "#event.eventType == 'REGISTER' && #event.username.length() > 2")
public void handleFilteredEvent(UserEvent event) {
System.out.println("【过滤监听】处理符合条件的注册事件:" + event.getUsername());
}
}
  • #event:指代方法参数中的事件对象;
  • 条件表达式支持 SpEL(Spring 表达式语言),可灵活判断事件数据。

Spring 事件监听的底层原理

  1. 事件注册:Spring 容器启动时,会扫描所有实现 ApplicationListener 或标注 @EventListener 的 Bean,将其注册到事件多播器(ApplicationEventMulticaster)中。
  2. 事件发布:当调用 publishEvent() 时,事件多播器会遍历所有注册的监听器,找到与事件类型匹配的监听器(泛型或注解指定的类型)。
  3. 事件处理:多播器调用监听器的 onApplicationEvent() 方法(或注解标记的方法),同步或异步执行处理逻辑。

核心类 SimpleApplicationEventMulticaster 的关键代码(简化):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
// 确定事件类型
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
// 获取所有匹配的监听器
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
// 执行监听器(同步或异步)
invokeListener(listener, event);
}
}

private void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
// 同步执行:直接调用 onApplicationEvent 方法
doInvokeListener(listener, event);
// 异步执行:通过线程池提交任务
// executor.execute(() -> doInvokeListener(listener, event));
}
}

应用场景与优势

典型应用场景

  • 业务解耦:如用户注册后,需同步执行 “发送短信”“添加积分”“记录日志” 等操作,通过事件将这些操作与注册逻辑解耦;
  • 生命周期回调:利用 ContextRefreshedEvent 在容器启动后初始化资源(如缓存预热);
  • 跨模块通信:微服务中,通过事件总线(如 Spring Cloud Stream)实现服务间通信。

优势

  • 解耦:发布者与监听器无需互相依赖,降低代码耦合度;
  • 扩展性:新增监听器无需修改发布者代码,符合开闭原则;
  • 灵活性:支持同步 / 异步、条件过滤等多种处理方式。

总结

Spring 事件监听机制基于观察者模式,通过事件、发布者和监听器的配合,实现了组件间的解耦通信。其核心包括:

  1. 内置事件用于监听容器生命周期和 Web 请求;
  2. 自定义事件可通过继承 ApplicationEvent 实现,配合 ApplicationListener@EventListener 处理;
  3. 支持同步 / 异步处理、事件过滤等进阶功能,满足复杂业务场景

欢迎关注我的其它发布渠道