Spring Application实例化流程和构造方法参数

2022-10-28 16:20:32 浏览数 (1)

SpringBoot构造流程源码分析

Spring Boot 的启动非常简单,只需执行一-个简单的 main 方法即可,但在整个 main 方法中,Spring Boot 都做了些什么呢?本章会为大家详细讲解 Spring Boot 启动过程中所涉及的源代码和相关知识点。只有了解 Spring Boot 启动时都做了些什么,我们在实践过程中才能更好地运用 Spring Boot,更好地排查问题,并借鉴 Spring Boot 的设计理念进行创新。

我们再来看一下 Spring Boot 的启动入口类源代码。

代码语言:javascript复制
@SpringBootApplication
public class SpringLearnApplication {
public static void main(String[] args) {
SpringApplication. run(SpringLearnApplication. class, args);}}

在上一章中,我们通过入口类的@SpringBootApplication 注解展开讲解 了SpringBoot的核 心 机 制 。而 本 章 则 围 绕 SpringApplication 类 的 静 态 方 法 一 run 方 法 的 初 始 化 类SpringApplication 自身的功能进行讲解。

SpringApplication的初始化简介

在入口类中主要通过 SpringApplication 的静态方法一-run 方 法进行 SpringApplication类的实例化操作,然后再针对实例化对象调用另外-个 run 方法来完成整个项目的初始化和启动。本章重点围绕此过程的前半部部分(即 SpringApplication 类的实例化)来讲解。

代码语言:javascript复制
public class SpringApplication {
public static ConfigurableApplicationContext run(Class<?> primarySource,
return run(new Class<?>[] { primarySource }, args);
public static ConfigurableApplicat ionContext run(Class<?>[] primarySource
String[] args) {
//创建 SpringApplication 对象并执行其 run 方法
return new SpringApplication(primarySources). run(args);}
}

通过入口类的方法进入,可以看到 SpringApplication 的实例化只是在它提供的静态 run 方法中新建了一个 SpringApplication 对象。其中参数 primarySources 为加载的主要资源类,通常就是 Spring Boot 的入口类,args 为传递给应用程序的参数信息。

借鉴 SpringApplication 内部 run 方法的实现,我们也可以直接新建一个 SpringApplication对象,并调用其 run 方法。因此,启动程序也可以如此来写:

代码语言:javascript复制
@SpringBootApplication
public class Springl earnApplication {
public static void main(String[] args)
new SpringApplication(SpringL earnApplication.class).run(args); }}

这样写程序的一个好处便是,可以通过 SpringApplication 提供的一 -些方法(setXX 或addXX 方法)来进行指定功能的定制化设置。

下面将重点围绕 SpringApplication 类的实例化展开。

SpringApplication 实例化流程

上面我们了解了进行 SpringApplication 实例化的基本方法,下面我们先通过一-张简单的流程图来系统地学习在创建 SpringApplication 对象时都进行了哪些核心操作,如图 3-1 所示。

通过图 3-1 可以看出,在 SpringApplication 对象实例化的过程中主要做了 3 件事:参数赋值给成员变量、应用类型及方法推断和 ApplicationContext 相关内容加载及实例化。

我们结合流程图看一下 SpringApplication 两个构造方法的核心源代码。

代码语言:javascript复制
public SpringApplication(Class<?>... primarySources) {
this(nu1l, primarySources);
@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primary
Sources)
this. resourceLoader = resourceLoader;
Assert . notNull(primarySources, "PrimarySources must not be null");
this . primarySources = new LinkedHashSet<> (Arrays . asList(primarySources));
//推断 web 应用类型
this . webApplicationType = WebApplicationType. deduceF romClasspath();
//加戴并初始化 Appl icationContextInitial izer 及相关实现类
setInitializers((Collection)
getSpringFactoriesInstances (ApplicationContextInitialize
r.class));
//加戴并初始化 Appl icationL istener 及相关实现类
setListeners((Collection) getSpringFactoriesInstances (ApplicationListene
r.class));
//推断 main 方法 Class 类
this . mainApplicationClass = deduceMainApplicationClass();
}

