SpringBoot 图文并茂万字拆解核心refresh方法

223 阅读9分钟

前言

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主要是通过调用SpringApplicationrun()方法,由其方法注释我们也可以看出主要作用就是为了启动SpringApplication并创建和刷新ApplicationContext

image.png

创建并刷新ApplicationContext

SpringApplicationrun()方法中短短4行代码便概括了创建ApplicationContext的整体流程

image.png

这个非常值得借鉴,虽然这几行代码涉及的逻辑会非常多,但是整体上看显得非常简洁明了,甚至都不用画流程图,看代码就像在看流程图一样了。令人非常清楚创建ApplicationContext大致会有哪几个步骤。

核心方法refresh()

AnnotationConfigServletWebServerApplicationContext

启动SpringBoot,走到调用refresh()方法的地方,因为我引入了web环境的依赖,所以创建的是AnnotationConfigServletWebServerApplicationContext

image.png

一路往下走

image.png

ServletWebServerApplicationContext

image.png

AbstractApplicationContext

最终来到了AbstractApplicationContextrefresh()方法中,整体来看该方法内涉及了13个方法的调用,会比较复杂,并且使用了synchronized关键词上了一把锁,表明同一时间内,只会有一个线程执行该方法具体逻辑。

image.png

prepareRefresh()

第一个执行的便是prepareRefresh()方法,从注释可以看出就是在刷新上下文之前做一些准备工作。

image.png

首先会尝试清除一些metadata元数据,不过我这里是第一次启动SpringBoot,没什么元数据

image.png

image.png

initPropertySources()

接着调用父类AbstractApplicationContextprepareRefresh()方法。 抓大放小跳过一些代码,第一步便是调用initPropertySources()方法初始化一些占位符属性资源。

image.png

往下一步执行到调用GenericWebApplicationContextinitPropertySources()方法,会尝试将一些属性加载到ServletContextServletConfig中。

image.png

往下执行,因为ServletContextServletConfig都为null,所以也不会真的加载什么属性。

image.png

image.png

validateRequiredProperties()

接下来便是调用validateRequiredProperties()方法进行必备属性的校验。

image.png

这个方法最终会调用到AbstractPropertyResolvervalidateRequiredProperties()方法,逻辑非常简单,就是判断如果当前缺少了某些必备属性,那么就直接抛异常。

image.png

初始化earlyApplicationListeners

接着便是将之前注册好的ApplicationListener赋值给earlyApplicationListeners。关于ApplicationListener可以看之前写过的《SpringBoot核心特性——应用事件监听》 《SpringBoot ApplicationListener原理解析

image.png

初始化earlyApplicationEvents

最后便是初始化earlyApplicationEvents属性,它其实就是一个元素为ApplicationEventHashSet,事件发布时会用到。

image.png

总结

由以上代码不难看出,prepareRefresh()方法主要做了几件事

  1. 设置容器状态
  2. 初始化属性
  3. 检查必备属性

obtainFreshBeanFactory()

接下来是调用obtainFreshBeanFactory()获取BeanFactory

image.png

image.png

刷新BeanFactory

首先会调用refreshBeanFactory()方法设置一下当前ApplicationContextrefreshed属性,表示当前已经开始刷新。

image.png

获取BeanFactory

接下来便是非常简单的获取BeanFactory,这里返回的为DefaultListableBeanFactory

image.png

总结

obtainFreshBeanFactory()方法非常简单,主要就是

  1. 更新ApplicationContextrefreshed属性
  2. BeanFactory设置序列化ID
  3. 获取BeanFactory

prepareBeanFactory()

prepareBeanFactory()方法主要是对BeanFactory做一些准备和配置。

image.png

通过源码可以看出对BeanFactory做一些设置,比如BeanClassLoaderBeanPostProcessor...忽略掉一些接口依赖,比如EnvironmentAwareApplicationEventPublisherAwareApplicationContextAware...注册一些Bean,比如environment,systemPropertiesapplicationStartup...

image.png

总结

prepareBeanFactory()方法也非常简单,主要作用为

  1. 设置beanFactory一些属性
  2. 添加后置处理器
  3. 设置忽略的自动装配接口
  4. 注册一些组件

postProcessBeanFactory()

postProcessBeanFactory()方法主要是对BeanFactory进行后置处理

image.png

image.png

postProcessBeanFactory()

