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.pswp.cn/web/89718.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/89718.shtml
英文地址,请注明出处:http://en.pswp.cn/web/89718.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于Rust游戏引擎实践(Game)

Rust游戏引擎推荐 以下是一些流行的Rust游戏引擎,适用于不同开发需求: Bevy 特点:数据驱动、模块化设计,支持ECS架构,适合初学者和复杂项目。 适用场景:2D/3D游戏、原型开发。 Amethyst 特点:成熟的ECS框架,支持多线程,社区活跃。 适用场景:大型游戏或高性能应用。…

PyTorch 数据加载实战:从 CSV 到图像的全流程解析

目录 一、PyTorch 数据加载的核心组件 1.1 Dataset 类的核心方法 1.2 DataLoader 的作用 二、加载 CSV 数据实战 2.1 自定义 CSV 数据集 2.2 使用 TensorDataset 快速加载 三、加载图像数据实战 3.1 自定义图像数据集 3.2 使用 ImageFolder 快速加载 四、加载官方数据…

程序人生,开启2025下半年

时光匆匆&#xff0c;2025年已然过去一半。转眼来到了7月份。 回望过去上半年&#xff0c;可能你也经历了职场的浮沉、生活的跌宕、家庭的变故。 而下半年&#xff0c;生活依旧充满了各种变数。 大环境的起起伏伏、生活节奏的加快&#xff0c;都让未来的不确定性愈发凸显。 在这…

在 .NET Core 中创建 Web Socket API

要在 ASP.NET Core 中创建 WebSocket API&#xff0c;您可以按照以下步骤操作&#xff1a;设置新的 ASP.NET Core 项目打开 Visual Studio 或您喜欢的 IDE。 创建一个新的 ASP.NET Core Web 应用程序项目。 选择API模板&#xff0c;因为这将成为您的 WebSocket API 的基础。在启…

Python 之地址编码识别

根据输入地址&#xff0c;利用已有的地址编码文件&#xff0c;构造处理规则策略识别地址的编码。 lib/address.json 地址编码文件&#xff08;这个文件太大&#xff0c;博客里放不下&#xff0c;需要的话可以到 gitcode 仓库获取&#xff1a;https://gitcode.com/TomorrowAndT…

kafka的部署

目录 一、kafka简介 1.1、概述 1.2、消息系统介绍 1.3、点对点消息传递模式 1.4、发布-订阅消息传递模式 二、kafka术语解释 2.1、结构概述 2.2、broker 2.3、topic 2.4、producer 2.5、consumer 2.6、consumer group 2.7、leader 2.8、follower 2.9、partition…

小语种OCR识别技术实现原理

小语种OCR&#xff08;光学字符识别&#xff09;技术的实现原理涉及计算机视觉、自然语言处理&#xff08;NLP&#xff09;和深度学习等多个领域的融合&#xff0c;其核心目标是让计算机能够准确识别并理解不同语言的印刷或手写文本。以下是其关键技术实现原理的详细解析&#…

GPT:让机器拥有“创造力”的语言引擎

当ChatGPT写出莎士比亚风格的十四行诗&#xff0c;当GitHub Copilot自动生成编程代码&#xff0c;背后都源于同一项革命性技术——**GPT&#xff08;Generative Pre-trained Transformer&#xff09;**。今天&#xff0c;我们将揭开这项“语言魔术”背后的科学原理&#xff01;…

LeetCode|Day19|14. 最长公共前缀|Python刷题笔记

LeetCode&#xff5c;Day19&#xff5c;14. 最长公共前缀&#xff5c;Python刷题笔记 &#x1f5d3;️ 本文属于【LeetCode 简单题百日计划】系列 &#x1f449; 点击查看系列总目录 >> &#x1f4cc; 题目简介 题号&#xff1a;14. 最长公共前缀 难度&#xff1a;简单…

安全事件响应分析--基础命令

