spring事件监听
Spring 事件监听机制:从原理到实战
Spring 事件监听机制是基于观察者模式的一种实现,用于组件间的解耦通信。通过事件发布者(Publisher)、事件(Event)和事件监听器(Listener)三者的配合,实现了 “发布 - 订阅” 模式,让组件无需直接依赖即可完成交互。本文将详细解析 Spring 事件监听的核心原理、内置事件及自定义事件的实现方式。
Spring 事件监听的核心组件
Spring 事件监听机制包含三个核心角色,它们协同工作完成事件的发布与处理:
| 组件 | 作用 | 核心接口 / 类 |
|---|---|---|
| 事件(Event) | 传递的消息载体,封装了需要传递的数据 | ApplicationEvent(所有事件的父类) |
| 事件发布者(Publisher) | 负责发布事件到容器中 | ApplicationEventPublisher(发布接口) |
| 事件监听器(Listener) | 监听指定类型的事件,当事件发布时执行回调逻辑 | ApplicationListener<E>(监听接口) |
三者关系如下:
- 事件发布者通过
publishEvent()方法发布事件; - Spring 容器将事件传递给所有订阅该事件的监听器;
- 监听器通过
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,并封装需要传递的数据(如用户信息、操作类型等):
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() 方法中编写事件处理逻辑:
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
/**
* 用户事件监听器:处理用户注册/登录事件
*/
// 注册为 Spring 组件,让容器管理
public class UserEventListener implements ApplicationListener<UserEvent> {
/**
* 事件处理逻辑:当 UserEvent 发布时触发
*/
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 容器本身实现了该接口,因此可以直接注入使用:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
/**
* 用户服务:负责用户注册/登录,并发布事件
*/
public class UserService {
// 注入事件发布器(Spring 容器自带实现)
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 管理:
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
// 扫描事件相关组件
public class EventConfig {
}
(2)测试事件发布与监听
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)输出结果
用户 张三 注册成功
【用户事件监听】用户 张三 注册成功,发送欢迎短信...
用户 张三 登录成功
【用户事件监听】用户 张三 登录成功,记录登录日志...
从结果可见:当 UserService 发布 UserEvent 后,UserEventListener 自动捕获事件并执行了处理逻辑,两者通过事件实现了解耦(UserService 无需依赖 UserEventListener)。
事件监听的进阶用法
1. 注解式监听器(@EventListener)
Spring 4.2+ 支持通过 @EventListener 注解定义监听器,无需实现 ApplicationListener 接口,更简洁:
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
public class AnnotationUserListener {
// 注解指定监听的事件类型(参数为事件对象)
public void handleUserEvent(UserEvent event) {
System.out.println("【注解式监听】用户 " + event.getUsername() + " 触发 " + event.getEventType() + " 事件");
}
}
效果:与实现接口的方式完全一致,且支持更灵活的配置(如条件过滤)。
2. 异步事件监听
默认情况下,事件处理是同步的(发布者阻塞直到所有监听器完成)。若需异步处理(不阻塞发布者),可通过 @Async 注解实现:
(1)开启异步支持
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.context.annotation.Configuration;
// 开启异步功能
public class AsyncConfig {
}
(2)异步处理事件
public class AsyncUserListener {
// @Async 标记该方法异步执行
public void handleAsyncEvent(UserEvent event) {
try {
// 模拟耗时操作
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("【异步监听】用户 " + event.getUsername() + " 事件处理完成");
}
}
测试结果:发布者无需等待异步监听器完成,直接执行后续逻辑,适合耗时操作(如发送邮件、生成报表)。
3. 事件过滤(条件监听)
通过 @EventListener 的 condition 属性,可根据事件数据过滤是否处理事件:
public class FilterUserListener {
// 仅处理用户名长度 > 2 的注册事件
public void handleFilteredEvent(UserEvent event) {
System.out.println("【过滤监听】处理符合条件的注册事件:" + event.getUsername());
}
}
#event:指代方法参数中的事件对象;- 条件表达式支持 SpEL(Spring 表达式语言),可灵活判断事件数据。
Spring 事件监听的底层原理
- 事件注册:Spring 容器启动时,会扫描所有实现
ApplicationListener或标注@EventListener的 Bean,将其注册到事件多播器(ApplicationEventMulticaster)中。 - 事件发布:当调用
publishEvent()时,事件多播器会遍历所有注册的监听器,找到与事件类型匹配的监听器(泛型或注解指定的类型)。 - 事件处理:多播器调用监听器的
onApplicationEvent()方法(或注解标记的方法),同步或异步执行处理逻辑。
核心类 SimpleApplicationEventMulticaster 的关键代码(简化):
public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
public void multicastEvent(final ApplicationEvent event, 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 事件监听机制基于观察者模式,通过事件、发布者和监听器的配合,实现了组件间的解耦通信。其核心包括:
- 内置事件用于监听容器生命周期和 Web 请求;
- 自定义事件可通过继承
ApplicationEvent实现,配合ApplicationListener或@EventListener处理; - 支持同步 / 异步处理、事件过滤等进阶功能,满足复杂业务场景