前言
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实例化流程解析》