ApplicationListener监听器

在spring-context 5.3.26中来看一下它的定义:

package org.springframework.context;import java.util.EventListener;
import java.util.function.Consumer;@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {void onApplicationEvent(E event);static <T> ApplicationListener<PayloadApplicationEvent<T>> forPayload(Consumer<T> consumer) {return (event) -> {consumer.accept(event.getPayload());};}
}

监听器顾名思义,它得监听点什么,就像老奶奶一样支楞着耳朵打听一些事情一样,它也要监听一些事件,它既然是在context包下,那么它也肯定和context有关。

它定义了一个泛型E事件,这个事件是ApplicationEvent类型,事件怎样通知给监听器呢?通过调用它的方法onApplicationEvent(E event);将这个事件给传进来。

当然了它还用一个forPayload方法,这个PayloadApplicationEvent其实也是继承ApplicationEvent,这个后讲。

那么ApplicationListener什么时候被回调呢?

事件发布:(例如自媒体发布的一篇油炸指甲盖能治疗脑血栓的文章,被栓柱娘刷抖音看到了)它的被回调时机是当应用程序中的某个部分(如一个Bean或者框架内部)需要通知其他组件某个事情发生时,它会创建一个事件对象(通常是ApplicationEvent的子类),并通过调用ApplicationContextpublishEvent(ApplicationEvent event)方法来发布这个事件。

