概述
一个标准的SpringBoot应用都具有如下类似代码:
这是我自己搭建的一个项目。里面只有一个注解@SpringBootApplication。这个注解有俩个作用:
- 标明这是一个SpringBoot应用
- 开启自动配置的功能
最后通过SpringApplication的run方法启动应用。这是我们使用SpringBoot应用的的时候最常用的代码。
SpringApplication
执行项目中的启动类的run方法后,就到了org.springframework.boot.SpringApplication的run方法。代码如下:
之后再继续调用run方法。如下:
这里创建了一个SpringApplication对象,然后执行run方法。关于SpringApplication对象属性简介,代码如下:
一些简单的注释我就直接放在截图里了,此处不再过多叙述。(ps:这个是我打断点的时候截的图)
自身的构造方法如下:
构造方法中初始化了一些属性。webApplicationType的属性具体实现是通过判断是否存在指定的类来赋值。代码如下:
属性mainApplicationClass是通过调用方法,判断是哪个调用了main方法来推断并设置项目main()方法启动的主程序启动类。代码如下:
简单的看完了以后,我们关注的重点是Initializers和Listeners 属性,俩者都是通过getSpringFactoriesInstances()方法获得不同类型的数组。
启动源码的测试案例就可以看到这俩个的值。Initializers属性的值如下:
Listenners属性在SpringBoot源码的测试用例中如下图:
这里简单说一下getSpringFactoriesInstances()方法。具体如下:
主要是通过Sping.factories的配置文件来获取对应的值。这里names的值如下:org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
spring.factories部分内容截图如下:
创建对象createSpringFactoriesInstances()方法的代码具体实现如下:
排序方法就不在多说,此处是实现了Conparator进行排序。
那么此处实例化Application做了哪些事情呢?
- 将项目启动类的class设置为属性存储起来
- 设置应用类型
- 设置初始化器
- 设置监听器(初始化器和监听器最后在run方法中都会调用)
- 设置mainApplicationClass属性
创建完SpringApplication对象后,接下来看run方法。run方法整体逻辑较长。代码如下图:
从上述代码中可以看出,项目初始化启动大致包括以下几个部分:
-
获取并启动监听器
getRunListeners(args)和listeners.starting()方法主要用于获取SpringApplication实例化过程中初始化的SpringApplicationRunListener监听器,并运行。getRunListeners(args)的代码具体实现如下:
也是通过getSpringFactoriesInstances()方法来获取一个SpringApplicationRunListener类型的监听器。 -
根据SpringApplicationRunListeners以及参数来准备环境
prepareEnvironment(listeners,applicationArguments)方法主要用于对项目运行环境进行预设置,代码如下:
1. 根据不同的webApplicationType创建环境
2. 加载指定的配置文件(包括多环境配置)
3. 通知监听器,环境变量已经加载完成
4. 最后将环境绑定到SpringApplication
基本思路就是:创建环境,设置参数,通知监听器,绑定到SpringApplication
然后,通过configureIgnoreBeanInfo(environment)方法排除一些不需要的运行环境 -
创建Spring容器
根据webApplicationType判断,确定容器类型,如果是Servlet类型,则通过反射装载对应的字节码,也就是AnnotationConfigServletWebServerApplication.代码如下:
-
Spring容器的前置处理
主要是在容器刷新前的操作,比如设置容器环境,包括各种变量。最关键的操作是:将启动类注入容器,为后续开启自动配置奠定基础。具体实现如下:
主要是对上下文进行一个预处理,比如监听器的响应事件,加载资源,设置上下文环境等等。 -
刷新容器
开启刷新Spring容器,通过refresh方法对整个IOC容器的初始化(包括bean资源的丁文,解析,注册等),同时向JVM运行时注册一个关机钩子函数,在JVM关机时候能够关闭上下文,除非已经关闭。具体实现如下:
注:第四步和第五步就是用之前的context初始化设置的context(应用上下文环境),environment(项目运行环境),listeners(监听器),applicationArguments(项目参数),和printedBanner(项目图标信息)进行应用上下文的组装配置,并刷新配置。 -
Spring容器的后置处理
拓展接口,理由设计模式的模板方法,默认为空实现。如果有自定义需要,可以重新实现该方法。 -
发出结束执行的事件
获取EventPublishingRunListener监听器,并执行started方法,并且将创建的Spring容器传进去,创建一个ApplicationStartedEvent事件,然后执行ConfigurableApplicationContext的publishEvent方法。也就是说在这里是在Spring容器中发布时间,前面的starting是直接向SpringApplication中的监听器发布启动事件。 -
执行Runners
用于调用项目中自定义的执行器xxxRunners类,使得在项目启动完成后立即执行一些特点的程序。其中SpringBoot提供俩种执行器接口,分别是ApplicationRunner和CommandLineRunner。在使用时,只需要自定义一个执行器类实现其中的一个接口并重写对应的run方法接口,项目启动后悔立即执行这些特定的程序。实现如下:
总结一下,run方法的执行过程中,发生了什么事情。
第一步:获取并启动监听器主要通过getSpringFactoriesInstances()方法来获取一个SpringApplicationRunListeners类型的监听器
第二步:根据SpringApplicationRunListeners以及参数来准备环境:
1.根据不同的webApplicationType创建环境
2.加载配置文件
3.通知监听器,环境变量准备完成
4.将环境绑定到SpringApplication
5.排除不需要的运行环境条件第三步:创建Spring容器。
通过webApplicationType的不同类型创建ApplicationContext
第四步:Spring容器的前置处理
主要是对上下文进行一个预处理,比如监听器的响应时间,加载资源,设置上下文环境等等
第五步:刷新容器
开启刷新容器,对整个IOC容器进行初始化,并注册关机钩子函数
第六步:后置处理
空实现。使用设计模式的模板方法,可以自定义实现
第七步:发出结束执行的事件
这里主要是在Spring容器中发布事件,和前面的starting方法不一样,前面的是直接向SpringApplication中的监听器发布事件。
第八步:执行Runners
用于调用自定义的执行器,默认俩种执行器接口ApplicationRunner和CommandLineRunner。