spring注解@EventListener实现监听原理

文章目录

  • @EventListener使用方式
  • @EventListener实现原理
    • 1.引入时机
    • 2 初始化时机
    • 3 作用时机->将加了EventListener注解的方法识别出来,并封装为监听器,加载spring容器中
  • 总结

@EventListener使用方式

package com.cyl.listener;import org.springframework.context.ApplicationEvent;
import org.springframework.context.PayloadApplicationEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;@Component
public class CylOrderSecListener {@EventListenerpublic void listen(ApplicationEvent event) {System.out.println(event);}
}

@EventListener实现原理

主要通过EventListenerMethodProcessor和DefaultEventListenerFactory这两个类实现。

EventListenerMethodProcessor的作用是识别所有使用eventListener注解的方法
DefaultEventListenerFactory将EventListenerMethodProcessor识别出的方法封装成为监听器类

以代码new AnnotationConfigApplicationContext为入口调试代码去讲解EventListenerMethodProcessor和DefaultEventListenerFactory如何去生效的

package com.cyl;import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class Test {public static void main(String[] args) {// 创建一个Spring容器AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);}
}

1.引入时机

EventListenerMethodProcessor和DefaultEventListenerFactory的bean定义信息在容器初始化最开始阶段,DefaultListableBeanFactory实例化后,被注册到DefaultListableBeanFactory的beanDefinitionMap中。

执行new AnnotationConfigApplicationContext,会优先执行父类 GenericApplicationContex构造方法,实例化一个bean工厂
在这里插入图片描述在这里插入图片描述
GenericApplicationContext执行完后,会实例化AnnotatedBeanDefinitionReader,可以理解为容器内一个bean定义阅读器,负责将bean定义注册到bean工厂中。实例化AnnotatedBeanDefinitionReader会注册一些bean定义到bean工厂中,其中就包括了EventListenerMethodProcessor和DefaultEventListenerFactory。
在这里插入图片描述
在这里插入图片描述在这里插入图片描述

2 初始化时机

1.1只引入了bean定义,还未真正对bean进行初始化,初始化是在执行spring容器启动最后阶段初始化非懒加载的单例bean时,将EventListenerMethodProcessor和DefaultEventListenerFactory放入到单例池内。
在这里插入图片描述
在这里插入图片描述
最终会走到org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons

@Overridepublic void preInstantiateSingletons() throws BeansException {if (logger.isTraceEnabled()) {logger.trace("Pre-instantiating singletons in " + this);}// Iterate over a copy to allow for init methods which in turn register new bean definitions.// While this may not be part of the regular factory bootstrap, it does otherwise work fine.List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);// Trigger initialization of all non-lazy singleton beans...for (String beanName : beanNames) {// 获取合并后的BeanDefinitionRootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {if (isFactoryBean(beanName)) {// 获取FactoryBean对象Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);if (bean instanceof FactoryBean) {FactoryBean<?> factory = (FactoryBean<?>) bean;boolean isEagerInit;if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit,getAccessControlContext());}else {isEagerInit = (factory instanceof SmartFactoryBean &&((SmartFactoryBean<?>) factory).isEagerInit());}if (isEagerInit) {// 创建真正的Bean对象(getObject()返回的对象)getBean(beanName);}}}else {// 创建Bean对象getBean(beanName);}}}// 所有的非懒加载单例Bean都创建完了后// Trigger post-initialization callback for all applicable beans...for (String beanName : beanNames) {Object singletonInstance = getSingleton(beanName);if (singletonInstance instanceof SmartInitializingSingleton) {StartupStep smartInitialize = this.getApplicationStartup().start("spring.beans.smart-initialize").tag("beanName", beanName);SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;if (System.getSecurityManager() != null) {AccessController.doPrivileged((PrivilegedAction<Object>) () -> {smartSingleton.afterSingletonsInstantiated();return null;}, getAccessControlContext());}else {smartSingleton.afterSingletonsInstantiated();}smartInitialize.end();}}}

当执行到上诉代码第47行时=》单例池中找到这两个对象
在这里插入图片描述

3 作用时机->将加了EventListener注解的方法识别出来,并封装为监听器,加载spring容器中

在这里插入图片描述
当执行org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons初始化后,因EventListenerMethodProcessor实现了SmartInitializingSingleton,而所有实现SmartInitializingSingleton类对象都需要在所有对象初始化后再执行afterSingletonsInstantiated
即org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons47行后代码

// 所有的非懒加载单例Bean都创建完了后// Trigger post-initialization callback for all applicable beans...for (String beanName : beanNames) {Object singletonInstance = getSingleton(beanName);if (singletonInstance instanceof SmartInitializingSingleton) {StartupStep smartInitialize = this.getApplicationStartup().start("spring.beans.smart-initialize").tag("beanName", beanName);SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;if (System.getSecurityManager() != null) {AccessController.doPrivileged((PrivilegedAction<Object>) () -> {//关键方法......smartSingleton.afterSingletonsInstantiated();return null;}, getAccessControlContext());}else {//关键方法......smartSingleton.afterSingletonsInstantiated();}smartInitialize.end();}}}

当执行smartSingleton.afterSingletonsInstantiated();就会调到org.springframework.context.event.EventListenerMethodProcessor#afterSingletonsInstantiated,EventListenerMethodProcessor真正的处理逻辑来了,主要看第38行关键方法

@Overridepublic void afterSingletonsInstantiated() {ConfigurableListableBeanFactory beanFactory = this.beanFactory;Assert.state(this.beanFactory != null, "No ConfigurableListableBeanFactory set");String[] beanNames = beanFactory.getBeanNamesForType(Object.class);for (String beanName : beanNames) {if (!ScopedProxyUtils.isScopedTarget(beanName)) {// 拿到当前Bean对象的类型Class<?> type = null;try {type = AutoProxyUtils.determineTargetClass(beanFactory, beanName);}catch (Throwable ex) {// An unresolvable bean type, probably from a lazy bean - let's ignore it.if (logger.isDebugEnabled()) {logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex);}}if (type != null) {if (ScopedObject.class.isAssignableFrom(type)) {try {Class<?> targetClass = AutoProxyUtils.determineTargetClass(beanFactory, ScopedProxyUtils.getTargetBeanName(beanName));if (targetClass != null) {type = targetClass;}}catch (Throwable ex) {// An invalid scoped proxy arrangement - let's ignore it.if (logger.isDebugEnabled()) {logger.debug("Could not resolve target bean for scoped proxy '" + beanName + "'", ex);}}}try {//关键方法processBean(beanName, type);}catch (Throwable ex) {throw new BeanInitializationException("Failed to process @EventListener " +"annotation on bean with name '" + beanName + "'", ex);}}}}}

org.springframework.context.event.EventListenerMethodProcessor#processBean,关注下面代码的注释,主要逻辑就是会遍历所有单例池中的对象,找到对象中加@EventListener注解的方法,然后通过EventListenerFactory将方法设置成监听器,注册到spring容器中

private void processBean(final String beanName, final Class<?> targetType) {if (!this.nonAnnotatedClasses.contains(targetType) &&AnnotationUtils.isCandidateClass(targetType, EventListener.class) &&!isSpringContainerClass(targetType)) {// 找到所有加了@EventListener注解的方法Map<Method, EventListener> annotatedMethods = null;try {annotatedMethods = MethodIntrospector.selectMethods(targetType,(MethodIntrospector.MetadataLookup<EventListener>) method ->AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class));}catch (Throwable ex) {// An unresolvable type in a method signature, probably from a lazy bean - let's ignore it.if (logger.isDebugEnabled()) {logger.debug("Could not resolve methods for bean with name '" + beanName + "'", ex);}}if (CollectionUtils.isEmpty(annotatedMethods)) {this.nonAnnotatedClasses.add(targetType);if (logger.isTraceEnabled()) {logger.trace("No @EventListener annotations found on bean class: " + targetType.getName());}}else {// Non-empty set of methodsConfigurableApplicationContext context = this.applicationContext;Assert.state(context != null, "No ApplicationContext set");List<EventListenerFactory> factories = this.eventListenerFactories;Assert.state(factories != null, "EventListenerFactory List not initialized");for (Method method : annotatedMethods.keySet()) {for (EventListenerFactory factory : factories) {// 利用EventListenerFactory来对加了@EventListener注解的方法生成ApplicationListener对象if (factory.supportsMethod(method)) {Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName));ApplicationListener<?> applicationListener =factory.createApplicationListener(beanName, targetType, methodToUse);if (applicationListener instanceof ApplicationListenerMethodAdapter) {((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator);}context.addApplicationListener(applicationListener);break;}}}if (logger.isDebugEnabled()) {logger.debug(annotatedMethods.size() + " @EventListener methods processed on bean '" +beanName + "': " + annotatedMethods);}}}}

2.4 发布事件,生效
容器初始化后,设置的监听器会收到容器初始化完成的事件,然后执行自定义的监听事件
容器初始化最后阶段,即执行org.springframework.context.support.AbstractApplicationContext#finishRefresh
在这里插入图片描述
在这里插入图片描述
最终效果图为:在这里插入图片描述

总结

EventListenerMethodProcessor和DefaultEventListenerFactory两个类是注解EventListener的逻辑处理类,在spring容器初始化阶段先显示引入这两个类的bean定义,然后在加载非懒加载单例bean时,对这两个类进行初始化,待所有非懒加载单例bean都初始化完后,执行EventListenerMethodProcessor的afterSingletonsInstantiated即初始化后方法,识别出所有加了注解EventListener的方法,将这些方法用DefaultEventListenerFactory封装成监听器类,注册到spring容器中。待发布事件时,再从spring容器中获取所有监听器类,回调监听方法。

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

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

相关文章

【EasyExcel】多sheet、追加列

业务-EasyExcel多sheet、追加列 背景 最近接到一个导出Excel的业务&#xff0c;需求就是多sheet&#xff0c;每个sheet导出不同结构&#xff0c;第一个sheet里面能够根据最后一列动态的追加列。原本使用的 pig4cloud 架子&#xff0c;使用 ResponseExcel注解方式组装返回数据…

设计模式深度解析:AI如何影响装饰器模式与组合模式的选择与应用

​&#x1f308; 个人主页&#xff1a;danci_ &#x1f525; 系列专栏&#xff1a;《设计模式》《MYSQL应用》 &#x1f4aa;&#x1f3fb; 制定明确可量化的目标&#xff0c;坚持默默的做事。 AI如何影响装饰器模式与组合模式的选择与应用 在今天这个快速发展的技术时代&#…

Segger Embedded Studio IDE使用体验——默认的Section和Linker的设置

Segger Embedded Studio IDE使用体验之一——默认的Section和Linker的设置 一、简介二、操作2.1 编译后代码分析2.1.1 符号浏览器2.1.2 读取elf文件和map文件 2.2 调试2.2.1 查看变量2.2.2 设置供电 2.3 运行环境设置2.3.1 编译器2.3.2 汇编器2.3.3 包含其他文件2.3.4 .bss和.d…

Java SE入门及基础(45)

目录 嵌套类&#xff08;上&#xff09; 4. 内部类 内部类对象创建语法 示例 5. 局部内部类 示例 6. 匿名内部类 示例 Java SE文章参考:Java SE入门及基础知识合集-CSDN博客 嵌套类&#xff08;上&#xff09; 4. 内部类 As with instance methods and variables, an…

【Java】【设计模式】动态代理

因为需要看spring源代码&#xff0c;看到代理这个词看不懂&#xff0c;后面顺着线索发现需要学习一下设计模式的动态代理&#xff0c;然后疯狂补课&#xff0c;结果发现&#xff0c;都不知道在说什么例子给的也比较抽象&#xff0c;好在自己付出快4小时还是有一些微弱的感悟的。…

Spring Cloud: openFegin使用

文章目录 一、OpenFeign简介二、Springboot集成OpenFeign1、引入依赖2、EnableFeignClients注解&#xff08;1&#xff09;应用&#xff08;2&#xff09;属性解析 3、 FeignClient&#xff08;1&#xff09;应用&#xff08;2&#xff09;属性解析&#xff08;3&#xff09;向…

基于百度地图实现Android定位功能实现(一)

Android集成百度地图 文章目录 Android集成百度地图前言准备工作创建工程申请密钥 在项目中集成BaiduMap SDK创建地图 前言 本案例使用百度地图实现在Android中集成地图&#xff0c;并且实现了普通地图/卫星地图&#xff0c;以及路况图和热状图功能&#xff1b; 参考技术文档&…

Android SQLite的使用

前言 本文用于介绍SQLite&#xff0c;SQLite是Android内置的数据库&#xff0c;是一款轻量级的关系型数据库。它具有运算速度快、占用资源少等优点。支持SQL语法同时遵循数据库的ACID事务。 创建数据库 Android为我们提供了一个SQLiteOpenHelper帮助类&#xff0c;我们可以在…

MTU/TCPMSS/VLAN/ACCESS/TRUNK/HYBRID

MTU RFC标准定义以太网的默认MTU值为1500 最小64字节是为了保证最极端的冲突能被检测到&#xff0c;64字节是能被检测到的最小值&#xff1b;最大不超过1518字节是为了防止过长的帧传输时间过长而占用共享链路太长时间导致其他业务阻塞。所以规定以太网帧大小为64~1518字节&am…

OpenFeign使用

OpenFeign天然支持负载均衡 想要去消费接口的API的服务引入pom依赖 <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> 主启动类上加注解 EnableFeignC…

C++入门(二)

目录 函数重载&#xff1a; 函数重载概念&#xff1a; 参数类型不同&#xff1a; 参数个数不同&#xff1a; 参数类型顺序不同&#xff1a; C支持函数重载的原理--名字修饰&#xff1a; 引用&#xff1a; 引用概念&#xff1a; 引用特性&#xff1a; 常引用&#xff1…

网页的血液——javascript

1.简介 网络开发唯一可选语言&#xff0c; 增加网页动态性与交互性&#xff0c;服务端开发&#xff0c; Nodejs&#xff0c;可以进行数据库文件读写 2.引入 html内部使用script标签&#xff0c;在script内部直接编写js 在外部js文件中编写&#xff0c;在script内部通…

JJJ:linux系统中第一个进程

以linux4.19内核linux系统中第一个进程。 执行shell指令 ps -ef 结果如下&#xff1a; xxxxxx-virtual-machine:~$ ps -ef UID PID PPID C STIME TTY TIME CMD root 1 0 0 20:55 ? 00:00:02 /sbin/init splash root …

【机器学习300问】58、什么是词袋模型和N-gram模型?

词袋模型&#xff08;Bag of Words, BoW&#xff09;和N-gram模型主要用于早期的自然语言处理任务&#xff0c;上文中我介绍了机器是如何读懂文本的四个阶段&#xff0c;这篇文章带大家来看看在不同阶段中会用到的两个模型——词袋模型和N-gram模型。如果没有读过我之前的文章&…

C语言二进制常用逻辑运算符介绍与使用

在C语言中&#xff0c;二进制常用的逻辑运算符包括与&#xff08;AND&#xff09;、或&#xff08;OR&#xff09;、非&#xff08;NOT&#xff09;&#xff0c;以及异或&#xff08;XOR&#xff09;。以下是它们的介绍和使用方法&#xff1a; 与&#xff08;AND&#xff09;&a…

Nodejs 第六十章(http缓存)

HTTP缓存 HTTP 缓存主要分为两大类&#xff1a;强缓存和协商缓存。这两种缓存都通过 HTTP 响应头来控制&#xff0c;目的是提高网站性能。 强缓存介绍 强缓存之后则不需要向服务器发送请求&#xff0c;而是从浏览器缓存读取分为&#xff08;内存缓存&#xff09;| &#xff…

HarmonyOS 应用开发之FA模型与Stage模型应用组件

应用配置文件概述&#xff08;FA模型&#xff09; 每个应用项目必须在项目的代码目录下加入配置文件&#xff0c;这些配置文件会向编译工具、操作系统和应用市场提供描述应用的基本信息。 应用配置文件需申明以下内容&#xff1a; 应用的软件Bundle名称&#xff0c;应用的开发…

《Long-CLIP: Unlocking the Long-Text Capability of CLIP》

论文:https://arxiv.org/pdf/2403.15378.pdf源码:https://github.com/beichenzbc/Long-CLIP 导读 CLIP(Contrastive Language–Image Pre-training),这个由 OpenAI 团队开源的多模态预训练模型,它通过对比学习的方式,同时学习图像和文本的表示,从而实现在没有针对特定…

坐标变换矩阵之平移-opencv

平移矩阵m&#xff0c;平移向量(tx,ty,tz) double tx10.2; double ty0.0; double tz0.0; cv::Mat m cv::Mat::eye(4, 4, CV_64FC1);//单位矩阵 m.at<double>(0,3)tx; m.at<double>(1,3)ty; m.at<double>(2,3)tz;齐次点p(1,0,0,1)&#xff0c; cv::Mat p(4…

ESD保护二极管ESD9B3.3ST5G 以更小的空间实现强大的保护 车规级TVS二极管更给力

什么是汽车级TVS二极管&#xff1f; TVS二极管是一种用于保护电子电路的电子元件。它主要用于电路中的过电压保护&#xff0c;防止电压过高而损坏其他部件。TVS二极管通常被称为“汽车级”是因为它们能够满足汽车电子系统的特殊要求。 在汽车电子系统中&#xff0c;由于车辆启…