事件多播:(栓柱娘看到了这个新闻发到自己的健康每一天姐妹群里ApplicationContext接收到事件后,它会使用内部的ApplicationEventMulticaster来广播这个事件(前提是监听器都注册上来了,也就是类似于关注健康的老太太都在群里)。ApplicationEventMulticaster维护了一个注册了的监听器列表,这些监听器都是实现了ApplicationListener接口的Bean。

匹配与回调:(有脑血栓的老太太看到了,感到有趣就点开看了这篇文章ApplicationEventMulticaster会遍历这个列表,对于每个监听器,它会检查该监听器是否对该类型的事件感兴趣(即监听器泛型参数是否与发布的事件类型匹配)。如果匹配,它就会调用该监听器的onApplicationEvent(ApplicationEvent event)方法,将事件传递给监听器,从而执行监听器中定义的业务逻辑。

我们再来看事件发布中的ApplicationContext ,它可不得了,它是IOC容器的高级模式,它是BeanFactory的拓展,他能管理Bean的生命周期,它能提供多种配置载入方式如(XML,注解等),它能自动装配Bean之间的依赖关系,它内置AOP支持,当然它也支持我们所讲的事件监听机制。

package org.springframework.context;import org.springframework.beans.factory.HierarchicalBeanFactory;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.core.env.EnvironmentCapable;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.lang.Nullable;public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory, MessageSource, ApplicationEventPublisher, ResourcePatternResolver {@NullableString getId();String getApplicationName();String getDisplayName();long getStartupDate();@NullableApplicationContext getParent();AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;
}

你看,它继承了ApplicationEventPublisher接口,说明它确实有发布事件的能力,也就是说这个高级容器能发布事件。

package org.springframework.context;@FunctionalInterface
public interface ApplicationEventPublisher {default void publishEvent(ApplicationEvent event) {this.publishEvent((Object)event);}void publishEvent(Object event);
}

spring还贴心的给我们整了几个事件,如ContextRefreshedEventContextStartedEventContextStoppedEventContextClosedEvent,这些事件分别对应Spring容器的生命周期的不同阶段,监听这些事件可以让Bean在容器启动、刷新、停止、关闭时执行特定的操作。

我们再来看事件多播中的ApplicationEventMulticaster,Spring框架默认使用的ApplicationEventMulticaster实现类是SimpleApplicationEventMulticaster。这个类是ApplicationEventMulticaster接口的一个基础且常用的实现,它维护了一个监听器列表,并在接收到事件时,按照顺序同步地调用这些监听器的onApplicationEvent方法来广播事件。

SimpleApplicationEventMulticaster注册监听器主要发生在Spring应用上下文初始化的过程中。具体来说,这个过程涉及以下几个步骤:

  1. Bean的初始化:当Spring容器初始化Bean时,如果某个Bean实现了ApplicationListener接口,或者通过@EventListener注解标记了处理事件的方法,那么这个Bean或方法就会被识别为事件监听器,并准备注册。

  2. ApplicationContext初始化:在ApplicationContext初始化的后期,特别是调用refresh()方法期间,会执行一系列的初始化工作,包括注册内部和用户定义的事件监听器。这是通过调用finishBeanFactoryInitialization(beanFactory)方法完成的,该方法会初始化剩下的单例Beans,包括事件监听器。

  3. 显式注册:在某些情况下,开发人员可能通过编程方式直接向ApplicationContextApplicationEventMulticaster实例注册监听器,这通常发生在应用的配置类中。

  4. 自动检测和注册:Spring在初始化过程中会自动检测并注册实现了ApplicationListener接口的Bean,或者使用了@EventListener注解的方法作为监听器。

也就是说SimpleApplicationEventMulticaster注册监听器主要是在Spring容器启动和初始化其Bean的时候。一旦容器完全初始化,所有监听器就已经注册完毕并准备好接收事件广播。

我们再看匹配与回调,我们看一下简单的例子:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ApplicationEventMulticaster;
import org.springframework.context.event.SimpleApplicationEventMulticaster;
import org.springframework.context.support.AbstractApplicationContext;@Configuration
public class AppConfig {@Beanpublic CustomEventListener customEventListener() {return new CustomEventListener();}@Beanpublic ApplicationEventMulticaster applicationEventMulticaster(AbstractApplicationContext applicationContext) {SimpleApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster();eventMulticaster.setTaskExecutor(null); // 可以自定义任务执行器以支持异步事件处理applicationContext.addApplicationListener(customEventListener());return eventMulticaster;}// 假设有一个服务类,用于在业务逻辑中触发事件@Beanpublic MyService myService(ApplicationEventMulticaster eventMulticaster) {return new MyServiceImpl(eventMulticaster);}
}class MyServiceImpl implements MyService {private final ApplicationEventMulticaster eventMulticaster;public MyServiceImpl(ApplicationEventMulticaster eventMulticaster) {this.eventMulticaster = eventMulticaster;}public void doSomething() {CustomEvent event = new CustomEvent(this, "Hello from Custom Event!");eventMulticaster.multicastEvent(event);}
}interface MyService {void doSomething();
}

最终它在eventMulticaster.multicastEvent(event);这里发布事件,在SimpleApplicationEventMulticaster类(这货就是ApplicationEventMulticaster的实现类,也是spring默认的事件广播器)的内部最终调用listener.onApplicationEvent(event);被listener监听到。

public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);Executor executor = this.getTaskExecutor();Iterator var5 = this.getApplicationListeners(event, type).iterator();while(var5.hasNext()) {ApplicationListener<?> listener = (ApplicationListener)var5.next();if (executor != null) {executor.execute(() -> {this.invokeListener(listener, event);});} else {this.invokeListener(listener, event);}}}private ResolvableType resolveDefaultEventType(ApplicationEvent event) {return ResolvableType.forInstance(event);}protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {ErrorHandler errorHandler = this.getErrorHandler();if (errorHandler != null) {try {this.doInvokeListener(listener, event);} catch (Throwable var5) {errorHandler.handleError(var5);}} else {this.doInvokeListener(listener, event);}}private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {try {listener.onApplicationEvent(event);} catch (ClassCastException var6) {String msg = var6.getMessage();if (msg != null && !this.matchesClassCastMessage(msg, event.getClass()) && (!(event instanceof PayloadApplicationEvent) || !this.matchesClassCastMessage(msg, ((PayloadApplicationEvent)event).getPayload().getClass()))) {throw var6;}Log loggerToUse = this.lazyLogger;if (loggerToUse == null) {loggerToUse = LogFactory.getLog(this.getClass());this.lazyLogger = loggerToUse;}if (loggerToUse.isTraceEnabled()) {loggerToUse.trace("Non-matching event type for listener: " + listener, var6);}}}

这样就串起来了,你可以用spring自带的监听器,监听一些spring的事件,在spring要干嘛的时候你打听到这些事决定是否参与,你也可以自定义监听器监听约定好的事件,听起来像AOP似的,但是它是以事件为驱动的并不依赖AOP,可以根据自己的使用场景再决定是否用它。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/2428.shtml

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

相关文章

[论文笔记] megatron训练参数:dataloader_type

在深度学习中&#xff0c;dataloader_type参数通常控制着数据的加载、处理和输入到模型的方式。不同的dataloader可能会按照不同的策略处理数据集&#xff0c;这可以显著影响模型训练和评估的效果。具体来说&#xff0c;single和cyclic类型通常如此区别&#xff1a; Single Dat…

关键绩效指标(KPI):明确目标及跟踪进展

在企业管理中&#xff0c;关键绩效指标&#xff08;KPI&#xff09;是一种重要的工具&#xff0c;用于明确目标并跟踪进展。通过设定和监控这些指标&#xff0c;企业能够确保员工、团队和整个组织都朝着既定的目标努力。本文将详细探讨关键绩效指标的重要性、设定方法以及如何有…

缓解工作压力的小窍门:保持健康与创新

目录 1 前言2 工作与休息的平衡3 保持心理健康4 社交与网络建设5 结语 1 前言 作为程序员&#xff0c;我们常常承受着高度的工作压力和持续的创新挑战。为了保持高效和健康&#xff0c;我们需要采取一些方法来缓解工作压力&#xff0c;同时促进个人的心理和身体健康。 2 工作…

大模型的原理与特点,奇异值分解(SVD);低秩近似

目录 一、大模型的原理与特点 二、一个基本架构,三种形式: Parameter-Efficient Fine-Tuning

5. Tailwind CSS 响应式设计的实现

Tailwind CSS 是一个功能类优先的 CSS 框架&#xff0c;它允许开发者通过使用响应式工具类来构建自适应的用户界面。这些工具类可以在不同的断点处有条件地应用&#xff0c;使得在不离开 HTML 的情况下构建复杂的响应式界面变得轻而易举。 基本概念 响应式设计在 Tailwind CS…

Python dlib(HOG+SVM)人脸识别总结

Python dlib(HOG+SVM)人脸识别总结 面部标志检测 dlib 68点(HOG+SVM),194点人脸识别模型,包括口(外嘴唇,内嘴唇),鼻,眉毛(左右眉),眼睛(左右眼),下鄂 5点面部标志检测器(左眼2点,右眼2点,鼻子1点)面部对齐更高效 眨眼检测 ear 眨眼瞬间达到0 疲劳驾驶检测…

kill 端口所属进程

IC:\Users\23022>netstat -ano | findstr “8080” TCP 127.0.0.1:8080 0.0.0.0:0 LISTENING 13532 C:\Users\23022>taskkill /f /t /pid 13532 成功: 已终止 PID 21028 (属于 PID 13532 子进程)的进程。 成功: 已终止 PID 13532 (属于 PID 19260 子进程)的进程。 C:\U…

如何在PostgreSQL中设置自动清理过期数据的策略

文章目录 方法一&#xff1a;使用临时表和定期清理步骤&#xff1a;示例代码&#xff1a;创建临时表&#xff1a;定期清理脚本&#xff08;bash psql&#xff09;&#xff1a; 方法二&#xff1a;使用分区表和定期清理步骤&#xff1a;示例代码&#xff1a;创建分区表&#xf…

初始化Git仓库时应该运行哪个命令?

文章目录 初始化Git仓库时&#xff0c;你应该运行git init这个命令。这个命令的作用是在你当前所在的目录里创建一个新的Git仓库。这样&#xff0c;你就可以在这个目录里开始使用Git来管理你的文件了。 下面我给你举个详细的例子来说明一下&#xff1a; 首先&#xff0c;你需要…

【Mysql】用frm和ibd文件恢复mysql表数据

问题 总是遇到mysql服务意外断开之后导致mysql服务无法正常运行的情况&#xff0c;使用Navicat工具查看能够看到里面的库和表&#xff0c;但是无法获取数据记录&#xff0c;提示数据表不存在。 这里记录一下用frm文件和ibd文件手动恢复数据表的过程。 思路 1、frm文件&…

c++ 派生类向基类转换的可访问性

1.如果派生类以public继承基类&#xff0c;则是is a关系&#xff0c;用派生类可以完成基类的所有功能&#xff0c;所以可以在任意地方将派生类自动转换成基类&#xff0c;注意&#xff0c;这里都是指指针或引用&#xff0c;而不是对象。 比如&#xff1a; class A{}&#xff1…

【代码】Python3|用Python PIL压缩图片至指定大小,并且不自动旋转

代码主体是GPT帮我写的&#xff0c;我觉得这个功能非常实用。 解决自动旋转问题参考&#xff1a;一行代码解决PIL/OpenCV读取图片出现自动旋转的问题&#xff0c;增加一行代码image ImageOps.exif_transpose(image) 即可恢复正常角度。 from PIL import Image, ImageOpsdef …

call,apply,bind

入参 call: 参数数量不固定。第一个参数指定了函数体内的this指向&#xff0c;从第二个参数开始往后&#xff0c;每个参数被依次传入函数。 apply: 接受两个参数。第一个参数指定了函数体内的this指向。第二个参数接受一个数组 [1,2]&#xff0c;但函数拿到的是解构后的入参 1…

spring-core:获取类/方法/字段/字段上直接定义的注解

AnnotatedElement.getAnnotation 如何获取一个类上定义的注解&#xff1f; 这个问题似乎不应该问&#xff0c;我们知道如果要获取一个类/方法/字段/字段上直接定义的注解是很方便的&#xff0c;如Class.getAnnotation(Class<CasbanScan>)就能实现, 只要实现了java.lang.…

Linux:Win10平台上,用VMware安装Centos7.x及系统初始化关键的相关配置(分步骤操作,详细,一篇足以)

VMware安装Centos7.x镜像的详细步骤&#xff1a;VMWare安装Centos系统&#xff08;无桌面模式&#xff09; 我这里是为了安装Hadoop集群&#xff0c;所以&#xff0c;以下这些步骤是必须进行的 如果你是学习Linux&#xff0c;可以跳过非必须的那些配置项 我安装的版本是&…

集群工具之HAProxy

集群工具之HAProxy HAProxy简介 它是一款实现负载均衡的调度器适用于负载特别大的web站点HAProxy的工作模式 mode http&#xff1a;只适用于web服务mode tcp&#xff1a;适用于各种服务mode health&#xff1a;仅做健康检查&#xff0c;很少使用 配置HAProxy client&#x…

jmeter--取样器-- HTTP请求

HTTP请求&#xff08;HTTP Request&#xff09; 右键 >>> 添加 >>> 取样器 >>> HTTP请求&#xff08;HTTP Request&#xff09;基本web服务器协议&#xff1a;https服务器名称或IP&#xff1a;端口号&#xff1a;443HTTP请求请求方式&#xff1a;路…

基于JAVA的机场航班起降与协调管理系统

毕业设计&#xff08;论文&#xff09;任务书 第1页 毕业设计&#xff08;论文&#xff09;题目&#xff1a; 基于JAVA的机场航班起降与协调管理系统 毕业设计&#xff08;论文&#xff09;要求及原始数据&#xff08;资料&#xff09;&#xff1a; 1&#xff0e;综述机场航班调…

【java配置】jpcap的下载与idea配置

解决报错&#xff1a;Cannot resolve symbol ‘jpcap’ 1. jpcap的下载 官网下载链接 百度网盘下载 双击WinpPca安装&#xff0c;jacap1和jpcap2任选其中之一 2. idea配置 &#xff08;1&#xff09;查看当前使用jdk目录 File -> Project Settings -> SDKs &#…

【1577】java网吧收费管理系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 java 网吧收费管理系统是一套完善的java web信息管理系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为 TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为Mysql5.0…