Spring Boot启动过程及回调接口汇总
Spring Boot启动过程及回调接口汇总
链接: https://www.itcodemonkey.com/article/1431.html
注:本文基于spring-boot 1.4.1.RELEASE, spring 4.3.3.RELEASE撰写。
启动顺序
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)
#L1174
2、SpringApplication#L1186 -> SpringApplication(sources)
#L236
3、SpringApplication#initialize(Object[] sources)
#L256 javadoc
4、SpringApplication#L257 添加source(复数),SpringApplication
使用source来构建Bean。一般来说在run
的时候都会把@SpringBootApplication标记的类(本例中是SampleApplication)放到sources
参数里,然后由这个类出发找到Bean的定义。
5、SpringApplication#L261 初始化ApplicationContextInitializer列表(见附录)
6、SpringApplication#L263 初始化ApplicationListener列表(见附录)
7、SpringApplication#L1186 -> SpringApplication#run(args)
#L297,进入运行阶段
推送ApplicationStartedEvent
SpringApplication#run(args)
#L297
1、SpringApplication#L303 初始化SpringApplicationRunListeners (SpringApplicationRunListener的集合)。它内部只包含EventPublishingRunListener。
2、SpringApplication#L304 推送ApplicationStartedEvent给所有的ApplicationListener(见附录)。 下面是关心此事件的listener:
准备Environment
SpringApplication#run(args)
#L297->#L308->prepareEnvironment(...)
#L331准备ConfigurableEnvironment。
1、SpringApplication#L335 创建StandardEnvironment(见附录)。
2、SpringApplication#L336 配置StandardEnvironment,将命令行和默认参数整吧整吧,添加到MutablePropertySources。
3、SpringApplication#L337 推送ApplicationEnvironmentPreparedEvent给所有的ApplicationListener(见附录)。下面是关心此事件的listener:
5、FileEncodingApplicationListener
6、AnsiOutputApplicationListener
7、ConfigFileApplicationListener(见附录)
8、DelegatingApplicationListener
9、ClasspathLoggingApplicationListener
可以参考官方文档了解StandardEnvironment构建好之后,其MutablePropertySources内部到底有些啥东东。
创建及准备ApplicationContext
SpringApplication#run(args)
#L297
1、SpringApplication#L311->SpringApplication#createApplicationContext()
#L583创建ApplicationContext。可以看到实际上创建的是AnnotationConfigApplicationContext或AnnotationConfigEmbeddedWebApplicationContext。
2、在构造AnnotationConfigApplicationContext的时候,间接注册了一个BeanDefinitionRegistryPostProcessor的Bean:ConfigurationClassPostProcessor。经由AnnotatedBeanDefinitionReader构造函数->AnnotationConfigUtils.registerAnnotationConfigProcessors。
3、SpringApplication#L313->SpringApplication#prepareContext(...)
#L344准备ApplicationContext
4、SpringApplication#L347->context.setEnvironment(environment)
,把之前准备好的Environment塞给ApplicationContext
5、SpringApplication#L348->postProcessApplicationContext(context)
#L605,给ApplicationContext设置了一些其他东西
6、SpringApplication#L349->applyInitializers(context)
#L630,调用之前准备好的ApplicationContextInitializer
7、SpringApplication#L350->listeners.contextPrepared(context)
->EventPublishingRunListener.contextPrepared,但实际上啥都没做。
8、SpringApplication#L366->load
#L687,负责将source(复数)里所定义的Bean加载到ApplicationContext里,在本例中就是SampleApplication,这些source是在初始化SpringApplication阶段获得的。
9、SpringApplication#L367->listeners.contextLoaded(context)
->EventPublishingRunListener.contextLoaded。
10、将SpringApplication自己拥有的ApplicationListener加入到ApplicationContext
11、发送ApplicationPreparedEvent。目前已知关心这个事件的有ConfigFileApplicationListener、LoggingApplicationListener、ApplicationPidFileWriter
要注意的是在这个阶段,ApplicationContext里只有SampleApplication,SampleApplication是Bean的加载工作的起点。
刷新ApplicationContext
根据前面所讲,这里的ApplicationContext实际上是GenericApplicationContext ->AnnotationConfigApplicationContext或者AnnotationConfigEmbeddedWebApplicationContext
SpringApplication#run(args)
#L297 ->#L315->SpringApplication#refreshContext(context)
#L370 ->#L371->SpringApplication#refresh(context)
#L759 ->#L761->AbstractApplicationContext#refresh
AbstractApplicationContext#L507
1、AbstractApplicationContext#L510->AbstractApplicationContext#prepareRefresh()
#L575,做了一些初始化工作,比如设置了当前Context的状态,初始化propertySource(其实啥都没干),检查required的property是否都已在Environment中(其实并没有required的property可供检查)等。
2、AbstractApplicationContext#L513->obtainFreshBeanFactory()
#L611,获得BeanFactory,实际上这里获得的是DefaultListableBeanFactory
3、AbstractApplicationContext#L516->prepareBeanFactory(beanFactory)
#L625准备BeanFactory
4、给beanFactory设置了ClassLoader
5、给beanFactory设置了SpEL解析器
6、给beanFactory设置了PropertyEditorRegistrar
7、给beanFactory添加了ApplicationContextAwareProcessor(BeanPostProcessor的实现类),需要注意的是它是第一个被添加到BeanFactory的BeanPostProcessor
8、给beanFactory设置忽略解析以下类的依赖:ResourceLoaderAware、ApplicationEventPublisherAware、MessageSourceAware、ApplicationContextAware、EnvironmentAware。原因是注入这些回调接口本身没有什么意义。
9、给beanFactory添加了以下类的依赖解析:BeanFactory、ResourceLoader、ApplicationEventPublisher、ApplicationContext
10、给beanFactory添加LoadTimeWeaverAwareProcessor用来处理LoadTimeWeaverAware的回调,在和AspectJ集成的时候会用到
11、把getEnvironment()
作为Bean添加到beanFactory中,Bean Name: environment
12、把getEnvironment().getSystemProperties()
作为Bean添加到beanFactory中,Bean Name: systemProperties
13、把getEnvironment().getSystemEnvironment()
作为Bean添加到beanFactory中,Bean Name: systemEnvironment
14、AbstractApplicationContext#L520->postProcessBeanFactory(beanFactory)
,后置处理BeanFactory,实际啥都没做
15、AbstractApplicationContext#L523->invokeBeanFactoryPostProcessors(beanFactory)
,利用BeanFactoryPostProcessor,对beanFactory做后置处理。调用此方法时有四个BeanFactoryPostProcessor:
16、SharedMetadataReaderFactoryContextInitializer的内部类CachingMetadataReaderFactoryPostProcessor,是在 创建及准备ApplicationContext 2.3 时添加的:#L57
17、ConfigurationWarningsApplicationContextInitializer的内部类ConfigurationWarningsPostProcessor,是在 创建及准备ApplicationContext 2.3 时添加的:#L60
18、ConfigFileApplicationListener的内部类PropertySourceOrderingPostProcessor,是在 创建及准备ApplicationContext 2.6 时添加的:#L158->#L199->#L244
19、ConfigurationClassPostProcessor,负责读取BeanDefinition是在 创建及准备ApplicationContext 1.1 时添加的
20、AbstractApplicationContext#L526->registerBeanPostProcessors(beanFactory)
,注册BeanPostProcessor
21、AbstractApplicationContext#L529->initMessageSource()
#L704,初始化MessageSource,不过其实此时的MessageSource是个Noop对象。
22、AbstractApplicationContext#L532->initApplicationEventMulticaster()
#L739,初始化ApplicationEventMulticaster。
23、AbstractApplicationContext#L535->onRefresh()
#L793,这个方法啥都没做
24、AbstractApplicationContext#L538->registerListeners()
#L801,把自己的ApplicationListener注册到ApplicationEventMulticaster里,并且将之前因为没有ApplicationEventMulticaster而无法发出的ApplicationEvent发送出去。
25、AbstractApplicationContext#L541->finishBeanFactoryInitialization
#L828。注意#L861,在这一步的时候才会实例化所有non-lazy-init bean,这里说的实例化不只是new而已,注入、BeanPostProcessor都会执行。
26、AbstractApplicationContext#L544->finishRefresh()
#L869。
27、在#L877发送了ContextRefreshedEvent
调用 ApplicationRunner 和 CommandLineRunner
SpringApplication#run(args)
#L297 ->afterRefresh(context, applicationArguments)
#L316 ->callRunners(context, args)
#L771 ->#L774 先后调用了当前ApplicationContext中的ApplicationRunner和CommandLineRunner。关于它们的相关文档可以看这里。
需要注意的是,此时的ApplicationContext已经刷新完毕了,该有的Bean都已经有了。
推送ApplicationReadyEvent or ApplicationFailedEvent
SpringApplication#run(args)
#L297->listeners.finished(context, null)
#L317 间接地调用了EventPublishingRunListener#getFinishedEvent
EventPublishingRunListener#L96,发送了ApplicationReadyEvent或ApplicationFailedEvent回调接口
ApplicationContextInitializer
加载方式:读取classpath*:META-INF/spring.factories
中key等于org.springframework.context.ApplicationContextInitializer
的property列出的类
排序方式:AnnotationAwareOrderComparator
已知清单1:spring-boot-1.4.1.RELEASE.jar!/META-INF/spring.factories
1、ConfigurationWarningsApplicationContextInitializer(优先级:0)
2、ContextIdApplicationContextInitializer(优先级:Ordered.LOWEST_PRECEDENCE – 10)
3、DelegatingApplicationContextInitializer(优先级:无=Ordered.LOWEST_PRECEDENCE)
4、ServerPortInfoApplicationContextInitializer(优先级:无=Ordered.LOWEST_PRECEDENCE)
已知清单2:spring-boot-autoconfigure-1.4.1.RELEASE.jar!/META-INF/spring.factories
1、SharedMetadataReaderFactoryContextInitializer(优先级:无=Ordered.LOWEST_PRECEDENCE)
2、AutoConfigurationReportLoggingInitializer(优先级:无=Ordered.LOWEST_PRECEDENCE)
ApplicationListener
加载方式:读取classpath*:META-INF/spring.factories
中key等于org.springframework.context.ApplicationListener
的property列出的类
排序方式:AnnotationAwareOrderComparator
已知清单1:spring-boot-1.4.1.RELEASE.jar!/META-INF/spring.factories中定义的
1、ClearCachesApplicationListener(优先级:无=Ordered.LOWEST_PRECEDENCE)
2、ParentContextCloserApplicationListener(优先级:Ordered.LOWEST_PRECEDENCE – 10)
3、FileEncodingApplicationListener(优先级:Ordered.LOWEST_PRECEDENCE)
4、AnsiOutputApplicationListener(优先级:ConfigFileApplicationListener.DEFAULT_ORDER + 1)
5、ConfigFileApplicationListener(优先级:Ordered.HIGHEST_PRECEDENCE + 10)
6、DelegatingApplicationListener(优先级:0)
7、LiquibaseServiceLocatorApplicationListener(优先级:无=Ordered.LOWEST_PRECEDENCE)
8、ClasspathLoggingApplicationListener(优先级:LoggingApplicationListener的优先级 + 1)
9、LoggingApplicationListener(优先级:Ordered.HIGHEST_PRECEDENCE + 20)
已知清单2:spring-boot-autoconfigure-1.4.1.RELEASE.jar!/META-INF/spring.factories中定义的
SpringApplicationRunListener
加载方式:读取classpath*:META-INF/spring.factories
中key等于org.springframework.boot.SpringApplicationRunListener
的property列出的类
排序方式:AnnotationAwareOrderComparator
已知清单:spring-boot-1.4.1.RELEASE.jar!/META-INF/spring.factories定义的
1、org.springframework.boot.context.event.EventPublishingRunListener(优先级:0)
EnvironmentPostProcessor
EnvironmentPostProcessor可以用来自定义StandardEnvironment(相关文档)。
加载方式:读取classpath*:META-INF/spring.factories
中key等于org.springframework.boot.env.EnvironmentPostProcessor
的property列出的类
排序方式:AnnotationAwareOrderComparator
已知清单:spring-boot-1.4.1.RELEASE.jar!/META-INF/spring.factories定义的
1、CloudFoundryVcapEnvironmentPostProcessor(优先级:ConfigFileApplicationListener.DEFAULT_ORDER – 1)
2、SpringApplicationJsonEnvironmentPostProcessor(优先级:Ordered.HIGHEST_PRECEDENCE + 5)
BeanPostProcessor
用来对Bean实例进行修改的勾子,根据Javadoc ApplicationContext会自动侦测到BeanPostProcessor Bean,然后将它们应用到后续创建的所有Bean上。
BeanFactoryPostProcessor和BeanDefinitionRegistryPostProcessor
PostProcessorRegistrationDelegate负责调用BeanDefinitionRegistryPostProcessor和BeanFactoryPostProcessor。BeanDefinitionRegistryPostProcessor在BeanFactoryPostProcessor之前被调用。
*Aware
*Aware是一类可以用来获得Spring对象的interface,这些interface都继承了Aware,已知的有:
@Configuration 和 Auto-configuration
@Configuration替代xml来定义BeanDefinition的一种手段。Auto-configuration也是定义BeanDefinition的一种手段。
这两者的相同之处有:
1、都是使用@Configuration注解的类,这些类里都可以定义@Bean、@Import、@ImportResource。
都可以使用@Condition*来根据情况选择是否加载
而不同之处有:
1、加载方式不同:
普通@Configuration则是通过扫描package path加载的
Auto-configuration的是通过读取
classpath*:META-INF/spring.factories
中key等于org.springframework.boot.autoconfigure.EnableAutoConfiguration
的property列出的@Configuration加载的
2、加载顺序不同:普通@Configuration的加载在Auto-configuration之前,但是有例外情况,看下面。
3、内部加载顺序可控上的不同:
普通@Configuration则无法控制加载顺序
Auto-configuration可以使用@AutoConfigureOrder、@AutoConfigureBefore、@AutoConfigureAfter
以下情况下Auto-configuration会在普通@Configuration前加载:
1、Auto-configuration如果出现在最初的扫描路径里(@ComponentScan),就会被提前加载到,然后被当作普通的@Configuration处理,这样@AutoConfigureBefore和@AutoConfigureAfter就没用了。参看例子代码里的InsideAutoConfiguration和InsideAutoConfiguration2。
2、Auto-configuration如果提供BeanPostProcessor,那么它会被提前加载。参见例子代码里的BeanPostProcessorAutoConfiguration。
3、Auto-configuration如果使用了ImportBeanDefinitionRegistrar,那么ImportBeanDefinitionRegistrar会被提前加载。参见例子代码里的ImportBeanDefinitionRegistrarAutoConfiguration。
4、Auto-configuration如果使用了ImportSelector,那么ImportSelector会被提前加载。参见例子代码里的UselessDeferredImportSelectorAutoConfiguration。
参考EnableAutoConfiguration和附录EnableAutoConfigurationImportSelector了解Spring boot内部处理机制。
AnnotatedBeanDefinitionReader
这个类用来读取@Configuration和@Component,并将BeanDefinition注册到ApplicationContext里。
ConfigurationClassPostProcessor
ConfigurationClassPostProcessor是一个BeanDefinitionRegistryPostProcessor,负责处理@Configuration。
需要注意一个烟雾弹:看#L296->ConfigurationClassUtils#L209。而order的值则是在ConfigurationClassUtils#L122从注解中提取的。 这段代码似乎告诉我们它会对@Configuration进行排序,然后按次序加载。 实际上不是的,@Configuration是一个递归加载的过程。在本例中,是先从SampleApplication开始加载的,而事实上在这个时候,也就只有SampleApplication它自己可以提供排序。 而之后则直接使用了ConfigurationClassParser,它里面并没有排序的逻辑。
关于排序的方式简单来说是这样的:@Configuration的排序根据且只根据@Order排序,如果没有@Order则优先级最低。
ConfigurationClassParser
前面讲了ConfigurationClassPostProcessor使用ConfigurationClassParser,实际上加载@Configuration的工作是在这里做的。
下面讲以下加载的顺序:
1、以SampleApplication为起点开始扫描
2、扫描得到所有的@Configuration和@Component,从中读取BeanDefinition并导入:
3、如果@Configuration注解了@Import
4、如果使用的是ImportSelector,则递归导入
5、如果使用的是ImportBeanDefinitionRegistrar,则递归导入
6、如果使用的是DeferredImportSelector,则仅收集不导入
7、如果@Configuration注解了@ImportResource,则递归导入
8、迭代之前收集的DeferredImportSelector,递归导入
那Auto-configuration在哪里呢? 实际上是在第3步里,@SpringBootApplication存在注解@EnableAutoConfiguration,它使用了EnableAutoConfigurationImportSelector, EnableAutoConfigurationImportSelector是一个DeferredImportSelector,所以也就是说,Auto-configuration是在普通@Configuration之后再加载的。
顺带一提,如果Auto-configuration里再使用DeferredImportSelector,那么效果和使用ImportSelector效果是一样的,不会再被延后处理。参见例子代码里的UselessDeferredImportSelectorAutoConfiguration。
EnableAutoConfigurationImportSelector
EnableAutoConfigurationImportSelector负责导入Auto-configuration。
它利用AutoConfigurationSorter对Auto-configuration进行排序。逻辑算法是:
1、先根据类名排序
2、再根据@AutoConfigureOrder排序,如果没有@AutoConfigureOrder则优先级最低
3、再根据@AutoConfigureBefore,@AutoConfigureAfter排序
内置类说明
LoggingApplicationListener
LoggingApplicationListener用来配置日志系统的,比如logback、log4j。Spring boot对于日志有详细解释,如果你想自定义日志配置,那么也请参考本文中对于LoggingApplicationListener的被调用时机的说明以获得更深入的了解。
StandardEnvironment
StandardEnvironment有一个MutablePropertySources,它里面有多个PropertySource,PropertySource负责提供property(即property的提供源),目前已知的PropertySource实现有:MapPropertySource、SystemEnvironmentPropertySource、CommandLinePropertySource等。当StandardEnvironment查找property值的时候,是从MutablePropertySources里依次查找的,而且一旦查找到就不再查找,也就是说如果要覆盖property的值,那么就得提供顺序在前的PropertySource。
ConfigFileApplicationListener
ConfigFileApplicationListener用来将application.properties
加载到StandardEnvironment中。
ConfigFileApplicationListener内部使用了EnvironmentPostProcessor(见附录)自定义StandardEnvironment
ApplicationContextAwareProcessor
ApplicationContextAwareProcessor实现了BeanPostProcessor接口,根据javadoc这个类用来调用以下接口的回调方法:
3、ApplicationEventPublisherAware
AnnotationConfigApplicationContext
根据javadoc,这个类用来将@Configuration和@Component作为输入来注册BeanDefinition。
特别需要注意的是,在javadoc中讲到其支持@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来读取@Configuration和@Component。
AnnotatedBeanDefinitionReader
AnnotatedBeanDefinitionReader在其构造函数内部间接(AnnotationConfigUtils#L145)的给BeanFactory注册了几个与BeanDefinition相关注解的处理器。
1、ConfigurationClassPostProcessor
2、AutowiredAnnotationBeanPostProcessor
3、RequiredAnnotationBeanPostProcessor
4、CommonAnnotationBeanPostProcessor
Spring Boot启动过程及回调接口汇总的更多相关文章
- Spring Boot启动过程(三)
我已经很精简了,两篇(Spring Boot启动过程(一).pring Boot启动过程(二))依然没写完,接着来. refreshContext之后的方法是afterRefresh,这名字起的真.. ...
- Spring Boot启动过程(四):Spring Boot内嵌Tomcat启动
之前在Spring Boot启动过程(二)提到过createEmbeddedServletContainer创建了内嵌的Servlet容器,我用的是默认的Tomcat. private void cr ...
- Spring Boot启动过程(七):Connector初始化
Connector实例的创建已经在Spring Boot启动过程(四):Spring Boot内嵌Tomcat启动中提到了: Connector是LifecycleMBeanBase的子类,先是设置L ...
- Spring Boot 启动过程
一切从SpringApplication.run()开始,最终返回一个ConfigurableApplicationContext 构造了一个SpringApplication对象,然后调用它的run ...
- Spring Boot启动过程(一)
之前在排查一个线上问题时,不得不仔细跑了很多遍Spring Boot的代码,于是整理一下,我用的是1.4.3.RELEASE. 首先,普通的入口,这没什么好说的,我就随便贴贴代码了: SpringAp ...
- Spring Boot启动过程(二)
书接上篇 该说refreshContext(context)了,首先是判断context是否是AbstractApplicationContext派生类的实例,之后调用了强转为AbstractAppl ...
- 转:Spring Boot启动过程
之前在排查一个线上问题时,不得不仔细跑了很多遍Spring Boot的代码,于是整理一下,我用的是1.4.3.RELEASE. 首先,普通的入口,这没什么好说的,我就随便贴贴代码了: SpringAp ...
- Spring Boot 启动过程及 自定义 Listener等组件
一.启动过程 二.自定义组件 package com.example.jdbc.listener; import org.springframework.context.ApplicationCont ...
- Spring boot 启动过程解析 logback
使用 Spring Boot 默认的日志框架 Logback. 所有这些 POM 依赖的好处在于为开发 Spring 应用提供了一个良好的基础.Spring Boot 所选择的第三方库是经过考虑的,是 ...
随机推荐
- vsphere VAAI介绍
VAAI:让特定的储存作业可以无需透过ESXi主机执行,而由储存设备来担纲 [TechTarget中国原创]目前,VAAI虽然已经成为虚拟化领域的标准语言之一,但是大多数人可能并不了解它还有隐藏的第四 ...
- Python学习笔记_05:使用Flask+MySQL实现用户登陆注册以及增删查改操作
前言:本文代码参考自两篇英文博客,具体来源点击文末代码链接中文档说明. (PS:代码运行Python版本为2.7.14) 运行效果: 首页: 注册页面: 登陆界面: 管理员登陆后界面: 添加.删除.修 ...
- 西数移动固态SSD
好款推荐! 我可没有收广告费:哈哈哈 就是看着产品不错,喜欢小米! 西数出了SSD移动固态硬盘真心不错! 文章来源:刘俊涛的博客 欢迎关注,有问题一起学习欢迎留言.评论
- 二、Django用Eclipse编写一个登录界面
一.Django用Eclipse编写一个登录界面 二.Django用Eclipse编写一个登录界面Ajax和Django交互 各软件版本:Python 2.7.14,django 1.6.11 原来已 ...
- 发现一个ReactNative大神
传送门: RN使用技巧:http://www.jianshu.com/p/2f575cc35780 RN 进阶技巧:http://www.jianshu.com/p/b877115fff1b 亮点有: ...
- sqlserver 日志查看
sqlserve的ErrorLog文件有时候会碰到文件很大的情况,可能通过命令xp_readerrorlog 或 sp_readerrorlog 执行,可以加搜索文本或起止时间 -- 日志查看 --e ...
- DHCP 服务测试
DHCP三个端口: 服务端:UDP 67 客户端:UDP 68 DHCPv6 客户端:UDP 546,这是需要特别开启的 DHCP failover 服务,用来做双机热备的. 实验一.DHCP服务器基 ...
- C#并行编程-PLINQ:声明式数据并行-转载
C#并行编程-PLINQ:声明式数据并行 目录 C#并行编程-相关概念 C#并行编程-Parallel C#并行编程-Task C#并行编程-并发集合 C#并行编程-线程同步原语 C#并行编程-P ...
- 转:TCP/IP协议栈的基本工作原理
转载自:http://jasonccie.blog.51cto.com/2143955/422966 TCP/IP是互联网的核心协议,也是大多数网络应用的核心协议.就前面一段时间面试中问到的TCP/I ...
- Redis-Redi事务注意事项
当客户端处于非事务状态下时, 所有发送给服务器端的命令都会立即被服务器执行.但是, 当客户端进入事务状态之后, 服务器在收到来自客户端的命令时, 不会立即执行命令, 而是将这些命令全部放进一个事务队列 ...