Spring的事件监听机制

这里写自定义目录标题

  • 1. 概述(重点)
  • 2. ApplicationEventMulticaster
    • 2.1 SimpleApplicationEventMulticaster
    • 2.2 AbstractApplicationEventMulticaster
  • 3. ApplicationListener
    • 3.1 注册监听器
    • 3.2 自定义
  • 4. SpringApplicationRunListeners

1. 概述(重点)

事件监听机制是观察者模式的一种,Spring的事件监听机制有几个重要的顶层接口,分别是:

  1. ApplicationEventMulticaster 主要看 AbstractApplicationEventMulticaster
  2. ApplicationListener
  3. ApplicationEvent

它们三哥们的关系可以用下面的图来概括

在这里插入图片描述
AbstractApplicationEventMulticaster维护了 源类型和事件类型 作为KEY 跟 监听器的关系,在下面 2.1 SimpleApplicationEventMulticaster有代码的实现解读。

2. ApplicationEventMulticaster

AbstractApplicationEventMulticaster
«Interface»
ApplicationEventMulticaster
«Interface»
Aware
«Interface»
BeanClassLoaderAware
«Interface»
BeanFactoryAware
SimpleApplicationEventMulticaster

目前就只有一个实现类SimpleApplicationEventMulticaster和一个抽象类AbstractApplicationEventMulticaster

2.1 SimpleApplicationEventMulticaster

发布和广播事件,主要注意一下getApplicationListeners方法就行了,会根据事件类型源类型找监听器。

@Override
public void multicastEvent(ApplicationEvent event) {
    multicastEvent(event, resolveDefaultEventType(event));
}

@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
    ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
    Executor executor = getTaskExecutor();
    for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
        if (executor != null) {
            executor.execute(() -> invokeListener(listener, event));
        }
        else {
            invokeListener(listener, event);
        }
    }
}

2.2 AbstractApplicationEventMulticaster

getApplicationListeners方法就是根据Class<?>、ResolvableType两个对象作为一个缓存key,这个key与一堆监听器`ApplicationListener`做映射,也就是说通过Class<?>、ResolvableType两个对象可以找到监听器,在用法上可以从SimpleApplicationEventMuticastermulticastEvent 方法去看。

看看getApplicationListeners方法的代码实现

protected Collection<ApplicationListener<?>> getApplicationListeners(
	    ApplicationEvent event, ResolvableType eventType) {

    Object source = event.getSource();
    Class<?> sourceType = (source != null ? source.getClass() : null);
    ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);
    CachedListenerRetriever newRetriever = null;

    // 这个cacheKey就是ApplicationEvent,ResolvableType两个对象。
    // 根据cacheKey找监听器,找不到就把cacheKey作为key,创建一个什么都么有的CachedListenerRetriever对象作为值缓存到retrieverCache中。
    // 这里有一些细节我没搞明白,putIfAbsent总是返回null。
    CachedListenerRetriever existingRetriever = this.retrieverCache.get(cacheKey);
    if (existingRetriever == null) {
        if (this.beanClassLoader == null ||
            (ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) &&
            (sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
            newRetriever = new CachedListenerRetriever();
            existingRetriever = this.retrieverCache.putIfAbsent(cacheKey, newRetriever);
            if (existingRetriever != null) {
                newRetriever = null;  // no need to populate it in retrieveApplicationListeners
            }
        }
    }

    if (existingRetriever != null) {
        Collection<ApplicationListener<?>> result = existingRetriever.getApplicationListeners();
        if (result != null) {
            return result;
        }
    }

    // 假设上面cacheKey对应的值没有监听器,那这个方面就找 给定事件类型和源类型 的监听器。
    return retrieveApplicationListeners(eventType, sourceType, newRetriever);
}

// 从默认的 defaultRetriever 和 我们注入的bean 中找监听器。
private Collection<ApplicationListener<?>> retrieveApplicationListeners(
        ResolvableType eventType, @Nullable Class<?> sourceType, @Nullable CachedListenerRetriever retriever) {

    List<ApplicationListener<?>> allListeners = new ArrayList<>();
    Set<ApplicationListener<?>> filteredListeners = (retriever != null ? new LinkedHashSet<>() : null);
    Set<String> filteredListenerBeans = (retriever != null ? new LinkedHashSet<>() : null);

    Set<ApplicationListener<?>> listeners;
    Set<String> listenerBeans;
    synchronized (this.defaultRetriever) {
    listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners);
    listenerBeans = new LinkedHashSet<>(this.defaultRetriever.applicationListenerBeans);
    }

    // 从defaultRetriever中找监听器
    // 这里的supportsEvent有点看不懂,不过只要理解通过事件类型和源类型判断监听器是否符合 事件要求就得了。
    for (ApplicationListener<?> listener : listeners) {
        if (supportsEvent(listener, eventType, sourceType)) {
            if (retriever != null) {
                filteredListeners.add(listener);
            }
            allListeners.add(listener);
        }
    }

    // 根据bean名字去找监听器
    if (!listenerBeans.isEmpty()) {
        ConfigurableBeanFactory beanFactory = getBeanFactory();
        for (String listenerBeanName : listenerBeans) {
            try {
                if (supportsEvent(beanFactory, listenerBeanName, eventType)) {
                    ApplicationListener<?> listener =
                        beanFactory.getBean(listenerBeanName, ApplicationListener.class);
                    if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) {
                        if (retriever != null) {
                            if (beanFactory.isSingleton(listenerBeanName)) {
                                filteredListeners.add(listener);
                            }
                            else {
                                filteredListenerBeans.add(listenerBeanName);
                            }
                        }
                        allListeners.add(listener);
                    }
                }
                else {
                    Object listener = beanFactory.getSingleton(listenerBeanName);
                    if (retriever != null) {
                        filteredListeners.remove(listener);
                    }
                    allListeners.remove(listener);
                }
                }
            catch (NoSuchBeanDefinitionException ex) {
            }
        }
    }

    // 把找到的监听器都放到CachedListenerRetriever中。
    AnnotationAwareOrderComparator.sort(allListeners);
    if (retriever != null) {
        if (filteredListenerBeans.isEmpty()) {
            retriever.applicationListeners = new LinkedHashSet<>(allListeners);
            retriever.applicationListenerBeans = filteredListenerBeans;
        }
        else {
            retriever.applicationListeners = filteredListeners;
            retriever.applicationListenerBeans = filteredListenerBeans;
        }
    }
    return allListeners;
}

3. ApplicationListener

主要是了解如何注册和自定义监听器就行了

3.1 注册监听器

通过META-INF/spring.factories注册监听器。

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    this.bootstrapRegistryInitializers = new ArrayList<>(
        getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    // 这里就是注册ApplicationListener的代码了,通过META-INF/spring.factories注册。
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    this.mainApplicationClass = deduceMainApplicationClass();
}

3.2 自定义

public class MyApplicationPreparedListener implements ApplicationListener<ApplicationPreparedEvent> {
    @Override
    public void onApplicationEvent(ApplicationPreparedEvent event) {
        System.out.println("开始填充上下文。。。。");
    }
}

MATE-INF/spring.factories

org.springframework.context.ApplicationListener=sample.config.MyApplicationPreparedListener

4. SpringApplicationRunListeners

SpringApplicationRunListeners管理了多个EventPublishingRunListener,EventPublishingRunListener里面包含了事件监听器模型#1.概述(重点)中描述的部分。

class SpringApplicationRunListeners {

    private final Log log;

    private final List<SpringApplicationRunListener> listeners;

    private final ApplicationStartup applicationStartup;

    ...
}

public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {

    private final SpringApplication application;

    private final String[] args;

    private final SimpleApplicationEventMulticaster initialMulticaster;

    ...
}

EventPublishingRunListener中定义了Spring整个启动过程中会触发的事件,下面对触发位置进行大概的描述,更具体的内容还有待研究。

  • starting
    在SpringApplication#run,事件ApplicationStartingEvent
  • environmentPrepared
    在SpringApplication#prepareEnvironment,事件ApplicationEnvironmentPreparedEvent
  • contextPrepared
    在SpringApplication#prepareContext,事件ApplicationContextInitializedEvent
  • contextLoaded
    在SpringApplication#prepareContext,事件ApplicationPreparedEvent
  • started
    在SpringApplication#run,事件ApplicationStartedEvent
  • ready
    在SpringApplication#run,事件ApplicationReadyEvent
  • failed
    在SpringApplication#handleRunFailure,事件ApplicationFailedEvent

我们也可以利用上面的事件,自己创建一个监听器,然后放到spring.factories,比如现在注册一个ApplicationPreparedEvent的监听器。