----万能密码oror1 or # 1or11 1 or 11安全事件响应分析------***windoes***------方法开机启动有无异常文件 【开始】➜【运行】➜【msconfig】文件排查 各个盘下的temp(tmp)相关目录下查看有无异常文件 &#xff1a;Windows产生的 临时文件 可以通过查看日志且通过筛…

基于C#+SQL Server实现(Web)学生选课管理系统

学生选课管理系统的设计与开发一、项目背景学生选课管理系统是一个学校不可缺少的部分&#xff0c;传统的人工管理档案的方式存在着很多的缺点&#xff0c;如&#xff1a;效率低、保密性差等&#xff0c;所以开发一套综合教务系统管理软件很有必要&#xff0c;它应该具有传统的…

垃圾回收(GC)

内存管理策略&#xff0c;在业务进程运行的过程中&#xff0c;由垃圾收集器以类似守护协程的方式在后台运行&#xff0c;按照指定策略回收不再被使用的对象&#xff0c;释放内存空间进行回收 优势&#xff1a; 屏蔽内存回收的细节&#xff1a;屏蔽复杂的内存管理工作&#xff0…

Datawhale AI夏令营-机器学习

比赛简介 「用户新增预测挑战赛」是由科大讯飞主办的一项数据科学竞赛&#xff0c;旨在通过机器学习方法预测用户是否为新增用户 比赛属于二分类任务&#xff0c;评价指标采用F1分数&#xff0c;分数越高表示模型性能越好。 如果你有一份带标签的表格型数据&#xff0c;只要…

Spring IOC容器在Web环境中是如何启动的(源码级剖析)?

文章目录一、Web 环境中的 Spring MVC 框架二、Web 应用部署描述配置传统配置&#xff08;web.xml&#xff09;&#xff1a;Java配置类&#xff08;Servlet 3.0&#xff09;&#xff1a;三、核心启动流程详解1. 启动流程图2. ★容器初始化入口&#xff1a;ContextLoaderListene…

18个优质Qt开源项目汇总

1&#xff0c;Clementine Music Player Clementine Music Player 是一个功能完善、跨平台的开源音乐播放器&#xff0c;非常适合用于学习如何开发媒体类应用&#xff0c;尤其是跨平台桌面应用。它基于 Qt 框架开发&#xff0c;支持多种操作系统&#xff0c;包括 Windows、macO…

计算机视觉:AI 的 “眼睛” 如何看懂世界?

1. 什么是计算机视觉&#xff1a;让机器 “看见” 并 “理解” 的技术1.1 计算机视觉的核心目标计算机视觉&#xff08;CV&#xff09;是人工智能的一个重要分支&#xff0c;它让计算机能够 “看懂” 图像和视频 —— 不仅能捕捉像素信息&#xff0c;还能分析内容、提取语义&am…

华为OD刷题记录

华为OD刷题记录 刷过的题 入门 1、进制 2、NC61 doing 订阅专栏

QT学习教程(二十五)

双缓冲技术&#xff08;Double Buffering&#xff09;&#xff08; 2、公有函数实现&#xff09;#include <QtGui> #include <cmath> using namespace std; #include "plotter.h"以上代码为文件的开头&#xff0c;在这里把std 的名空间加入到当前的全…

设计模式笔记_结构型_装饰器模式

1.装饰器模式介绍装饰器模式是一种结构型设计模式&#xff0c;允许你动态地给对象添加行为&#xff0c;而无需修改其代码。它的核心思想是将对象放入一个“包装器”中&#xff0c;这个包装器提供了额外的功能&#xff0c;同时保持原有对象的接口不变。想象一下&#xff0c;你有…

day25 力扣90.子集II 力扣46.全排列 力扣47.全排列 II

子集II给你一个整数数组 nums &#xff0c;找出并返回所有该数组中不同的递增子序列&#xff0c;递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。数组中可能含有重复元素&#xff0c;如出现两个整数相等&#xff0c;也可以视作递增序列的一种特殊情况。示例 1&…