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初始化简介、实例化流程和构造方法参数
- 下篇文章给大家讲解的是Web应用类型推断和ApplicationContextlnitializer加载;
- 觉得文章不错的朋友可以转发此文关注小编;
- 感谢大家的支持!
本文就是愿天堂没有BUG给大家分享的内容,大家有收获的话可以分享下,想学习更多的话可以到微信公众号里找我,我等你哦。