public class MyApplicationPreparedListener implements ApplicationListener<ApplicationPreparedEvent> {
    @Override
    public void onApplicationEvent(ApplicationPreparedEvent event) {
        System.out.println("开始填充上下文。。。。");
    }
}

MATE-INF/spring.factories

org.springframework.context.ApplicationListener=sample.config.MyApplicationPreparedListener

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

协会认证!百望云荣获信创工委会年度“卓越贡献成员单位”称号

当前&#xff0c;新一轮科技革命和产业变革正加速重塑全球经济结构&#xff0c;强化企业科技创新的主体地位&#xff0c;推动创新链、产业链、人才链深度融合&#xff0c;加快科技成果产业化进程至关重要。 近日&#xff0c;中国电子工业标准化技术协会信息技术应用创新工作委员…

对付勒索病毒,复杂的往往无法落地

一道道复杂门墙防护安全&#xff0c; 还是一个精密的锁更安全&#xff1f; &#x1f447;&#x1f447;&#x1f447; 在网络数据安全问题频发的当下&#xff0c;除了常规的备份、灾备措施以外&#xff0c;企业是否有做好应对最坏情况的准备&#xff1f;一旦病毒绕过了一道道…

shell - 免交互

一.Here Document 免交互 1. 交互的概念 交互&#xff1a;当计算机播放某多媒体程序的时候&#xff0c;编程人员可以发出指令控制该程序的运行&#xff0c;而不是程序单方面执行下去&#xff0c;程序在接受到编程人员相应的指令后而相应地做出反应。 对于Linux操作系统中&…

ztest中ddof起什么作用

⭐️ statsmodels 中 ztest 基本使用 statsmodels 也是一个强大的统计分析库&#xff0c;提供了丰富的统计模型和检验功能。对于 Z 检验&#xff0c;statsmodels 提供了 ztest 函数。 以下是使用 statsmodels 进行 Z 检验的示例&#xff1a; from statsmodels.stats.weights…

ElementUI 组件:Container 布局容器

ElementUI安装与使用指南 Container 布局容器 点击下载learnelementuispringboot项目源码 效果图 el-container.vue&#xff08;Container 布局容器&#xff09;页面效果图 项目里el-container.vue代码 <script> import PagePath from "/components/PagePat…

[NOIP2011 提高组] 聪明的质监员

[NOIP2011 提高组] 聪明的质监员 题目描述 小T 是一名质量监督员&#xff0c;最近负责检验一批矿产的质量。这批矿产共有 n n n 个矿石&#xff0c;从 1 1 1 到 n n n 逐一编号&#xff0c;每个矿石都有自己的重量 w i w_i wi​ 以及价值 v i v_i vi​ 。检验矿产的流程…

Python代码覆盖率工具

Coverage.py是一个用于测量Python程序代码覆盖率的工具。它监视您的程序&#xff0c;注意代码的哪些部分已经执行&#xff0c;然后分析源代码&#xff0c;以确定哪些代码本可以执行&#xff0c;但没有执行。 覆盖率测量通常用于衡量测试的有效性。它可以显示代码的哪些部分正在…

S275 4G网络IO模块:智能酒店的理想选择

行业背景 随着物联网技术的发展&#xff0c;酒店服务也变得更加“智能”——自动灯光效果、室内温湿度控制、各种人性化操作等贴心服务&#xff0c;带给顾客真正的宾至如归之感。 同时&#xff0c;智慧酒店更为管理者提供了高效的管理手段&#xff0c;将酒店物耗、能耗、人员…

全网最简单的幻兽帕鲁服务器搭建教程

幻兽帕鲁是一款备受欢迎的多人在线游戏&#xff0c;为了提供更好的游戏体验&#xff0c;许多玩家选择自行搭建服务器。本文将指导大家如何简单快速地搭建幻兽帕鲁服务器&#xff0c;轻松享受游戏的乐趣。 第一步&#xff1a;购买游戏联机服务器 购买入口&#xff1a;https://tx…

【八大排序】直接插入排序 | 希尔排序 + 图文详解!!

&#x1f4f7; 江池俊&#xff1a; 个人主页 &#x1f525;个人专栏&#xff1a; ✅数据结构冒险记 ✅C语言进阶之路 &#x1f305; 有航道的人&#xff0c;再渺小也不会迷途。 文章目录 一、排序的概念二、直接插入排序2.1 基本思想2.2 适用说明2.3 过程图示2.4 代码实现2.…

