前言
我们知道,spring 的启动其实就是容器的启动,而一般情况下,容器指的其实就是上下文 ApplicationContext。
AbstractApplicationContext 作为整个 ApplicationContext 体系中最高级的抽象类,为除了 ComplexWebApplicationContext 和 SimpleWebApplicationContext 这两个容器外的全部容器,规定好了 refresh 的整体流程,所有的容器在完成一些自己的初始化配置后,都需要调用该 refresh 方法,依次完成指定内容的初始化。
也就是说,读懂了 AbstractApplicationContext.refresh() 方法,其实就读懂了容器的启动流程:
1 | public void refresh() throws BeansException, IllegalStateException { |
从总体来看,该方法描述的初始化过程大概分为三步:
- [x] 上下文的初始化;
- [ ]
BeanFactory初始化; - [ ] 事件,Bean及其他配置的初始化;
笔者将基于 spring 源码 5.2.x 分支,分别通过五篇文章从源码分析 spring 容器的初始化过程。
本文是其中的第一篇文章,将介绍上下文的初始化过程。
相关文章:
一、刷新上下文属性
上下文的初始化,对应 prepareRefresh,obtainFreshBeanFactory,prepareBeanFactory 三个步骤。
其中,调用 prepareRefresh 是第一个步骤,它用于用于初始化 ApplicationContext 本身的一些属性。
1 | protected void prepareRefresh() { |
1、初始化属性数据源
initPropertySources 方法在 AbstractApplicationContext 是个空方法:
1 | protected void initPropertySources() { |
这里需要先强调一下占位符的概念。由于在 spring 启动过程,很多对象的需要经过多个阶段才能彻底完成配置,但是在未加载完毕的时候就需要在其他地方被引用,为此 spring 中很多地方引入了名称为 XXXHolder 的类,用 Holder 表示一个还未加载完成的实例。
initPropertySources 的本意是将一些上下文属性里面数据源的占位符替换为真正的资源对象,实际场景中,该方法只有在 WebApplicationContext 体系下的实现类会去重写该方法,比如 AbstractRefreshableWebApplicationContext:
1 |
|
这个方法的作用是在 web 环境中,把 servlet 相关的一些配置的占位符替换为真正的 servletContext 和 servletConfig。
2、必要属性参数的校验
getEnvironment().validateRequiredProperties() 这行代码的作用的校验必要的属性参数,实际操作的是 Environment,我们以 AbstractEnvironment#validateRequiredProperties 为例,该方法最后会调用 AbstractPropertyResolver#validateRequiredProperties :
1 | // AbstractEnvironment |
这里的处理非常简单,遍历非空属性,如果是空就抛出 MissingRequiredPropertiesException 异常。
二、刷新上下文中的工厂
调用 AbstarctApplicationContext.obtainFreshBeanFactory() 方法是初始化容器的第二步。
该方法主要用于关闭上下文中可能存在的旧 BeanFactory,并创建新的 BeanFactory。
按 AbstractApplicationContext 中对 refreshBeanFactory 的解释,该方法的实现按容器是否可重复刷新分为两种:
- 直接返回上下文中原有的工厂,如果重复刷新会抛出
IllegalStateException异常; - 直接创建一个新工厂,然后替换上下文中原有的工厂;
1、刷新工厂
可重复刷新的上下文
不允许重复刷新的容器包括 GenericApplicationContext 与它的子类,或者更明确一点,除了 AbstractRefreshableApplicationContext 子类外的全部上下文:
1 |
|
不可重复刷新的上下文
可重复刷新的容器包括 AbstractRefreshableApplicationContext 的子类:
1 |
|
2、向工厂注册BeanDefinition
加载 BeanDefinition 这一步其实是在 refreshBeanFactory() 完成的,在 AbstractRefreshableApplicationContext 中他是一个抽象方法,而有三个子类实现了该方法,它们分别是:
AnnotationConfigWebApplicationContext;GroovyWebApplicationContext;XmlWebApplicationContext;
光看着三个上下文的名称,用脚指头都能猜到,不同的配置环境会对应不同的实现类,实现类会在这一步根据不同的配置文件解析并加载 BeanDefinition 到 BeanFactory 里。
我们以注解配置对应的 AnnotationConfigWebApplicationContext 为例:
1 | protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) { |
不同的实现对应不同的代码,不过逻辑基本都一致,无外乎创建 Reader 然后解析配置文件/配置类,然后把配置中的 Bean 或者扫描配置的 class 或项目路径上的 Class 得到的 Bean 加载到 BeanFactory 里。
3、关闭旧工厂
若当前已有 BeanFactory,上下文会先尝试移除该工厂,然后再新建工厂。
destroyBeans调用 将销毁工厂中全部的单例 bean,并清空相关缓存,比如我们熟悉的三级缓存:
1 | protected void destroyBeans() { |
然后再在 closeBeanFactory 方法中移除当前上下文对应旧 BeanFactory 实例的引用:
1 | protected final void closeBeanFactory() { |
4、创建新工厂
新建工厂时,先通过 createBeanFactory 方法创建一个 DefaultListableBeanFactory,该 DefaultListableBeanFactory 会具备当前上下文的父工厂实例:
1 | protected DefaultListableBeanFactory createBeanFactory() { |
然后获得 DefaultListableBeanFactory 实例后,再在通过 customizeBeanFactory 方法同步一些参数:
1 | protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) { |
最后通过 loadBeanDefinitions 将 bean 的信息重新加载到新的工厂中。这里需要注意的是,在 AbstractApplicationContext 中 loadBeanDefinitions 是个抽象方法,根据子类的不同会有不同的实现,我们以比较熟悉的 XmlWebApplicationContext 为例:
1 |
|
这里的 Reader 与上下文的类型对应,或者说,上下文能读取的配置就取决于 Reader 的类型,不同类型的上下文有不同的 Reader,比如 AnnotationConfigApplicationContext 中使用的就是 AnnotatedBeanDefinitionReader。
这部分具体的解析流程会另外开一篇文章分析,这里暂且知道是重新加载配置文件,获取 bean 定义信息就行。
至此,旧的 BeanFactory 已经被新的 BeanFactory 替换,配置和数据都已经转移到新的 BeanFactory 中。
三、对工厂进行预处理
现在,经过 refreshBeanFactory 方法的处理,旧的工厂被销毁,新的 BeanFactory 也已经准备好了。接着,上下文需要通过 prepareBeanFactory 方法对这个新的工厂再进行初始化:
1 | protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) { |
这个方法主要用于为 BeanFactory 添加默认的后置处理器,与一些默认的 bean 实例。
设置默认Bean为已解析
需要注意的是,正常情况下,我们通过容器获取到的 bean 都是根据 BeanDefinition 创建的,而在这里注册的四个 bean :
ResourceLoader;BeanFactory;ApplicationEventPublisher;ApplicationContext;
这四个类要么是容器本身,要么是容器本身持有的成员变量,因此都是没有对应 BeanDefinition 的。
它在这一步会被直接设置为已经完成解析。
后处理器
这里默认注册了 ApplicationContextAwareProcessor 这个 Bean 后处理器,当它调用 postProcessBeforeInitialization 方法时,会再去调用 6 个 Aware 接口:
EnvironmentAware;EmbeddedValueResolverAware;ResourceLoaderAware;ApplicationEventPublisherAware;MessageSourceAware;ApplicationContextAware;
也就说,这 6 个 Aware 接口会在 Bean 初始化前被调用。
此外,还注册了 ApplicationListenerDetector 这个后处理,该后处理器会在 Bean 初始化前被调用,然后实现了 ApplicationEventListener 接口的 Bean 就会被注册到广播器中。
总结
BeanFactory 的初始化共分为三个方法,对应三个主要过程:
prepareRefresh:初始化上下文的属性以及一些状态;obtainFreshBeanFactory:销毁上下文中的旧BeanFactory,然后创建新的BeanFactory对象;prepareBeanFactory:为新BeanFactory配置一些基本的参数,包括注册一些默认 Bean、Bean 作用域以及一些后置处理器,以及根据配置文件加载BeanDefinition;
