当前位置: 首页 > web >正文

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表达式简化实现。
  • ‌事件过滤‌:只会接收到指定类型的事件通知。
  • ‌观察者模式‌:实现发布-订阅机制,解耦事件生产者和消费者‌。

监听器的原理:

  1. 事件发布者调用publishEvent()方法发布事件。
  2. ApplicationEventMulticaster获取所有匹配的监听器。
  3. 通过反射调用监听器的onApplicationEvent()方法。
  4. 监听器处理完成后返回,流程结束。

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("请求后处理");}
}

http://www.xdnf.cn/news/15887.html

相关文章:

  • 2.4 组件间通信Props(父传子)
  • Rust Web 全栈开发(九):增加教师管理功能
  • 【SVM smote】MAP - Charting Student Math Misunderstandings
  • Custom SRP - Custom Render Pipeline
  • RabbitMQ01——基础概念、docker配置rabbitmq、内部执行流程、五种消息类型、测试第一种消息类型
  • RabbitMQ—事务与消息分发
  • 软考 系统架构设计师系列知识点之杂项集萃(113)
  • AJAX概述
  • c++ 基本语法易错与技巧总结
  • 零基础学习性能测试-linux服务器监控:内存监控
  • fastjson2 下划线字段转驼峰对象
  • 【RK3576】【Android14】分区划分
  • 石子问题(区间dp)
  • 从Prompt到结构建模:如何以数据驱动重构日本语言学校体系?以国际日本语学院为例
  • Linux:lvs集群技术
  • LVS四种工作模式深度解析
  • 千线万网,电路之行——LVS检查的内核逻辑
  • Python day18
  • 统计EfficientNet-B7的参数个数。
  • 华为擎云L420安装LocalSend
  • 单元测试学习+AI辅助单测
  • 【图像处理基石】什么是小波变换?
  • 美国VPS服务器Linux内核参数调优的实践与验证
  • iOS 通知机制及底层原理
  • 突破 MySQL 性能瓶颈:死锁分析 + 慢查询诊断 + 海量数据比对实战
  • 【设计模式C#】状态模式(用于解决解耦多种状态之间的交互)
  • 中间件安全攻防全解:从Tomcat到Weblogic反序列化漏洞介绍
  • 使用DataGrip连接安装在Linux上的Redis
  • FreeRTOS—列表和列表项
  • 相机参数的格式与作用