首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

构造流程源码分析:ApplicationListener加载

ApplicationListener加载

完成了 ApplicationContextlnitializer 的加载之后,便会进行 ApplicationListener 的加载。它的常见应用场景为:当容器初始化完成之后,需要处理一些如数据的加载、初始化缓存、特定任务的注册等操作。而在此阶段,更多的是用于 ApplicationContext 管理 Bean 过程的场景。

Spring 事件传播机制是基于观察者模式(Observer) 实现的。比如,在 ApplicationContext管 理 Bean 生 命 周 期 的 过 程 中 , 会 将 一 些 改 变 定 义 为 事 件 ( ApplicationEvent ) 。

ApplicationContext 通过 ApplicationListener 监听 ApplicationEvent,当事件被发布之后,ApplicationListener 用来对事件做出具体的操作。

ApplicationListener 的整个配置和加载流程与 ApplicationContexthnitializer 完全一致, 也是 先 通 过SpringFactoriesLoader的loadFactoryNames方 法 获 得META-INF/spring.factories 中对应配置,然后再进行实例化,最后将获得的结果集合添加到SpringApplication 的成员变量 listeners 中,代码如下。

同样的,在调用 setListeners 方法时也会进行覆盖赋值的操作,之前加载的内容会被清除。

下 面 我 们 看 看 ApplicationListener 这里的基本使 用 。ApplicationListener 接 口 和ApplicationEvent 类配合使用,可实现 ApplicationContext 的事件处理。如果容器中存在AplicationListener 的 Bean,当 ApplicationContext 调用 publishEvent 方法时,对应的 Bean会被触发。这就是上文提到的观察者模式的实现。

在接口 ApplicationL istener 中只定义了一个 onAplicationEvent 方法,当监听事件被触发时,onApplicationEvent 方法会被执行,接口 ApplicationListener 的源代码如下。

onApplicationEvent 方法一般用于处理应用程序事件,参数 event 为 ApplicationEvent 的子类,是具体响应(接收到)的事件。

当 ApplicationContext 被初始化或刷新时,会触发 ContextRefreshedEvent 事件,下面我们就实现一-个 ApplicationListener 来监听此事件的发生,代码如下。

上面的 LearnListener 实现了 ApplicationListener 并监听 ContextRefreshedEvent 事件,当容器创建或刷新时,该监听器的 onApplicationEvent 方法会被调用,并打印出当前容器中 Bean 的数量。

在具体的实战业务中,我们也可以自定义事件,在完成业务之后手动触发对应的事件监听器,也就是手动调用 ApplicationContext 的 publishEvent(ApplicationEvent event)方法。

入口类推断

创 建 SpringApplication 的 最 后 一 步 便 是 推 断 入 口 类 , 我 们 通 过 调 用 自 身 的deduce-MainApplicationClass 方法来进行入口类的推断。

该方法实现的基本流程就是先创建一个运行时异常, 然后获得栈数组,遍历栈数组,判断类的方法中是否包含 main 方法。第-个被匹配的类会通过 Class.forName 方法创建对象,并 将 其 被 返 回 , 最 后 在 上 层 方 法 中 将 对 象 赋 值 给 SpringApplication 的 成 员 变 量mainApplicationClass。在遍历过程中如果发生异常,会忽略掉该异常并继续执行遍历操作。

至此,整个 SpringApplication 类的实例化过程便完成 了。

SpringApplication的定制化配置

前面我们学习了 Spring Boot 启动过程中构建 SpringApplication 所做的一系列初始化操作,这些操作都是 Spring Boot 默认配置的。如果在此过程中需要定制化配置,Spring Boot 在SpringApplication 类中也提供了相应的入口。

但正常情况下,如果无特殊需要,采用默认配置即可。

针对定制化配置,Spring Boot 提供了如基于入口类、配置文件、环境变量、命令行参数等多种形式。下面我们了 解一下几种不同的配置形式。

基础配置

基础配置与在 application.properties 文件中的配置-样, 用来修改 SpringBoot 预置的参数。

比如,我们想在启动程序的时候不打印 Banner 信息,可以通过在 application.properties 文件 中 设 置 “spring.main.banner-mode=off 来 进 行 关 闭 。当 然 , 我 们 也 可 以 通 过SpringApplication 提供的相关方法来进行同样的操作。以下是官方提供的关闭 Banner 的代码。

除了前面讲到的 setInitializers 和 setL isteners 方法之外,其他的 Setter 方法都具有类似的功能,比如我们可以通过 setWebApplicationType 方法来代替 Spring Boot 默认的自动类型推断。

针对这些 Setter 方法,SpringBoot 还专门提供了流式处理类 SpringApplicationBuilder,我们将它的功能与 SpringApplication 逐一对照,可知 SpringApplicationBuilder 的优点是使代码更加简洁、流畅。

其他相同配置形式的功能就不再赘述了,我们可通过查看源代码进行进一步的学习。出于集中配置、方便管理的思路, 不建议大家在启动类中配置过多的参数。比如,针对 Banner的设置,我们可以在多处进行配置,但为了方便管理,尽可能的统一在 application.properties文件中。

配置源配置

除了直接通过 Setter 方法进行参数的配置,我们还可以通过设置配置源参数对整个配置文件或配置类进行配置。我们可通过两个途径进行配置:

SpringApplication 构造方法参数或 SpringApplication 提供的 setSources 方法来进行设置。

在 3.3 节 SpringApplication 构造方法参数中已经讲到可以通过 Class

因此,配置类可通过 SpringApplication 的构造方法来进行指定。但这种方法有一一个弊端就是无法指定 XML 配置和基于 package 的配置。

另外一种配置形式为直接调用 setSources 方法来进行设置,方法源代码如下。

该方法的参数为 String 集合,可传递类名、package 名称和 XML 配置资源。下面我们以类名为例进行演示。

WithoutAnnoConfiguration 配置类代码如下。

使用该配置的实例代码如下。

运行程序,我们在日志中即可看到已经获取到对应类的属性值。

无论是通过构造参数的形式还是通过 Setter 方法的形式对配置源信息进行指定,在 SpringBoot 中都会将其合并。SpringApplication 类中提供了 一个 getAllSources 方法,能够将两者参数进行合并。

关于 SpringApplication 类指定配置及配置源就讲到这里,更多相关配置信息可参考对应章节进行学习。

小结

本章内容重点围绕 SpringApplication 类的初始化过程展开,详细介绍了在初始化过程中Spring Boot 所 进 行 的 操 作 : Web应用类型推断 、 入 口类 推 断 、 默认的Application-Contextlnitializer 接口加载、默认的 ApplicationListener 加载、SpringApplication类的参数配置功能, 以及针对这些操作我们能够进行的自定义组件及配置。建议大家在学习的过程中可配合相应的实战练习,获得更好的学习效果。

本文给大家讲解的内容是ApplicationListener加载和入口类推断、SpringApplication 的定制化配置

下篇文章给大家讲解的是SpringBoot运行流程源码分析;

觉得文章不错的朋友可以转发此文关注小编;

感谢大家的支持!

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20210225A0AR0100?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券