以debug形式了解SpringBoot启动自动配置的原理

在debug时,由于我们是对源码进行分析,因此时常会忘记自己走到哪一步(千万不要自信于自己的记性),尤其有时候我们对源码debug深度很深,再返回时一脸懵逼,因此我们最好用思维导图整理一下。后续我也会整理整合至本片文章中。

目录

1、创建SpringApplication对象

2、运行run方法

3、总结

4、关于后续


1、创建SpringApplication对象

我们对主程序类先进行debug,来看看整个流程,之后step into

以debug形式了解SpringBoot启动自动配置的原理

step into

以debug形式了解SpringBoot启动自动配置的原理

step into:会发现返回了一个参数,里面new了一个SpringApplication(),即创建SpringApplication对象

以debug形式了解SpringBoot启动自动配置的原理

 

这一步的启动流程:

  1. 创建SpringApplication对象
  2. 运行run方法

我们来看看是如何创建SpringApplication对象的:

从上一步我们force step into进去看看

以debug形式了解SpringBoot启动自动配置的原理

进入后我们一路step over(被我们step over的这些步骤都会被系统赋上默认值)

以debug形式了解SpringBoot启动自动配置的原理

走到setInitializers(),可以看出它主要是做赋值:

以debug形式了解SpringBoot启动自动配置的原理

 

同时我们可以看到setInitializers是getSpringFactoriesInstances(),它是传送了一个ApplicationContextInitializer.class

以debug形式了解SpringBoot启动自动配置的原理

getSpringFactoriesInstances()我们点进去看看,这里面有一个SpringFactoriesLoader.loadFactoryNames():

以debug形式了解SpringBoot启动自动配置的原理

我们再点进loadFactoryNames看看:

以debug形式了解SpringBoot启动自动配置的原理

其实也能看到FACTORIES_RESOURCE_LOCATION的值,这表示从这个路径下加载指定的文件

以debug形式了解SpringBoot启动自动配置的原理

从这个路径找ApplicationContextInitializer,我们可以在debug过程中发现ApplicationContextInitializer有7个值

以debug形式了解SpringBoot启动自动配置的原理

 

拿到Initializers后,进入到 setListeners():

以debug形式了解SpringBoot启动自动配置的原理

我们发现这个方法里面的参数和上一行的形式一致,说明也得从SpringFactories中取值。我们也可以从依赖中看看:

以debug形式了解SpringBoot启动自动配置的原理

我们依旧可以看看Listeners的值【需要在debug时进行赋值动作之后,才能看到值】

以debug形式了解SpringBoot启动自动配置的原理

以下是旧版源码,因此有较大差异:

以debug形式了解SpringBoot启动自动配置的原理

最后这一行,是用来决定哪个ApplicationClass是主程序类:

以debug形式了解SpringBoot启动自动配置的原理

我们点deduceMainApplicationClass()进去看看:

以debug形式了解SpringBoot启动自动配置的原理

它是从我们传入的所有配置类中,判断哪个有main方法

 

2、运行run方法

以上的流程是走完了SpringApplication(primarySources),接下来,我们将要走run(args)

以debug形式了解SpringBoot启动自动配置的原理

我们继续step into,进入run(String... args)这个方法里面:

以debug形式了解SpringBoot启动自动配置的原理

接下来我们看一些关键步骤。前两行是停止监听的操作,可以暂时不看。接下来是声明了一个IOC容器:

以debug形式了解SpringBoot启动自动配置的原理

接下来是this.configureHeadlessProperty():

以debug形式了解SpringBoot启动自动配置的原理

我们step into看看:

以debug形式了解SpringBoot启动自动配置的原理

可以看出这里是做AWT应用的

我们step over一下,接下来是声明一个SpringApplicationRunListeners,这个监听器在前面的注意getRunListeners()

以debug形式了解SpringBoot启动自动配置的原理

点击getRunListeners()进去看看:

以debug形式了解SpringBoot启动自动配置的原理

这里面有个关键方法getSpringFactoriesInstances()

继续step over,可以查看这个listeners的值:

以debug形式了解SpringBoot启动自动配置的原理

查看方式是右键:

以debug形式了解SpringBoot启动自动配置的原理

接下来我们force step into

以debug形式了解SpringBoot启动自动配置的原理

以debug形式了解SpringBoot启动自动配置的原理

 

以debug形式了解SpringBoot启动自动配置的原理

Step over我们开始进入到try体内。首先第一行是封装命令行参数:

以debug形式了解SpringBoot启动自动配置的原理

下一行是准备环境,即ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments):

以debug形式了解SpringBoot启动自动配置的原理

准备环境都准备一些什么呢?我们可以进去看看,这里我们force step into:

以debug形式了解SpringBoot启动自动配置的原理

在这里,我们创建了一个“环境”,环境的值如果有就是get,如果没有就是create。所谓的“环境”其实是指创建IOC容器运行时所需要的环境。

以debug形式了解SpringBoot启动自动配置的原理

我们可以在step over之后,即完成第一行代码后,可以在第二行查看environment的值:

以debug形式了解SpringBoot启动自动配置的原理

接下来是环境配置,不是重点。下一步是创建环境完成后,回调SpringApplicationRunListener.environmentPrepared(),用以表示环境准备完成:

以debug形式了解SpringBoot启动自动配置的原理

接下来一路放行,再次回到上一步,此时环境已经创建成功。下一步是在debug控制台打印springboot的banner,下图是执行之前的情况:

以debug形式了解SpringBoot启动自动配置的原理

下图是执行后的情况:

以debug形式了解SpringBoot启动自动配置的原理

下一步比较关键,是创建IOC容器。即创建ApplicationContext,它是用来决定创建web的ioc还是普通的ioc:

