Spring监听器
1、监听器的原理
ApplicationListener<T>是Spring框架中基于观察者模式实现的事件监听接口,用于监听应用程序中特定类型的事件。该接口是一个函数式接口,从Spring 4.2开始支持Lambda表达式实现。
接口定义如下:
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {void onApplicationEvent(E event);
}
核心特性包括:
- 泛型参数T:限定监听的事件类型,必须是ApplicationEvent的子类。
- 函数式接口:支持Lambda表达式简化实现。
- 事件过滤:只会接收到指定类型的事件通知。
- 观察者模式:实现发布-订阅机制,解耦事件生产者和消费者。
监听器的原理:
- 事件发布者调用publishEvent()方法发布事件。
- ApplicationEventMulticaster获取所有匹配的监听器。
- 通过反射调用监听器的onApplicationEvent()方法。
- 监听器处理完成后返回,流程结束。
ApplicationEventMulticaster是事件广播器,负责管理监听器和事件分发。
下面是一段自定义监听器的代码,包括自定义事件、发布事件和监听事件。
自定义事件CustomEvent.java:
package com.example.event;import org.springframework.context.ApplicationEvent;public class CustomEvent extends ApplicationEvent {private String data;public CustomEvent(Object source, String data) {super(source);this.data = data;}public String getData() {return data;}
}
EventPublisher类负责发布事件,真正触发事件发布是在具体业务代码中。
package com.example.event;import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;@Component
public class EventPublisher {private final ApplicationContext context;public EventPublisher(ApplicationContext context) {this.context = context;}public void publish(String data) {context.publishEvent(new CustomEvent(this, data));}
}
OrderService在业务处理完成后发布事件:
package com.example.service;import com.example.event.EventPublisher;
import org.springframework.stereotype.Service;@Service
public class OrderService {private final EventPublisher eventPublisher;public OrderService(EventPublisher eventPublisher) {this.eventPublisher = eventPublisher;}public void createOrder(String orderId) {// 业务处理...eventPublisher.publish("Order created: " + orderId);}
}
自定义监听器OrderEventListener处理监听事件:
package com.example.listener;import com.example.event.CustomEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;@Component
public class OrderEventListener implements ApplicationListener<CustomEvent> {@Overridepublic void onApplicationEvent(CustomEvent event) {System.out.println("收到订单事件: " + event.getData());// 处理逻辑:发送通知、更新状态等}
}
@EventListener 是Spring 4.2引入的基于注解的声明式监听方式,实现与ApplicationListener接口相同的功能,并且更加灵活。建议优先使用@EventListener注解的方式实现监听器,对于遗留系统需要保持与旧版本代码风格一致的可以使用ApplicationListener。
@Component
public class MyPreListener {@EventListenerpublic void handleRequestStart(ServletRequestEvent event) {System.out.println("请求开始之前进行预处理操作: " + event.getServletRequest());}@EventListenerpublic void handleRequestCompletion(ServletRequestHandledEvent event) {System.out.println("响应返回之前进行后置处理操作");}
}
2、监听事件类型
Spring提供了丰富的事件类型,都是ApplicationEvent的子类,主要分为以下几类:
1. 容器生命周期事件
- ContextRefreshedEvent:容器刷新或初始化完成之后触发。
- ContextStartedEvent:容器调用start()方法启动时触发。
- ContextStoppedEvent:容器调用stop()方法后触发。
- ContextClosedEvent:容器关闭时触发。
2. Spring Boot特有事件
- ApplicationStartingEvent:应用启动但未做任何初始化时触发。
- ApplicationEnvironmentPreparedEvent:环境准备就绪但上下文未创建。
- ApplicationPreparedEvent:容器刷新前触发。
- ApplicationReadyEvent:应用完全就绪可接收请求。
- ApplicationFailedEvent:启动失败时触发。
3. Web相关事件
- ServletRequestEvent:请求开始时做一些预处理操作。
- ServletRequestHandledEvent:请求结束返回响应之前做一些后置处理操作。
- RequestHandledEvent:请求处理完成后触发(比ServletRequestHandledEvent更通用)。
- HttpSessionCreatedEvent:会话创建时触发,用于会话管理。
- HttpSessionDestroyedEvent:会话销毁时触发,用于资源清理。
例如请求接口时可以通过ServletRequestEvent事件做一些预处理操作,或者返回响应之前通过ServletRequestHandledEvent事件做一些后置操作。
@Component
public class PreRequestListener implements ApplicationListener<ServletRequestEvent> {@Overridepublic void onApplicationEvent(ServletRequestEvent event) {// 请求预处理逻辑}
}@Component
public class PostRequestListener implements ApplicationListener<ServletRequestHandledEvent> {@Overridepublic void onApplicationEvent(ServletRequestHandledEvent event) {// 请求后处理逻辑}
}
下面重点看一下ContextRefreshedEvent和ApplicationReadyEvent事件。
根据Spring bean的初始化-CSDN博客这篇文章可知,常用的spring bean的初始化方法有以下四种:
(1)@PostConstruct注解的方法;
(2)类实现了InitializingBean接口,实现了afterPropertiesSet方法;
(3)通过XML配置文件在<bean>标签中的init-method属性指定初始化方法,或者@Bean的initMethod属性指定的方法,如@Bean(initMethod = "init"),其中init是一个方法;
(4)还可以自定义后置处理器实现BeanPostProcessor接口,重写postProcessBeforeInitialization方法实现初始化。
以上这四种初始化方法如果在一个类上同时使用,执行顺序是(4)>(1)>(2)>(3)。
ContextRefreshedEvent事件是在Spring容器完成初始化之后触发的,它主要用于执行需要在所有Bean就绪后才能进行的全局操作,如缓存预热、数据预加载等。ContextRefreshedEvent是Spring框架原生事件,表示Spring容器ApplicationContext已初始化或刷新完成。在Spring Boot中,ContextRefreshedEvent位于ApplicationPreparedEvent之后和ApplicationStartedEvent之前,更在ApplicationReadyEvent之前。
ApplicationReadyEvent具有与ContextRefreshedEvent相似的功能,也可以执行缓存预热、数据预加载的操作。ApplicationReadyEvent是Spring Boot特有事件,表示应用程序已完全启动并准备好接收请求,是整个启动过程中最后触发的事件,确保所有基础设施就绪。ApplicationReadyEvent事件在ApplicationRunner与CommandLineRunner执行完成后执行。
ContextRefreshedEvent与ApplicationReadyEvent功能特性对比:
特性 | ContextRefreshedEvent | ApplicationReadyEvent |
所属框架 | Spring原生 | Spring Boot特有 |
触发确定性 | 可能多次触发(父子容器) | 确保只触发一次 |
基础设施状态 | 容器就绪,外部服务未保证 | 所有基础设施(如Web服务器)已就绪 |
适用阶段 | 容器初始化后 | 应用完全可用时 |
健康检查 | 未执行 | 已通过 |
应用场景分析:
1. ContextRefreshedEvent适用场景
- Bean初始化后的配置调整:当需要基于已初始化的Bean进行动态配置时。
- 内部缓存预热:加载不依赖外部服务的内部缓存数据。
- 早期资源初始化:需要在容器就绪后立即执行的轻量级操作。
- 多模块系统中的模块间协调:在复杂应用中协调不同模块的初始化顺序。
2. ApplicationReadyEvent适用场景
- 外部服务连接:建立与数据库、消息队列等外部服务的连接。
- 定时任务启动:确保所有依赖Bean就绪后再启动定时任务。
- 服务注册:向服务注册中心(如Eureka)注册服务实例。
- 就绪状态通知:通知监控系统应用已准备好接收流量。
- 关键资源检查:验证所有必要服务是否正常启动。
实际工作中的选择建议:
纯Spring应用:只能使用ContextRefreshedEvent。
Spring Boot应用:
- 需要确保基础设施就绪 → 选择ApplicationReadyEvent。
- 仅需容器初始化后操作 → 选择ContextRefreshedEvent。
- 微服务架构中的服务注册 → 必须使用ApplicationReadyEvent。
ContextRefreshedEvent中应检查event.getApplicationContext().getParent()避免重复执行,ApplicationReadyEvent天然保证单次执行。ContextRefreshedEvent中的异常可能阻止应用启动,ApplicationReadyEvent中的异常通常不会阻止启动但应记录。在springboot项目中,如果需要在Spring容器初始化之后,且正式对外提供服务之前做一些预处理操作,尽量使用ApplicationReadyEvent。
ContextRefreshedEvent事件监听的代码示例:
@Component
public class MyContextListener implements ApplicationListener<ContextRefreshedEvent> {@Overridepublic void onApplicationEvent(ContextRefreshedEvent event) {// 避免在Web环境中因父子容器重复执行if (event.getApplicationContext().getParent() == null) {// 执行容器初始化后的操作System.out.println("Context refreshed, beans are ready");}}
}
ApplicationReadyEvent事件监听的代码示例:
@Component
public class MyAppReadyListener {@EventListenerpublic void onApplicationReady(ApplicationReadyEvent event) {// 应用完全就绪后可安全执行的操作System.out.println("Application is fully ready to serve requests");// 示例:启动定时任务ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);scheduler.scheduleAtFixedRate(() -> System.out.println("Periodic task running"), 0, 1, TimeUnit.MINUTES);}
}
通过过滤器和拦截器实现请求前和请求后进行处理操作:
Filter实现请求前后处理:
@Component
public class MyFilter implements Filter {@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {System.out.println("请求前处理");chain.doFilter(request, response);System.out.println("请求后处理");}
}
Interceptor实现请求前后处理:
@Component
public class MyInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("请求前处理");return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("请求后处理");}
}