执行到postProcessBeanFactory方法,可以看到主要作用就是向BeanFactory中注册一个ServletContextAwareProcessor

image.png

registerWebApplicationScopes()

接着执行registerWebApplicationScopes()方法,该方法中向BeanFactory注册了Web环境的作用域

image.png

image.png

web开发非常熟悉的ServletRequestServletResponseHttpSession,WebRequest

image.png

总结

该方法主要作用

  1. BeanFactory完成创建后对其做进一步设置

invokeBeanFactoryPostProcessors()

该方法主要是实例化并调用所有在上下文中注册了的BeanFactoryPostProcessor

image.png

image.png

getBeanFactoryPostProcessors()

首先会获取当前上下文中存在的BeanFactoryPostProcessor,默认有3个postProcessor

image.png

invokeBeanFactoryPostProcessors()

接着进入PostProcessorRegistrationDelegateinvokeBeanFactoryPostProcessors()方法。 首先因为传入的beanFactoryBeanDefinitionRegistry接口的实现,那么逐个遍历BeanFactoryPostProcessor列表,并由于postProcessor也是BeanDefinitionRegistryPostProcessor接口的实现,所以最终会调用BeanDefinitionRegistryPostProcessorpostProcessBeanDefinitionRegistry()方法,并将postProcessor添加到registryProcessors集合当中。

image.png

postProcessBeanDefinitionRegistry()

postProcessBeanDefinitionRegistry()方法可以看出,调用的是SharedMetadataReaderFactoryContextInitializerregister()方法,主要就是添加了一些Bean的定义,也就是BeanDefinition

image.png

接着配置一下metadataReaderFactory对应的是org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory

image.png

image.png

image.png

处理BeanDefinitionRegistryPostProcessor

接着会获取所有BeanDefinitionRegistryPostProcessor的实现,并且如果实现了PriorityOrdered接口的话,会将它们添加到currentRegistryProcessorsprocessedBeans变量当中,然后做一个排序。

image.png

postProcessBeanDefinitionRegistry()

最后逐个遍历BeanDefinitionRegistryPostProcessor,调用其postProcessBeanDefinitionRegistry()方法。

image.png

稍微看一下该方法声明,可以知道该方法主要就是在所有常规Bean定义加载完毕,但还没有实例化任何Bean的这个阶段, 允许我们再进一步添加Bean定义。

image.png

断点往下执行可以看到实际会走到ConfigurationClassPostProcessorprocessConfigBeanDefinitions()方法中做一些处理。

image.png

第二次postProcessBeanDefinitionRegistry()

往下执行,再次获取BeanDefinitionRegistryPostProcessors,排序,调用其postProcessBeanDefinitionRegistry()方法,最后做一个清空操作。

image.png

第三次postProcessBeanDefinitionRegistry()

往下执行,第三次获取BeanDefinitionRegistryPostProcessors,排序,调用其postProcessBeanDefinitionRegistry()方法,最后做一个清空操作。

image.png

Tips:之所以要重复获取BeanDefinitionRegistryPostProcessors并调用其postProcessBeanDefinitionRegistry()方法,是因为可能会出现这么一种情况,当有些类在实现BeanDefinitionRegistryPostProcessors接口时,它又往SpringFactory中添加了一个新的BeanDefinitionRegistryPostProcessors实现,所以Spring为了确保任何BeanDefinitionRegistryPostProcessors的方法都会被调用,会孜孜不倦地尝试检索并收集被遗漏了的BeanDefinitionRegistryPostProcessors实现,把这些被遗漏的processors,做最后一次排序,并执行postProcessBeanDefinitionRegistry()方法。

postProcessBeanFactory()

接着遍历并调用BeanFactoryPostProcessorpostProcessBeanFactory()方法

image.png

image.png

处理BeanFactoryPostProcessor

接下来是获取BeanFactoryPostProcessor实现的集合,并将其做好分类,最后分别遍历并调用其postProcessBeanFactory()方法,最后清空一下缓存。

image.png

image.png

image.png

Tips: BeanFactoryPostProcessor的postProcessBeanFactory()方法执行时机是在所有Bean定义都已经加载,但还没实例化任何Bean的时候,可以给Bean覆盖或添加一些属性。

总结

该方法主要作用

  1. 调用BeanDefinitionRegistryPostProcessor实现向容器内添加bean的定义
  2. 调用BeanFactoryPostProcessor实现给容器内bean定义添加属性

registerBeanPostProcessors()

