前言
SpringBoot底层其实也依赖着Spring,不管是自动装配还是手动装配,最终Bean的管理和实例化还是交由Spring,那么SpringBoot在启动时,会有一个非常关键的refresh()
方法,这个方法是Bean配置读取加载的入口,也包含了spring框架启动流程,本文将通过深入refresh()
方法源码的形式来进行原理解析。
启动SpringBoot
简单启动SpringBoot代码如下
@Slf4j
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
SpringApplication.run()
启动SpringBoot主要是通过调用SpringApplication
的run()
方法,由其方法注释我们也可以看出主要作用就是为了启动SpringApplication
并创建和刷新ApplicationContext
。
创建并刷新ApplicationContext
在SpringApplication
的run()
方法中短短4行代码便概括了创建ApplicationContext
的整体流程
这个非常值得借鉴,虽然这几行代码涉及的逻辑会非常多,但是整体上看显得非常简洁明了,甚至都不用画流程图,看代码就像在看流程图一样了。令人非常清楚创建ApplicationContext大致会有哪几个步骤。
核心方法refresh()
AnnotationConfigServletWebServerApplicationContext
启动SpringBoot,走到调用refresh()
方法的地方,因为我引入了web环境的依赖,所以创建的是AnnotationConfigServletWebServerApplicationContext
一路往下走
ServletWebServerApplicationContext
AbstractApplicationContext
最终来到了AbstractApplicationContext
的refresh()
方法中,整体来看该方法内涉及了13个方法的调用,会比较复杂,并且使用了synchronized
关键词上了一把锁,表明同一时间内,只会有一个线程执行该方法具体逻辑。
prepareRefresh()
第一个执行的便是prepareRefresh()
方法,从注释可以看出就是在刷新上下文之前做一些准备工作。
首先会尝试清除一些metadata
元数据,不过我这里是第一次启动SpringBoot,没什么元数据
initPropertySources()
接着调用父类AbstractApplicationContext
的prepareRefresh()
方法。
抓大放小跳过一些代码,第一步便是调用initPropertySources()
方法初始化一些占位符属性资源。
往下一步执行到调用GenericWebApplicationContext
的initPropertySources()
方法,会尝试将一些属性加载到ServletContext
和ServletConfig
中。
往下执行,因为ServletContext
和ServletConfig
都为null,所以也不会真的加载什么属性。
validateRequiredProperties()
接下来便是调用validateRequiredProperties()
方法进行必备属性的校验。
这个方法最终会调用到AbstractPropertyResolver
的validateRequiredProperties()
方法,逻辑非常简单,就是判断如果当前缺少了某些必备属性,那么就直接抛异常。
初始化earlyApplicationListeners
接着便是将之前注册好的ApplicationListener
赋值给earlyApplicationListeners
。关于ApplicationListener
可以看之前写过的《SpringBoot核心特性——应用事件监听》 《SpringBoot ApplicationListener原理解析》
初始化earlyApplicationEvents
最后便是初始化earlyApplicationEvents
属性,它其实就是一个元素为ApplicationEvent
的HashSet
,事件发布时会用到。
总结
由以上代码不难看出,prepareRefresh()
方法主要做了几件事
- 设置容器状态
- 初始化属性
- 检查必备属性
obtainFreshBeanFactory()
接下来是调用obtainFreshBeanFactory()
获取BeanFactory
刷新BeanFactory
首先会调用refreshBeanFactory()
方法设置一下当前ApplicationContext
的refreshed
属性,表示当前已经开始刷新。
获取BeanFactory
接下来便是非常简单的获取BeanFactory
,这里返回的为DefaultListableBeanFactory
总结
obtainFreshBeanFactory()
方法非常简单,主要就是
- 更新
ApplicationContext
的refreshed
属性 - 给
BeanFactory
设置序列化ID - 获取
BeanFactory
prepareBeanFactory()
prepareBeanFactory()
方法主要是对BeanFactory
做一些准备和配置。
通过源码可以看出对BeanFactory
做一些设置,比如BeanClassLoader
,BeanPostProcessor
...忽略掉一些接口依赖,比如EnvironmentAware
,ApplicationEventPublisherAware
,ApplicationContextAware
...注册一些Bean,比如environment
,systemProperties
,applicationStartup
...
总结
prepareBeanFactory()
方法也非常简单,主要作用为
- 设置beanFactory一些属性
- 添加后置处理器
- 设置忽略的自动装配接口
- 注册一些组件
postProcessBeanFactory()
postProcessBeanFactory()
方法主要是对BeanFactory
进行后置处理
postProcessBeanFactory()
执行到postProcessBeanFactory
方法,可以看到主要作用就是向BeanFactory
中注册一个ServletContextAwareProcessor
registerWebApplicationScopes()
接着执行registerWebApplicationScopes()
方法,该方法中向BeanFactory
注册了Web环境的作用域
web开发非常熟悉的ServletRequest
,ServletResponse
,HttpSession
,WebRequest
总结
该方法主要作用
- 在
BeanFactory
完成创建后对其做进一步设置
invokeBeanFactoryPostProcessors()
该方法主要是实例化并调用所有在上下文中注册了的BeanFactoryPostProcessor
getBeanFactoryPostProcessors()
首先会获取当前上下文中存在的BeanFactoryPostProcessor
,默认有3个postProcessor
invokeBeanFactoryPostProcessors()
接着进入PostProcessorRegistrationDelegate
的invokeBeanFactoryPostProcessors()
方法。
首先因为传入的beanFactory
是BeanDefinitionRegistry
接口的实现,那么逐个遍历BeanFactoryPostProcessor
列表,并由于postProcessor也是BeanDefinitionRegistryPostProcessor
接口的实现,所以最终会调用BeanDefinitionRegistryPostProcessor
的postProcessBeanDefinitionRegistry()
方法,并将postProcessor添加到registryProcessors
集合当中。
postProcessBeanDefinitionRegistry()
从postProcessBeanDefinitionRegistry()
方法可以看出,调用的是SharedMetadataReaderFactoryContextInitializer
的register()
方法,主要就是添加了一些Bean的定义,也就是BeanDefinition
。
接着配置一下metadataReaderFactory
对应的是org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory
处理BeanDefinitionRegistryPostProcessor
接着会获取所有BeanDefinitionRegistryPostProcessor
的实现,并且如果实现了PriorityOrdered
接口的话,会将它们添加到currentRegistryProcessors
和processedBeans
变量当中,然后做一个排序。
postProcessBeanDefinitionRegistry()
最后逐个遍历BeanDefinitionRegistryPostProcessor
,调用其postProcessBeanDefinitionRegistry()
方法。
稍微看一下该方法声明,可以知道该方法主要就是在所有常规Bean定义加载完毕,但还没有实例化任何Bean的这个阶段, 允许我们再进一步添加Bean定义。
断点往下执行可以看到实际会走到ConfigurationClassPostProcessor
的processConfigBeanDefinitions()
方法中做一些处理。
第二次postProcessBeanDefinitionRegistry()
往下执行,再次获取BeanDefinitionRegistryPostProcessors
,排序,调用其postProcessBeanDefinitionRegistry()
方法,最后做一个清空操作。
第三次postProcessBeanDefinitionRegistry()
往下执行,第三次获取BeanDefinitionRegistryPostProcessors
,排序,调用其postProcessBeanDefinitionRegistry()
方法,最后做一个清空操作。
Tips:之所以要重复获取BeanDefinitionRegistryPostProcessors并调用其postProcessBeanDefinitionRegistry()方法,是因为可能会出现这么一种情况,当有些类在实现BeanDefinitionRegistryPostProcessors接口时,它又往SpringFactory中添加了一个新的BeanDefinitionRegistryPostProcessors实现,所以Spring为了确保任何BeanDefinitionRegistryPostProcessors的方法都会被调用,会孜孜不倦地尝试检索并收集被遗漏了的BeanDefinitionRegistryPostProcessors实现,把这些被遗漏的processors,做最后一次排序,并执行postProcessBeanDefinitionRegistry()方法。
postProcessBeanFactory()
接着遍历并调用BeanFactoryPostProcessor
的postProcessBeanFactory()
方法
处理BeanFactoryPostProcessor
接下来是获取BeanFactoryPostProcessor
实现的集合,并将其做好分类,最后分别遍历并调用其postProcessBeanFactory()
方法,最后清空一下缓存。
Tips: BeanFactoryPostProcessor的postProcessBeanFactory()方法执行时机是在所有Bean定义都已经加载,但还没实例化任何Bean的时候,可以给Bean覆盖或添加一些属性。
总结
该方法主要作用
- 调用
BeanDefinitionRegistryPostProcessor
实现向容器内添加bean的定义 - 调用
BeanFactoryPostProcessor
实现给容器内bean定义添加属性
registerBeanPostProcessors()
接下来执行registerBeanPostProcessors()
方法,可以看出该方法主要就是实例化并注册所有BeanPostProcessor
实现
可以看出首先获取容器中BeanPostProcessor
实现的全限定类名,然后往容器中添加一个BeanPostProcessorChecker
接着对所有BeanPostProcessor
进行分组和排序
并调用registerBeanPostProcessors()
方法,将postProcessor注册到Spring容器中
Tips:在这里只是做BeanPostProcessor注册,并不会调用BeanPostProcessor实现的方法。
总结
该方法的作用是
- 找到
BeanPostProcesor
的实现 - 排序后注册到容器中
initMessageSource()
接下来是initMessageSource()
方法,该方法主要是用来初始化MessageSource
,首先会检查当前Spring容器中是否已存在MessageSource
实现,没有的话则会创建并添加到Spring容器中,MessageSource
主要是用来做国际化多语言的配置。
总结
该方法主要作用
- 初始化国际化相关属性
initApplicationEventMulticaster()
接下来调用initApplicationEventMulticaster()
方法,该方法主要是初始化事件广播器,首先会检查当前Spring容器中是否已存在ApplicationEventMulticaster
实现,没有的话则会创建SimpleApplicationEventMulticaster
并添加到Spring容器中。
关于SimpleApplicationEventMulticaster
之前也有讲解过,传送门《SpringBoot ApplicationListener原理解析》
总结
该方法的作用为
- 初始化事件广播器
onRefresh()
接下来调用onRefresh()
方法,
在AbstractApplicationContext
中它其实是一个空方法,留给子类实现。
因为我引入了Web环境,所以具体执行的时候是调用了ServletWebServerApplicationContext
的onRefresh()
方法
createWebServer()
可以看到其中调用了createWebServer()
方法,而正是在该方法中初始化了Tomcat
作为Web容器。
总结
该方法主要作用为
- 创建Web容器
registerListeners()
接下来调用registerListeners()
方法,该方法里面的逻辑是把ApplicationListener
实现注册到ApplicationEventMulticaster
中
值得注意的是,还有一段判断earlyEventsToProcess
不为空的话,则进行事件广播的逻辑。当监听机制还没有完善的时候,如果在这个时候调用广播器并发布事件,会先将内容保存到earlyEventsToProcess
集合中,当监听机制初始化完毕了,才会将尚未广播的事件,依次广播出去,也就是执行到下图代码的时候。
总结
该方法的作用主要是
- 添加容器内事件监听器至事件广播器中
- 广播早期事件
finishBeanFactoryInitialization()
接下来便是调用finishBeanFactoryInitialization()
方法,该方法主要是实例化所有剩余的(非延迟加载)单例。
关于延迟加载可以看这篇《SpringBoot核心特性——延迟初始化》
关于初始化Bean的详细细节,可以看DefaultListableBeanFactory
的preInstantiateSingletons()
方法,这里限于篇幅太长的问题,我可能会留到以后再写一篇拆解。
总结
该方法主要作用是
- 初始化所有剩下的单实例Bean
finishRefresh()
接着调用finishRefresh()
方法,该方法主要是清理一些缓存,往容器中注册LifecycleProcessor
实现,并调用它们的onRefresh()
方法,以及广播ContextRefreshedEvent
事件
总结
该方法主要作用是
- 初始化并注册
LifecycleProcessor
实现,也就是生命周期处理器 - 调用
LifecycleProcessor
的onRefresh()
方法 - 广播
ContextRefreshedEvent
事件
总结
由以上可知SpringApplication
的refresh()
方法其实会涉及到非常多的处理和逻辑,这个还得反复多琢磨琢磨。但Spring
家族的编码风格,变量方法命名是真的很不错,注释简短但很容易让人推断出该变量或方法作用是什么,整体逻辑也是比较清晰的,多断点多反复运行总是可以搞明白的。
如果以上解析你都没记住的话,至少记住一点:看优秀框架源码也不是非常难或者高大上的事情,因为那可比不少历史遗留项目的代码好看多了。
结尾
本文章源自《Learn SpringBoot》专栏,感兴趣的话还请关注点赞收藏.
上一篇文章:《SpringBoot Banner输出原理解析》
下一篇文章:《SpringBoot Bean实例化流程解析》