SpringApplication 提供了两个构造方法,核心业务逻辑在第二个构造方法中实现,在后面章节我们会从构造方法中的具体实现入手进行详细讲解,先来了解 SpringApplication 的初始化过程。

SpringApplication 构造方法参数

SpringApplication 的核心构造方法有两个参数,第一个为 ResourceLoader resourcel oader,第二个 为 Class<?...primarySources.

ResourceLoader 为资源加载的接口,在 Spring Boot 启动时打印对应的 banner 信息,默认采用的就是 DefaultResourceLoader。实战过程中,如果程序未按照 Spring Boot 的“约定”将 banner 的内容放置于 classpath 下,或者文件名不是 banner.*格式,默认资源加载器是无法加载到对应的 banner 信息的,此时可通过 ResourceL oader 来指定需要加载的文件路径。

第二个参数 Class<?>.. .primarySources,为可变参数,默认传入 SpringBoot 入口类。如果作 为 项 目 的 引 导 类 , 此 参 数 传 入 的 类 需 要 满 足 一 个 条 件 , 就 是 被 注 解@EnableAutoConfiguration 或其组合注解标注。由于@SpringBootApplication 注解中包含了@EnableAutoConfiguration 注解,因此被@SpringBootApplication 注解标注的类也可作为参数传入。当然,该参数也可传入其他普通类。但只有传入被@EnableAutoConfiguration标注的类才能够开启 Spring Boot 的自动配置。

下面我们以实例来演示以其他引导类为入口类进行的 Spring Boot 项目启动。先在入口类同级创建一个 OtherApplication 类, 使用@SpringBootApplication 进行注解。

代码语言:javascript复制
@SpringBootApplication
public class OtherApplication {
}

然后在原来的入口类 SpringL earnApplication 的 main 方法中将 primarySources 参数的值由 SpringL earnApplication.class 替 换 为 OtherApplication.class, 并 将 SpringLearnApplication 类上的注解去掉。

代码语言:javascript复制
public class SpringLearnApplication {
public static void main(String[] args) {
new SpringApplication(OtherApplication. class). run(args);
}

执行 main 方法,程序依旧可完成自动配置,可以正常访问。因此,决定 Spring Boot 启动的入口类并不一定是 main 方法所在类,而是直接或间接被@EnableAutoConfiguration 标注的类。在此也证明了之前提到的@SpringBootApplication 和@EnableAutoConfiguration 入口并没有依赖关系,只是无论通过 new 创建 SpringApplication 对象再调用 run 方法或是通过 SpringApplication 的 run 方法来启动程序,都不离不开 primarySources 参数。

同时,在 SpringApplication 类中还提供 了追加 primarySources 的方法,代码如下。

代码语言:javascript复制
public void addPrimarySources (Collection<Class<?>> additionalPrimarySources) {
this . primarySources . addAll( additionalPrimarySources );}

回 到 primarySources 参 数 中 , 在 实 例 化 SpringApplication 类 过 程 中 并 没 有 对primarySources 参数做过多处理,只是将其转化为 Set 集合,并赋值给 SpringApplication的私有成员变量 Set<Class<?>> primarySources,代码如下。

代码语言:javascript复制
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primary
Sources) {
this. primarySources = new LinkedHashSet<> (Arrays . aslist (primarySources));
}

注意 SpringApplication 的私有变量 primarySources 依旧为 LinkedHashSet,它具有去重的特性。

至此,SpringApplication 构造时参 数赋值对应变量这一步便完成了 。

本文给大家讲解的内容是SpringApplication初始化简介、实例化流程和构造方法参数

  1. 下篇文章给大家讲解的是Web应用类型推断和ApplicationContextlnitializer加载;
  2. 觉得文章不错的朋友可以转发此文关注小编;
  3. 感谢大家的支持!

本文就是愿天堂没有BUG给大家分享的内容,大家有收获的话可以分享下,想学习更多的话可以到微信公众号里找我,我等你哦。

0 人点赞