快捷搜索:  汽车  科技

开发环境的搭建步骤:构造流程源码分析

开发环境的搭建步骤:构造流程源码分析当 ApplicationContext 被初始化或刷新时,会触发 ContextRefreshedEvent 事件,下面我们就实现一-个 ApplicationListener 来监听此事件的发生,代码如下。@FunctionalInterface public interface ApplicationListener<E extends ApplicationEvent> extends Ev entListener void onApplicationEvent(E event);}onApplicationEvent 方法一般用于处理应用程序事件,参数 event 为 ApplicationEvent 的子类,是具体响应(接收到)的事件。private List<ApplicationListener<?>> listeners; public

applicationListener加载

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

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

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

开发环境的搭建步骤:构造流程源码分析(1)

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

private List<ApplicationListener<?>> listeners; public void setl isteners(Collection<了 extends App. lcationLi stener<?》>li steners) this. listeners = new Arraylist<>(listeners);

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

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

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

@FunctionalInterface public interface ApplicationListener<E extends ApplicationEvent> extends Ev entListener void onApplicationEvent(E event);}

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

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

@Component //需对该类进行 Bean 的实例化 public class LearnListener implements ApplicationListener<ContextRefreshedE vent> @Override public void onApplicationEvent(ContextRefreshedEvent event) { /打印容中出 Beon8 得空婴中初始化 ystem. out. println(" 监听器获得容器 event. getApplica tion- Context(). getBeanDefinitionCount()); }

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

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

开发环境的搭建步骤:构造流程源码分析(2)

入口类推断

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

private Class<?> deduceMainApplicationClass() { /获取栈元素数组 StackTraceElement[] stackTrace = new Runt imeException() . getStackTrace () //遍历栈元素数组 for (StackTraceElement stackTraceElement : stackTrace) { //匹配第一个 main 方法,并返回 ("main" . equals(stackTraceElement,getMethodName())) { return Class. forName( stackTraceElement . getClassName()); } catch (ClassNotFoundException ex) { //如果发生异常,忽略该异常,并继续执行 return null;}

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

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

开发环境的搭建步骤:构造流程源码分析(3)

SpringApplication的定制化配置

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

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

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

基础配置

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

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

public static void main(String[] args) { SpringApplication app = new SpringApplication(MySpringConfiguration.clas s); app. setBannerMode( Banner .Mode.0FF); app. run(args); }

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

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

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

配置源配置

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

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

在 3.3 节 SpringApplication 构造方法参数中已经讲到可以通过 Class<?...primarySources参数来配置普通类。

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

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

private Set<String> sources = new LinkedHashSet<>(); public void setSources (Set<String> sources) { Assert. notNull(sources "Sources must not be nul1"); this. sources = new L inkedHashSet<> ( sources); }

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

WithoutAnnoConfiguration 配置类代码如下。

public class WithoutAnnoConfiguration public WithoutAnnoConfiguration(){ System. out. println( "Wi thoutAnnoConfiguration 对象被创建"); @Value("${ admin. name}") private String name ; @Value( "${admin. age}") private int age; //省略 getter/setter 方法 }

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

public static void main(String[] args){ SpringApplication app = new SpringApplication(SpringLearnApplication.class); Set<String> set = new HashSet<>(); set. add (WithoutAnnoConfiguration. class . getName()); app . setSources(set); ConfigurableApplicat ionContext context = app . run(args); WithoutAnnoConfiguration bean = context . getBean(WithoutAnnoConfiguration. class);System. out. println(bean. getName()); }

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

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

public Set<0bject> getAllSources() { //创建去除的 L inkedHashSet Set<0bject> allSources = new LinkedHashSet<>(); // primarySources 不为空则加入 Set if (!CollectionUtils. isEmpty(this. primarySources)) { allSources . addAll(this . primarySources); // sources 不为空则加入 Set if (!CollectionUtils . isEmpty(this. sources)) { allSources . addAll(this. sources); //对 Set 进行包装,变为不可变的 Set return Collections . unmodifiableSet(allSources);}}

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

小结

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

本文给大家讲解的内容是ApplicationListener加载和入口类推断、SpringApplication 的定制化配置
  1. 下篇文章给大家讲解的是SpringBoot运行流程源码分析;
  2. 觉得文章不错的朋友可以转发此文关注小编;
  3. 感谢大家的支持!

猜您喜欢: