【Spring Boot 源码学习】ApplicationListener 详解

2023-12-11 02:01

本文主要是介绍【Spring Boot 源码学习】ApplicationListener 详解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Spring Boot 源码学习系列

在这里插入图片描述

ApplicationListener 详解

  • 引言
  • 往期内容
  • 主要内容
    • 1. 初识 ApplicationListener
    • 2. 加载 ApplicationListener
    • 3. 响应应用程序事件
  • 总结

引言

书接前文《初识 SpringApplication》,我们从 Spring Boot 的启动类 SpringApplication 上入手,了解了 SpringApplication 实例化过程。其中,《BootstrapRegistryInitializer 详解》 和 《ApplicationContextInitializer 详解》博文中,Huazie 已经带大家详细分析了 BootstrapRegistryInitializerApplicationContextInitializer 的加载和初始化过程,如下还有 2.5 还未详细分析:
在这里插入图片描述
那本篇博文就主要围绕 2.5 的内容展开,详细分析一下 ApplicationListener 的加载和处理应用程序事件的逻辑。

在这里插入图片描述

往期内容

在开始本篇的内容介绍之前,我们先来看看往期的系列文章【有需要的朋友,欢迎关注系列专栏】:

Spring Boot 源码学习
Spring Boot 项目介绍
Spring Boot 核心运行原理介绍
【Spring Boot 源码学习】@EnableAutoConfiguration 注解
【Spring Boot 源码学习】@SpringBootApplication 注解
【Spring Boot 源码学习】走近 AutoConfigurationImportSelector
【Spring Boot 源码学习】自动装配流程源码解析(上)
【Spring Boot 源码学习】自动装配流程源码解析(下)
【Spring Boot 源码学习】深入 FilteringSpringBootCondition
【Spring Boot 源码学习】OnClassCondition 详解
【Spring Boot 源码学习】OnBeanCondition 详解
【Spring Boot 源码学习】OnWebApplicationCondition 详解
【Spring Boot 源码学习】@Conditional 条件注解
【Spring Boot 源码学习】HttpEncodingAutoConfiguration 详解
【Spring Boot 源码学习】RedisAutoConfiguration 详解
【Spring Boot 源码学习】JedisConnectionConfiguration 详解
【Spring Boot 源码学习】初识 SpringApplication
【Spring Boot 源码学习】Banner 信息打印流程
【Spring Boot 源码学习】自定义 Banner 信息打印
【Spring Boot 源码学习】BootstrapRegistryInitializer 详解
【Spring Boot 源码学习】ApplicationContextInitializer 详解

主要内容

注意: 以下涉及 Spring Boot 源码 均来自版本 2.7.9,其他版本有所出入,可自行查看源码。

1. 初识 ApplicationListener

我们先来看看 ApplicationListener 接口的源码【spring-context-5.3.25.jar】:

@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());}
}

从上述代码,我们可以看到 ApplicationListener 接口被 @FunctionalInterface 注解修饰。

知识点: @FunctionalInterfaceJava 8 中引入的一个注解,用于标识一个函数式接口。函数式接口是只有一个抽象方法的接口,常用于实现 Lambda 表达式和方法引用。
使用 @FunctionalInterface 注解可以向编译器指示该接口是一个函数式接口,从而在编译时进行类型检查,确保该接口 只包含一个抽象方法。此外,该注解还可以为函数式接口生成特殊的方法,如默认方法(default method)和 静态方法(static method),这些方法可以在接口中提供更多的功能,这里就不赘述了,感兴趣的朋友可以自行查阅相关函数式接口的资料。

ApplicationListenerSpring 中应用程序事件监听器实现的接口。它基于观察者设计模式的java.util.EventListener 接口的标准。在注册到 Spring ApplicationContext 时,事件将进行相应的过滤,只有匹配的事件对象才会使该监听器被调用。