排序之计数排序

꒰˃͈꒵˂͈꒱ write in front ꒰˃͈꒵˂͈꒱ ʕ̯•͡˔•̯᷅ʔ大家好&#xff0c;我是xiaoxie.希望你看完之后,有不足之处请多多谅解&#xff0c;让我们一起共同进步૮₍❀ᴗ͈ . ᴗ͈ აxiaoxieʕ̯•͡˔•̯᷅ʔ—CSDN博客 本文由xiaoxieʕ̯•͡˔•̯᷅ʔ 原创 CSDN …

vue+element 换肤功能

1.首先建深色和浅色两个主题样式变量样式表&#xff0c;样式表名和按钮中传入的值一样&#xff0c;本例中起名为default.scss和dark.scss 2.在data中定义主题变量名 zTheme:‘defalut’&#xff0c;默认引用defalut.scss, 在点击按钮时切换引用的样式表&#xff0c;达到换肤效果…

Codeforces Round 884 E. Great Grids

E. Great Grids 题意 一个 n m n \times m nm 的网格图是 g o o d good good 的当且仅当&#xff1a; 每个网格的字符是 A 、 B 、 C A、B、C A、B、C 中的一种每一个 2 2 2 \times 2 22 的子格都包含三种不同的字符相邻的格子字符不一样 现在给定 k k k 个限制条件&…

【Redis】实现缓存及相关问题

Redis实现缓存及相关问题 认识缓存 缓存就是数据交换的缓冲区&#xff0c;是存贮数据的临时地方&#xff0c;一般读写性能较高。 缓存的作用&#xff1a; 降低后端负载提高读写效率&#xff0c;降低响应时间 缓存的成本&#xff1a; 数据一致性成本代码维护成本运维成本 …

PXE高效批量装机

一、系统安装 1. 系统装机的三种引导方式 1. 硬盘安装 2. 光驱&#xff08;u盘&#xff09;安装 3. 网络启动 pxe 2.系统安装过程 加载boot loader Boot Loader 是在操作系统内核运行之前运行的一段小程序。通过这段小程序&#xff0c;我们可以初始化硬件设备、建立内…

C#使用OpenCvSharp4库中5个基础函数-灰度化、高斯模糊、Canny边缘检测、膨胀、腐蚀

C#使用OpenCvSharp4库中5个基础函数-灰度化、高斯模糊、Canny边缘检测、膨胀、腐蚀 使用OpenCV可以对彩色原始图像进行基本的处理&#xff0c;涉及到5个常用的处理&#xff1a; 灰度化 模糊处理 Canny边缘检测 膨胀 腐蚀 1、测试图像lena.jpg 本例中我们采用数字图像处…

day39_mysql

今日内容 0 复习昨日 1 DML 2 约束 3 DQL 0 复习昨日 1 什么是数据库(Database)? 用来组织,存储,管理数据的仓库 2 什么是数据库管理系统(Database Management System-DBMS)? 用来管理数据库的一个软件 3 数据库分类 关系型数据库,Oracle,Mysql,SqlServer,DB2非关系数据库,Re…

深入理解Java中的ForkJoin框架原理

在现代多核处理器的时代&#xff0c;有效地利用并行计算可以极大地提高程序的性能。Java中的ForkJoin框架是Java 7引入的一个并行计算框架&#xff0c;它提供了一种简单而高效的方式来利用多核处理器。在本文中&#xff0c;我们将深入探讨ForkJoin框架的原理和工作方式。 一、什…

《区块链简易速速上手小册》第10章:区块链的未来与趋势(2024 最新版)

文章目录 10.1 区块链的未来展望10.1.1 基础知识10.1.2 主要案例&#xff1a;区块链在金融领域的发展10.1.3 拓展案例 1&#xff1a;区块链在供应链管理中的应用10.1.4 拓展案例 2&#xff1a;区块链在身份管理和隐私保护中的应用 10.2 新兴技术与区块链的融合10.2.1 基础知识1…

数据结构之图

图 图&#xff08;Graph&#xff09;是比树还要难以理解和学习的“多对多”数据结构&#xff0c;可以认为树也是图的一种。图的知识点众多&#xff0c;按照存储路径的方向分&#xff0c;可分为无向图和有向图&#xff0c;按照图的存储结构分&#xff0c;可分为完全图与有向完全…