接下来执行registerBeanPostProcessors()方法,可以看出该方法主要就是实例化并注册所有BeanPostProcessor实现

image.png

可以看出首先获取容器中BeanPostProcessor实现的全限定类名,然后往容器中添加一个BeanPostProcessorChecker

image.png

接着对所有BeanPostProcessor进行分组和排序

image.png

并调用registerBeanPostProcessors()方法,将postProcessor注册到Spring容器中

image.png

image.png

Tips:在这里只是做BeanPostProcessor注册,并不会调用BeanPostProcessor实现的方法。

总结

该方法的作用是

  1. 找到BeanPostProcesor的实现
  2. 排序后注册到容器中

initMessageSource()

接下来是initMessageSource()方法,该方法主要是用来初始化MessageSource,首先会检查当前Spring容器中是否已存在MessageSource实现,没有的话则会创建并添加到Spring容器中,MessageSource主要是用来做国际化多语言的配置。

image.png

image.png

总结

该方法主要作用

  1. 初始化国际化相关属性

initApplicationEventMulticaster()

接下来调用initApplicationEventMulticaster()方法,该方法主要是初始化事件广播器,首先会检查当前Spring容器中是否已存在ApplicationEventMulticaster实现,没有的话则会创建SimpleApplicationEventMulticaster并添加到Spring容器中。

image.png

image.png

关于SimpleApplicationEventMulticaster之前也有讲解过,传送门《SpringBoot ApplicationListener原理解析

总结

该方法的作用为

  1. 初始化事件广播器

onRefresh()

接下来调用onRefresh()方法,

image.png

AbstractApplicationContext中它其实是一个空方法,留给子类实现。

image.png

因为我引入了Web环境,所以具体执行的时候是调用了ServletWebServerApplicationContextonRefresh()方法

image.png

createWebServer()

可以看到其中调用了createWebServer()方法,而正是在该方法中初始化了Tomcat作为Web容器。

image.png

总结

该方法主要作用为

  1. 创建Web容器

registerListeners()

接下来调用registerListeners()方法,该方法里面的逻辑是把ApplicationListener实现注册到ApplicationEventMulticaster

image.png

image.png

值得注意的是,还有一段判断earlyEventsToProcess不为空的话,则进行事件广播的逻辑。当监听机制还没有完善的时候,如果在这个时候调用广播器并发布事件,会先将内容保存到earlyEventsToProcess集合中,当监听机制初始化完毕了,才会将尚未广播的事件,依次广播出去,也就是执行到下图代码的时候。

image.png

总结

该方法的作用主要是

  1. 添加容器内事件监听器至事件广播器中
  2. 广播早期事件

finishBeanFactoryInitialization()

接下来便是调用finishBeanFactoryInitialization()方法,该方法主要是实例化所有剩余的(非延迟加载)单例。

关于延迟加载可以看这篇《SpringBoot核心特性——延迟初始化

image.png

关于初始化Bean的详细细节,可以看DefaultListableBeanFactorypreInstantiateSingletons()方法,这里限于篇幅太长的问题,我可能会留到以后再写一篇拆解。

总结

该方法主要作用是

  1. 初始化所有剩下的单实例Bean

finishRefresh()

接着调用finishRefresh()方法,该方法主要是清理一些缓存,往容器中注册LifecycleProcessor实现,并调用它们的onRefresh()方法,以及广播ContextRefreshedEvent事件

image.png

image.png

总结

该方法主要作用是

  1. 初始化并注册LifecycleProcessor实现,也就是生命周期处理器
  2. 调用LifecycleProcessoronRefresh()方法
  3. 广播ContextRefreshedEvent事件

总结

由以上可知SpringApplicationrefresh()方法其实会涉及到非常多的处理和逻辑,这个还得反复多琢磨琢磨。但Spring家族的编码风格,变量方法命名是真的很不错,注释简短但很容易让人推断出该变量或方法作用是什么,整体逻辑也是比较清晰的,多断点多反复运行总是可以搞明白的。

如果以上解析你都没记住的话,至少记住一点:看优秀框架源码也不是非常难或者高大上的事情,因为那可比不少历史遗留项目的代码好看多了。

结尾

本文章源自《Learn SpringBoot》专栏,感兴趣的话还请关注点赞收藏.

上一篇文章:《SpringBoot Banner输出原理解析

下一篇文章:《SpringBoot Bean实例化流程解析