ApplicationListener 接口中,我们可以看到它定义了一个 onApplicationEvent(E event) 方法,当监听事件被触发时,onApplicationEvent 方法就会被调用执行。onApplicationEvent 方法一般用于处理应用程序事件,参数 eventApplicationEvent 的子类,也就是具体要响应处理的各种类型的应用程序事件。例如,当某个特定事件发生时,你可能想要记录日志、更新数据库、发送电子邮件等等。

另外,ApplicationListener 接口还提供了一个静态方法 forPayload(Consumer<T> consumer),用于创建一个新的 ApplicationListener 实例。这个方法接受一个 Consumer<T> 类型的参数,这个参数是一个函数接口,它接受一个泛型参数 T,并对其执行一些操作。通过这个方法,你可以将一个 Consumer 函数作为参数,然后返回一个对应的事件监听器。这个监听器会在事件发生时,调用 Consumer 函数处理事件的有效载荷【即事件中包含的有效信息或数据】。

2. 加载 ApplicationListener

setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

上述代码是 SpringApplication 的核心构造方法中的逻辑,它用于加载实现了 ApplicationListener 接口的监听器实例集合,并将该监听器实例集合设置到 SpringApplicationlisteners 变量中。

private List<ApplicationContextInitializer<?>> initializers;

我们进入 getSpringFactoriesInstances 方法,查看如下:

在这里插入图片描述

我们看到了如下的代码 :

SpringFactoriesLoader.loadFactoryNames(type, classLoader);

这里是通过 SpringFactoriesLoader 类的 loadFactoryNames 方法来获取 META-INF/spring.factories 中配置 key 为 org.springframework.context.ApplicationListener 的数据;

我们以 spring-boot-autoconfigure-2.7.9.jar 为例:

在这里插入图片描述

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer

3. 响应应用程序事件

这里我们需要查看 SpringApplicationrun(String... args) 方法,如下所示:

在这里插入图片描述

我们看上面的 SpringApplicationRunListeners ,其内的 listeners 变量是 SpringApplicationRunListener 接口的集合,如下所示:

在这里插入图片描述

SpringApplicationRunListener 接口的一个实现就是 EventPublishingRunListener 类,该类的作用就是根据 Spring Boot 程序启动过程的 不同阶段 发布对应的事件,然后由不同的实现 ApplicationListener 接口的应用程序监听器,来处理对应的事件【有关 SpringApplicationRunListener 监听器的内容,我们后续博文中会详细介绍,这里不展开了】。

如下图是 SpringApplicationRunListeners 类中的方法,它们分别对应了 Spring Boot 程序启动过程中要发布的不同阶段的事件的逻辑。

在这里插入图片描述

  • starting :当 run 方法第一次被执行时,该方法会立即被调用,可用于非常早期的初始化工作
  • environmentPrepared :当 environment 准备完成,在 ApplicationContext 创建之前,该方法被调用
  • contextPrepared :当 ApplicationContext 构建完成,资源还未被加载时,该方法被调用
  • contextLoaded :当 ApplicationContext 加载完成,未被刷新之前,该方法被调用
  • started :当 ApplicationContext 刷新并启动之后,CommandLineRunnerApplicationRunner 未被调用之前,该方法被调用
  • ready :当所有准备工作就绪,run 方法执行完成之前,该方法被调用
  • failed :当应用程序出现错误时,该方法被调用

我们以 starting 方法的逻辑为例,看一下 ApplicationStartingEvent 事件发布并被处理的过程。

void starting(ConfigurableBootstrapContext bootstrapContext, Class<?> mainApplicationClass) {doWithListeners("spring.boot.application.starting", (listener) -> listener.starting(bootstrapContext),(step) -> {if (mainApplicationClass != null) {step.tag("mainApplicationClass", mainApplicationClass.getName());}});
}

我们继续看 doWithListeners 方法:

在这里插入图片描述

结合上面的截图,我们重点看下这行:

(listener) -> listener.starting(bootstrapContext)

这里时调用了 SpringApplicationRunListener 接口的 starting 方法:

在这里插入图片描述

这里的 multicastEvent 方法就是用来发布一个指定的应用程序事件,比如这里发布的就是 ApplicationStartingEvent 事件。

在这里插入图片描述
在这里插入图片描述

总结

本篇 Huazie 带大家详细分析了 ApplicationListener 的加载和处理应用程序事件,这对于后续的 SpringApplication 运行流程的理解至关重要。

这篇关于【Spring Boot 源码学习】ApplicationListener 详解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/479076

相关文章

SpringBoot集成Milvus实现数据增删改查功能

《SpringBoot集成Milvus实现数据增删改查功能》milvus支持的语言比较多,支持python,Java,Go,node等开发语言,本文主要介绍如何使用Java语言,采用springboo... 目录1、Milvus基本概念2、添加maven依赖3、配置yml文件4、创建MilvusClient

浅析Java中如何优雅地处理null值

《浅析Java中如何优雅地处理null值》这篇文章主要为大家详细介绍了如何结合Lambda表达式和Optional,让Java更优雅地处理null值,感兴趣的小伙伴可以跟随小编一起学习一下... 目录场景 1:不为 null 则执行场景 2:不为 null 则返回,为 null 则返回特定值或抛出异常场景

python logging模块详解及其日志定时清理方式

《pythonlogging模块详解及其日志定时清理方式》:本文主要介绍pythonlogging模块详解及其日志定时清理方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地... 目录python logging模块及日志定时清理1.创建logger对象2.logging.basicCo

前端CSS Grid 布局示例详解

《前端CSSGrid布局示例详解》CSSGrid是一种二维布局系统,可以同时控制行和列,相比Flex(一维布局),更适合用在整体页面布局或复杂模块结构中,:本文主要介绍前端CSSGri... 目录css Grid 布局详解(通俗易懂版)一、概述二、基础概念三、创建 Grid 容器四、定义网格行和列五、设置行

Node.js 数据库 CRUD 项目示例详解(完美解决方案)

《Node.js数据库CRUD项目示例详解(完美解决方案)》:本文主要介绍Node.js数据库CRUD项目示例详解(完美解决方案),本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考... 目录项目结构1. 初始化项目2. 配置数据库连接 (config/db.js)3. 创建模型 (models/

SQL表间关联查询实例详解

《SQL表间关联查询实例详解》本文主要讲解SQL语句中常用的表间关联查询方式,包括:左连接(leftjoin)、右连接(rightjoin)、全连接(fulljoin)、内连接(innerjoin)、... 目录简介样例准备左外连接右外连接全外连接内连接交叉连接自然连接简介本文主要讲解SQL语句中常用的表

SpringMVC获取请求参数的方法

《SpringMVC获取请求参数的方法》:本文主要介绍SpringMVC获取请求参数的方法,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下... 目录1、通过ServletAPI获取2、通过控制器方法的形参获取请求参数3、@RequestParam4、@

shell编程之函数与数组的使用详解

《shell编程之函数与数组的使用详解》:本文主要介绍shell编程之函数与数组的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录shell函数函数的用法俩个数求和系统资源监控并报警函数函数变量的作用范围函数的参数递归函数shell数组获取数组的长度读取某下的

SpringBoot应用中出现的Full GC问题的场景与解决

《SpringBoot应用中出现的FullGC问题的场景与解决》这篇文章主要为大家详细介绍了SpringBoot应用中出现的FullGC问题的场景与解决方法,文中的示例代码讲解详细,感兴趣的小伙伴可... 目录Full GC的原理与触发条件原理触发条件对Spring Boot应用的影响示例代码优化建议结论F

Python中局部变量和全局变量举例详解

《Python中局部变量和全局变量举例详解》:本文主要介绍如何通过一个简单的Python代码示例来解释命名空间和作用域的概念,它详细说明了内置名称、全局名称、局部名称以及它们之间的查找顺序,文中通... 目录引入例子拆解源码运行结果如下图代码解析 python3命名空间和作用域命名空间命名空间查找顺序命名空