Spring Boot启动过程及回调接口汇总 链接:
来自:
注:本文基于, 撰写。
启动顺序
Spring boot的启动代码一般是这样的:
1 2 3 4 5 6 | @SpringBootApplication public class SampleApplication { public static void main(String[] args) throws Exception { SpringApplication.run(SampleApplication. class , args); } } |
初始化SpringApplication
1、SpringApplication#run(Object source, String... args)
2、 -> SpringApplication(sources)
3、SpringApplication#initialize(Object[] sources)
4、 添加source(复数),SpringApplication
使用source来构建Bean。一般来说在run
的时候都会把标记的类(本例中是SampleApplication)放到sources
参数里,然后由这个类出发找到Bean的定义。
5、 初始化列表(见附录)
6、 初始化列表(见附录)
7、 -> SpringApplication#run(args)
,进入运行阶段
推送ApplicationStartedEvent
SpringApplication#run(args)
1、 初始化 (的集合)。它内部只包含。
2、 推送给所有的(见附录)。 下面是关心此事件的listener:
(见附录)
准备Environment
SpringApplication#run(args)
->->prepareEnvironment(...)
准备。
1、 创建(见附录)。
2、 配置,将命令行和默认参数整吧整吧,添加到。
3、 推送给所有的(见附录)。下面是关心此事件的listener:
4、
5、
6、
7、(见附录)
8、
9、
10、
11、
可以参考了解构建好之后,其内部到底有些啥东东。创建及准备ApplicationContext
SpringApplication#run(args)
1、->SpringApplication#createApplicationContext()
创建。可以看到实际上创建的是或。
->SpringApplication#prepareContext(...)
准备
->context.setEnvironment(environment)
,把之前准备好的塞给
->postProcessApplicationContext(context)
,给设置了一些其他东西
->applyInitializers(context)
,调用之前准备好的
->listeners.contextPrepared(context)
->,但实际上啥都没做。
->load
,负责将source(复数)里所定义的Bean加载到里,在本例中就是SampleApplication,这些source是在初始化SpringApplication阶段获得的。
->listeners.contextLoaded(context)
->。
刷新ApplicationContext
根据前面所讲,这里的实际上是 ->或者
SpringApplication#run(args)
->->SpringApplication#refreshContext(context)
->->SpringApplication#refresh(context)
->->AbstractApplicationContext#refresh
1、->AbstractApplicationContext#prepareRefresh()
,做了一些初始化工作,比如设置了当前Context的状态,初始化propertySource(其实啥都没干),检查required的property是否都已在Environment中(其实并没有required的property可供检查)等。
->obtainFreshBeanFactory()
,获得,实际上这里获得的是
->prepareBeanFactory(beanFactory)
准备
getEnvironment()
作为Bean添加到beanFactory中,Bean Name: environment
12、把getEnvironment().getSystemProperties()
作为Bean添加到beanFactory中,Bean Name: systemProperties
13、把getEnvironment().getSystemEnvironment()
作为Bean添加到beanFactory中,Bean Name: systemEnvironment
->postProcessBeanFactory(beanFactory)
,后置处理,实际啥都没做
->invokeBeanFactoryPostProcessors(beanFactory)
,利用,对beanFactory做后置处理。调用此方法时有四个:
的内部类,是在 创建及准备ApplicationContext 2.3 时添加的:
的内部类,是在 创建及准备ApplicationContext 2.3 时添加的:
的内部类,是在 创建及准备ApplicationContext 2.6 时添加的:->->
,负责读取是在 创建及准备ApplicationContext 1.1 时添加的
->registerBeanPostProcessors(beanFactory)
,注册
->initMessageSource()
,初始化,不过其实此时的是个Noop对象。
->initApplicationEventMulticaster()
,初始化。
->onRefresh()
,这个方法啥都没做
->registerListeners()
,把自己的注册到里,并且将之前因为没有而无法发出的发送出去。
->finishBeanFactoryInitialization
。注意,在这一步的时候才会实例化所有non-lazy-init bean,这里说的实例化不只是new而已,注入、都会执行。
->finishRefresh()
。
调用 ApplicationRunner 和 CommandLineRunner
SpringApplication#run(args)
->afterRefresh(context, applicationArguments)
->callRunners(context, args)
-> 先后调用了当前中的和。关于它们的相关文档可以看。
需要注意的是,此时的已经刷新完毕了,该有的Bean都已经有了。
推送ApplicationReadyEvent or ApplicationFailedEvent
SpringApplication#run(args)
->listeners.finished(context, null)
间接地调用了EventPublishingRunListener#getFinishedEvent
,发送了或回调接口
ApplicationContextInitializer
加载方式:读取classpath*:META-INF/spring.factories
中key等于org.springframework.context.ApplicationContextInitializer
的property列出的类
排序方式:
已知清单1:spring-boot-1.4.1.RELEASE.jar!/META-INF/spring.factories
1、(优先级:0)
2、(优先级:Ordered.LOWEST_PRECEDENCE – 10)
3、(优先级:无=Ordered.LOWEST_PRECEDENCE)
4、(优先级:无=Ordered.LOWEST_PRECEDENCE)
已知清单2:spring-boot-autoconfigure-1.4.1.RELEASE.jar!/META-INF/spring.factories
1、(优先级:无=Ordered.LOWEST_PRECEDENCE)
2、(优先级:无=Ordered.LOWEST_PRECEDENCE)
ApplicationListener
加载方式:读取classpath*:META-INF/spring.factories
中key等于org.springframework.context.ApplicationListener
的property列出的类
排序方式:
已知清单1:spring-boot-1.4.1.RELEASE.jar!/META-INF/spring.factories中定义的
1、(优先级:无=Ordered.LOWEST_PRECEDENCE)
2、(优先级:Ordered.LOWEST_PRECEDENCE – 10)
3、(优先级:Ordered.LOWEST_PRECEDENCE)
4、(优先级:ConfigFileApplicationListener.DEFAULT_ORDER + 1)
5、(优先级:Ordered.HIGHEST_PRECEDENCE + 10)
6、(优先级:0)
7、(优先级:无=Ordered.LOWEST_PRECEDENCE)
8、(优先级:LoggingApplicationListener的优先级 + 1)
9、(优先级:Ordered.HIGHEST_PRECEDENCE + 20)
已知清单2:spring-boot-autoconfigure-1.4.1.RELEASE.jar!/META-INF/spring.factories中定义的
10、
SpringApplicationRunListener
加载方式:读取classpath*:META-INF/spring.factories
中key等于org.springframework.boot.SpringApplicationRunListener
的property列出的类
排序方式:
已知清单:spring-boot-1.4.1.RELEASE.jar!/META-INF/spring.factories定义的
1、org.springframework.boot.context.event.EventPublishingRunListener(优先级:0)
EnvironmentPostProcessor
可以用来自定义()。
加载方式:读取classpath*:META-INF/spring.factories
中key等于org.springframework.boot.env.EnvironmentPostProcessor
的property列出的类
排序方式:
已知清单:spring-boot-1.4.1.RELEASE.jar!/META-INF/spring.factories定义的
1、(优先级:ConfigFileApplicationListener.DEFAULT_ORDER – 1)
2、(优先级:Ordered.HIGHEST_PRECEDENCE + 5)
BeanPostProcessor
用来对Bean实例进行修改的勾子,根据Javadoc ApplicationContext会自动侦测到BeanPostProcessor Bean,然后将它们应用到后续创建的所有Bean上。
BeanFactoryPostProcessor和BeanDefinitionRegistryPostProcessor
负责调用和。在之前被调用。
*Aware
*Aware是一类可以用来获得Spring对象的interface,这些interface都继承了,已知的有:
@Configuration 和 Auto-configuration
替代xml来定义的一种手段。也是定义的一种手段。
这两者的相同之处有:
1、都是使用注解的类,这些类里都可以定义、、。
都可以使用来根据情况选择是否加载
而不同之处有:
1、加载方式不同:
普通则是通过扫描package path加载的
的是通过读取
classpath*:META-INF/spring.factories
中key等于org.springframework.boot.autoconfigure.EnableAutoConfiguration
的property列出的加载的
普通则无法控制加载顺序
可以使用、、
1、如果出现在最初的扫描路径里(@ComponentScan),就会被提前加载到,然后被当作普通的处理,这样和就没用了。参看例子代码里的InsideAutoConfiguration和InsideAutoConfiguration2。
如果提供,那么它会被提前加载。参见例子代码里的BeanPostProcessorAutoConfiguration。
如果使用了,那么会被提前加载。参见例子代码里的ImportBeanDefinitionRegistrarAutoConfiguration。
如果使用了,那么会被提前加载。参见例子代码里的UselessDeferredImportSelectorAutoConfiguration。
参考和附录了解Spring boot内部处理机制。
AnnotatedBeanDefinitionReader
这个类用来读取和,并将注册到里。
ConfigurationClassPostProcessor
是一个,负责处理。
需要注意一个烟雾弹:看->。而order的值则是在从注解中提取的。 这段代码似乎告诉我们它会对进行排序,然后按次序加载。 实际上不是的,是一个递归加载的过程。在本例中,是先从SampleApplication开始加载的,而事实上在这个时候,也就只有SampleApplication它自己可以提供排序。 而之后则直接使用了,它里面并没有排序的逻辑。
关于排序的方式简单来说是这样的:的排序根据且只根据排序,如果没有则优先级最低。
ConfigurationClassParser
前面讲了使用,实际上加载的工作是在这里做的。
下面讲以下加载的顺序:
1、以SampleApplication为起点开始扫描
2、扫描得到所有的和,从中读取并导入:
3、如果注解了
4、如果使用的是,则递归导入
5、如果使用的是,则递归导入
6、如果使用的是,则仅收集不导入
7、如果注解了,则递归导入
8、迭代之前收集的,递归导入
那在哪里呢? 实际上是在第3步里,存在注解,它使用了, 是一个,所以也就是说,是在普通之后再加载的。
顺带一提,如果里再使用,那么效果和使用效果是一样的,不会再被延后处理。参见例子代码里的UselessDeferredImportSelectorAutoConfiguration。
EnableAutoConfigurationImportSelector
负责导入。
它利用对进行排序。逻辑算法是:
1、先根据类名排序
2、再根据排序,如果没有则优先级最低
3、再根据,排序
内置类说明
LoggingApplicationListener
用来配置日志系统的,比如logback、log4j。Spring boot对于日志有,如果你想,那么也请参考本文中对于的被调用时机的说明以获得更深入的了解。
StandardEnvironment
有一个,它里面有多个,负责提供property(即property的提供源),目前已知的实现有:、、等。当查找property值的时候,是从里依次查找的,而且一旦查找到就不再查找,也就是说如果要覆盖property的值,那么就得提供顺序在前的。
ConfigFileApplicationListener
用来将application.properties
加载到中。
内部使用了(见附录)自定义
ApplicationContextAwareProcessor
实现了接口,根据javadoc这个类用来调用以下接口的回调方法:
1、
2、
3、
3、
4、
5、
AnnotationConfigApplicationContext
根据,这个类用来将和作为输入来注册BeanDefinition。
特别需要注意的是,在中讲到其支持@Bean的覆盖:
In case of multiple @Configuration classes, @Bean methods defined in later classes will override those defined in earlier classes. This can be leveraged to deliberately override certain bean definitions via an extra @Configuration class.
它使用来读取和。
AnnotatedBeanDefinitionReader
在其构造函数内部间接()的给注册了几个与相关注解的处理器。
1、
2、
3、
4、
5、
6、