项目启动入口
实际执行的内容是通过SpringApplication类的静态方法创建一个ConfigurableApplicationContext,顾名思义,即可配置的对象容器,也就是Springboot中的上下文
1 | java复制代码/** |
SpringApplication类
SpringApplication主要的构造函数及初始化过程
在初始化的时候会初始化他的成员变量,成员变量会先取默认值,以及加载初始化器和监听器
加载的方式都是通过getSpringFactoriesInstances方法调用loadSpringFactories从”META-INF/spring.factories”文件读入要加载的类的全路径类型,获取到全路径类名之后,通过如下代码,根据全路径类名通过反射的方式创建相应的bean:
下面这个方法就是通过反射来创建bean实例了:
1 | java复制代码private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, |
在通过构造函数创建SpringApplication类的实例时,会先加载一部分能够加载的资源
SpringApplication对象的run方法
先附上这个方法的完整代码,通过注释可以知道这个方法的作用就是创建和刷新一个新的ApplicationContext,也就是Springboot的上下文bean容器
1 | java复制代码/** |
1.创建计时器开始计时
StopWatch会记录项目开始启动到启动完毕的时间,我们在启动项目的时候日志里面会有一行日志输出启动时间就是通过StopWatch实现的
2.加载SpringApplicationRunListeners
首先第一步是:通过SpringFactoriesLoader 到META-INF/spring.factories查找并加载所有的SpringApplicationRunListeners,通过start()方法通知所有的SpringApplicationRunListener,本质上这是一个事件发布者,他在SpringBoot应用启动的不同阶段会发布不同的事件类型。
SpringApplicationRunListener接口只有一个实现类EventPublishingRunListener,也就是说SpringApplicationRunListeners类的List listeners中只会生成一个EventPublishingRunListener实例。那么SpringApplicationRunListener是如何发布事件类型的呢?首先我们看下SpringApplicationRunListener这个接口。接口每个方法的注释上面都将其调用的时机交代得很清楚
SpringApplicationRunListener监听器SpringBoot应用启动的不同阶段都会有相应的监听通知。通知贯穿了SpringBoot应用启动的完成过程
1 | java复制代码public interface SpringApplicationRunListener { |
3.创建并配置当前应用将要使用的环境
1 | scss复制代码private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, |
创建并配置当前应用的环境(Environment),Environment用于描述应用程序当前的运行环境,其抽象了两方面的内容:
配置文件(profile)和属性(properties),我们知道不同的环境(开发环境,测试环境,发布环境)可以使用不同的属性配置,这些属性配置可以从配置文件,环境变量,命令行参数等来源获取。因此,当Environment准备好之后,在整个应用的任何时候,都可以获取这些属性。
所以,这一步的做的事情主要有三件:
- 获取创建的环境(Environment),如果没有则创建,如果是web环境则创建StandardServletEnvironment,如果不是的话则创建StandardEnvironment。
- 配置环境(Environment):主要是配置profile和属性properties
- 调用SpringApplicationRunListener的environmentPrepared方法,通知事件监听者:应用环境(Environment)已经准备好了。
4.根据WebApplicationType创建ApplicationContext容器
最终的创建代码是一段lambda表达式,根据对应的webApplicationType创建ApplicationContextFactory
1 | java复制代码ApplicationContextFactory DEFAULT = (webApplicationType) -> { |
5.初始化ApplicationContext
前面个步骤已经创建好了与本应用环境相匹配的ApplicationContext实例,那么接下来就是对ApplicationContext进行初始化了。这一步也是比较核心的一步。首先让我们来看看实现逻辑的相关代码:
1 | java复制代码private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context, |
以上就是初始化ApplicationContext的主要逻辑,主要有如下逻辑:
- 将准备好的Environment设置给ApplicationContext
- 遍历调用所有的ApplicationContextInitializer的 initialize() 方法来对已经创建好的 ApplicationContext 进行进一步的处理
- 调用SpringApplicationRunListeners的 contextPrepared() 方法,通知所有的监听者,ApplicationContext已经准备完毕
- 将applicationArguments实例注入到IOC容器。
- 将printedBanner实例注入到IOC容器,这个就是之前生成的Banner的实例。
- 加载资源,这里的资源一般是启动类xxxApplication
- 将所有的bean加载到容器中
- 通知所有的监听者:ApplicationContext已经装载完毕。
6.调用ApplicationContext的refresh() 方法
上下文这个步骤将会解析xml配置以及java配置,从而把Bean的配置解析成为BeanDefinition,将从而把Bean的配置解析成为BeanDefinition加载到上下文容器。这里的 SpringApplication的 refresh方法最终还是调用到AbstractApplicationContext的refresh方法。
AbstractApplicationContext的refresh方法:
1 | java复制代码@Override |
最核心的一步,将之前通过@EnableAutoConfiguration获取的所有配置以及其他形式的IoC容器配置加载到已经准备完毕的ApplicationContext。ioc容器的refresh过程先做一个小结。我们知道了上下文和Bean容器是继承关系又是组合关系。refreshContext的核心就是为了加载BeanDefinition,而加载BeanDefinition将从main方法所在的主类开始,主类作为一个配置类将由ConfigurationClassParser解析器来完成解析的职责
本文已参与「新人创作礼」活动, 一起开启掘金创作之路。
本文转载自: 掘金