以debug形式了解SpringBoot启动自动配置的原理

我们force step into进去看看

以debug形式了解SpringBoot启动自动配置的原理

createApplicationContext()主要是利用switch-case语句来判断到底是创建web的IOC(又分为SERVLET还是REACTIVE类型)还是普通的IOC。最后,再利用BeanUtils的反射机制创建出IOC容器。继续放行,接下来的一步是做异常处理报告的,暂时不管。在旧版源码1.x中,这部分为

以debug形式了解SpringBoot启动自动配置的原理

而在2.x版本里已经变为如下代码

以debug形式了解SpringBoot启动自动配置的原理

下一步是准备上下文,我们进入一探究竟:

以debug形式了解SpringBoot启动自动配置的原理

进入prepareContext()方法体中,其第一步是将我们之前创建的environment放入到IOC容器中:

以debug形式了解SpringBoot启动自动配置的原理

第二步是IOC容器的后置处理:

以debug形式了解SpringBoot启动自动配置的原理

再进入到其中,发现是注册一些小组件:

以debug形式了解SpringBoot启动自动配置的原理

第三步关键,是this.applyInitializers(context):

以debug形式了解SpringBoot启动自动配置的原理

它是干嘛的呢,我们进去看看:

以debug形式了解SpringBoot启动自动配置的原理

第一步是获取到所有的Initializers,第二步就是调用initialize(),是否有想过,这些Initializers是哪里有的呢?实际上就是前文提到的在创建SpringApplication对象时,从类路径下找

于是我们总结出来,applyInitializers()是回调之前保存的所有的ApplicationContextInitializer的initialize方法。

接下来我们step out,回到上一层:

以debug形式了解SpringBoot启动自动配置的原理

走到这一步,我们发现还要回调所有的SpringApplicationRunListener的contextPrepared(),进入:

以debug形式了解SpringBoot启动自动配置的原理

我们step out,继续走。下一步就是开始进行日志记录:

以debug形式了解SpringBoot启动自动配置的原理

下一步是从IOC容器中取值:

以debug形式了解SpringBoot启动自动配置的原理

这个beanFactory先把命令行参数对象applicationArguments注册进来,之后再把springBootBanner和allowBeanDefinitionOverriding注册进来。最后一步是判断这次的初始化是不是为“延迟初始化”,是的话,给上下文addBeanFactoryPostProcessor。不过我们可以看到这里为false:

以debug形式了解SpringBoot启动自动配置的原理

接下来的一步就是获取主类:

以debug形式了解SpringBoot启动自动配置的原理

当prepareContext()走到最后一步,所有的listeners开始回调contextLoaded()即说明我们的环境已经准备好了:

以debug形式了解SpringBoot启动自动配置的原理

于是我们总结出: prepareContext运行完成以后回调所有的SpringApplicationRunListener的contextLoaded();

走完以上的流程,我们才把prepareContext()走完。

是否感觉整个流程是比较乱的呢?这是因为我们在debug时,进入源码的深度时深时浅,因此思维上容易乱掉,最好是画思维导图整理一下

 

此时我们再看看控制台,发现环境也已经准备好了

以debug形式了解SpringBoot启动自动配置的原理

下一步就是刷新容器,对IOC容器初始化(如果是web应用还会创建嵌入式的Tomcat)

以debug形式了解SpringBoot启动自动配置的原理

以debug形式了解SpringBoot启动自动配置的原理

 

我们进入看看:

以debug形式了解SpringBoot启动自动配置的原理

我们再进入到第一行的refresh()中

以debug形式了解SpringBoot启动自动配置的原理

我们走到下一行后进入看看:

以debug形式了解SpringBoot启动自动配置的原理

再进入其中:

以debug形式了解SpringBoot启动自动配置的原理

再进入:(就是这么绕!)

以debug形式了解SpringBoot启动自动配置的原理

这个就是我们的IOC容器的整个的初始化过程。接下来我们一路step out 跳回到refreshContext()之初

接下来一步就是afterRefresh(),我们进入看看:

以debug形式了解SpringBoot启动自动配置的原理

其中有一个callRunners()方法,第2、3行分别是获取了服务器的ApplicationRunner和CommandLineRunner

以debug形式了解SpringBoot启动自动配置的原理

最后再进行回调。而且注意,这里的两个Runner是有优先级的:ApplicationRunner先回调,CommandLineRunner再回调

于是我们总结出:

afterRefresh()是从ioc容器中获取所有的ApplicationRunner和CommandLineRunner进行回调。注意!不是从SpringFactory配置中获取的

接下来我们继续回到afterRefresh,走下一步listeners.started(context):

以debug形式了解SpringBoot启动自动配置的原理

进入:

以debug形式了解SpringBoot启动自动配置的原理

我们看到在这个方法里面,主要是调用SpringApplicationRunListener.最后我们让所有的SpringApplicationRunListener回调finished方法:

以debug形式了解SpringBoot启动自动配置的原理

并且再返回这个IOC容器:

以debug形式了解SpringBoot启动自动配置的原理

于是整个流程就结束了,我们对整个项目的debug也就结束了

以debug形式了解SpringBoot启动自动配置的原理

3、总结

整个流程有2个核心:

一个是监听机制的使用,重要的有两个:

ApplicationContextInitializer和SpringApplicationRunListener,这两个是需要先配置在META-INF/spring.factories

还有另外两个:ApplicationRunner和CommandLineRunner,这两个只需要放在ioc容器中

第二个是 refreshContext(context),是用以扫描,创建,加载所有组件的地方(包括配置类,组件,自动配置)

 

4、关于后续

简单的启动过程中,就让人感叹自动配置的强大之所在。后续我也会对整个流程重新梳理整理成流程图放在本篇文章中,同时不断完善其中的诸多细节。