SpringBoot 启动流程
启动从SpringApplication.run(MyServiceApplication.class, args);开始,然后到SpringApplication的静态run方法中new 一个 SpringApplication对象并调用它的run方法
1 | /* SpringApplication */ |
启动流程可以主要分为两大部分,一部分是初始化 就是创建SpringApplication对象,一部分是run。
首先分析初始化的过程.
初始化
1 | // resourceLoader = null, primarySource 就是一个class数组,里面存了我们的MyServiceApplication.class |
推断WebApplicationType
这里返回的是SERVLET,共有以下几种:
NONEThe application should not run as a web application and should not start an embedded web server.SERVLETThe application should run as a servlet-based web application and should start an embedded servlet web server.REACTIVEThe application should run as a reactive web application and should start an embedded reactive web server.
在我们初始化完成之后,调用run方法之前,也可以去手动指定该type:setWebApplicationType(webApplicationType)
初始化 并设置 initializers
根据META-INF/spring.factories中存储的映射关系,找到对应类型org.springframework.context.ApplicationContextInitializer的类的名字数组,完成初始化并返回。
1 | /* SpringApplication */ |
比如其中的一个 META-INFO/spring.factories 如下:jar:file:/Users/wuyingjie/.m2/repository/org/springframework/boot/spring-boot-autoconfigure/2.1.0.RELEASE/spring-boot-autoconfigure-2.1.0.RELEASE.jar!/META-INF/spring.factories

result 也就是扫描出来的所有key和类名数组如下:

org.springframework.context.ApplicationContextInitializer对应的类有:

初始化 并设置 applicationlisteners
org.springframework.context.ApplicationListener 对应的类有:

推断 主应用程序的类 也就是我们的main 函数所在的类
就是根据栈的调用信息,获取main函数所在的栈帧,最后得到类信息
1 | private Class<?> deduceMainApplicationClass() { |
SpringApplication的初始化到这里也就结束了。
其实也就是在为WebApplicationContext的初始化 做一些准备工作。
run
run方法代码如下,而且结构非常清晰。
1 | public ConfigurableApplicationContext run(String... args) { |
整个启动流程 就完成了。
那么下面就仔细的分析每一步具体做了什么。
获取listeners 并通知listeners事件
获取SpringApplicationRunListeners的方法如下:
1 | private SpringApplicationRunListeners getRunListeners(String[] args) { |
还是使用SpringFactoriesLoader类从各个 spring.factories配置文件中获取。得到的是SpringApplicationRunListener所有配置在spring.factories中的子类的数组,这里该数组只有一个元素org.springframework.boot.context.event.EventPublishingRunListener
首先,我们看下SpringApplicationRunListener,该接口提供了Application运行期各个事件的回调,也就是上面所有的事件其实都是这里发的
1 | package org.springframework.boot; |
比如说,我们看看listeners.starting();方法
1 | // SpringApplicationRunListeners |
广播逻辑,大致就是在所有的listeners中根据event找到合适的能处理该event的listeners,并调用该listener的onApplicationEvent方法
具体的广播逻辑实现方式就不分析了,可以看我的另一篇介绍springboot 事件机制的文章,
所以,如果我们想要监听SpringApplication运行期间的各种事件,我们就可以实现SpringApplicationRunListener,并仔细看接口的注释。
这里主要涉及到三个类:
SpringApplicationRunListeners类,包含了所有实现了SpringApplicationRunListener接口并通过spring.factories注册的类。SpringApplicationRunListener接口,定义监听SpringApplication运行的回调接口EventPublishingRunListener类,实现了SpringApplicationRunListener接口,是通过SpringFactoriesLoader加载的唯一实现类。
另外,这里的
SpringApplicationRunListeners中的SimpleApplicationEventMulticaster只负责SpringApplication启动过程中的事件发送,也只发送给通过SpringFactoriesLoader加载并放在它里面的listeners。我们写的自己的listeners和这些listeners都放在ApplicationContext中的listeners,并且由ApplicationContext中的SimpleApplicationEventMulticaster负责通知(他俩并不是同一个)。
创建Environment 和 context
创建ConfigurableEnvironment和ApplicationContext 都会根据之前得到的WebApplicationType。
1 | protected ConfigurableApplicationContext createApplicationContext() { |
prepareContext
1 | private void prepareContext(ConfigurableApplicationContext context, |
refresh
这里还需要着重说一下refresh方法。
上面的load方法只加载了根类 MyServiceApplication,而该类下的所有有注解的并没有加载,实际的加载是在refresh的过程中实现的。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37// AbstractApplicationContext
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
...
invokeBeanFactoryPostProcessors(beanFactory);
...
}
}
// 最后委托给了 PostProcessorRegistrationDelegate
public static void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
// 首先,需要明白,BeanFactoryPostProcessor 有两个来源,一个是 传进来的,另外一个是 从BeanFactory中拿到的。
// 代码就省了 写下大致逻辑,也是比较简单的
if (beanFactory instanceof BeanDefinitionRegistry) {
// 如果是一个可以注册的BeanFactory,
/*
1. 从参数中的 beanFactoryPostProcessors 中找实现 BeanDefinitionRegistryPostProcessor接口的 并调用该方法
2. 调用BeanFactory中的BeanDefinitionRegistryPostProcessor接口
3. 调用 BeanDefinitionRegistryPostProcessor接口的 BeanFactoryPostProcessors 的接口方法
4. 调用 参数中的 BeanFactoryPostProcessors 接口的方法
*/
} else {
// 如果不可以注册bean
// 调用所有参数的 BeanFactoryPostProcessors 接口方法
}
//调用 beanFactory中的 BeanFactoryPostProcessors 接口方法
}
而 SpringBoot 自动装配,去解析根类中的注解并注入bean的过程就发生在 BeanDefinitionRegistryPostProcessor的调用过程中。
具体实现的类是ConfigurationClassPostProcessor。 具体过程还是挺复杂的,之后可以详细分析。
refresh 方法也是非常重要的,但是不是本文的重点,具体可以去百度。
这里是主要的
BeanFactoryPostProcessor的来源,但是还有一些来自ApplicationContextInitializer实现类,在调用回调方法时注入的几个(他这么着急注入是要早点用?可是哪里都没有调用啊。。 可能是一些需要提前获取的bean在getBean方法的时候会回调这些早早就初始化好的BeanFactoryPostProcessor)。
另外,
BeanPostProcessor也是有一些需要提前注册好的。还有一些Bean,比如说spring.factories中注册的bean,还有回调或者初始化其他类的时候增加的一些BeanDefinition。如在实例化AnnotationConfigServletWebServerApplicationContext会创建AnnotatedBeanDefinitionReader对象,在实例化该对象的时候,会像BeanFactory中注册Annotion方式的BeanRegistry所必须用到的几个类,比如说ConfigurationClassPostProcessor,AutowiredAnnotationBeanPostProcessor,CommonAnnotationBeanPostProcessor等等吧。
再之后,除了还是SpringApplicationRunListeners的回调,还有一个callRunners方法。
callRunners
1 | private void callRunners(ApplicationContext context, ApplicationArguments args) { |
主要就是回调以下两个接口,对我们运行时传入的参数 加上自己的处理(如果有需要的话):
ApplicationRunner接收由ApplicationArguments包装之后的参数CommandLineRunner接收原始运行程序传入的参数
例子 网上一百度 就有了。比如说:CommandLineRunner或者ApplicationRunner接口