Spring 框架其本质是作为一个容器,提供给应用程序需要的对象。了解容器的诞生过程,有助于我们理解 Spring 框架,也便于我们“插手”这个过程。
本文不会涉及具体的源码(尽管本文思路来源于此)。
1. 准备
在容器准备阶段,需要告知容器从哪里读取 Bean 信息以及环境信息。尽管,有时我们并未提供这些信息,但容器依然能正确创建。这得益于一些默认行为(约定大于配置),所以,也不需要由我们主动告知。
1. Bean 的准备
Bean 的准备阶段离不开 BeanDefinationRegistry
和 BeanFactory
。
这里需要明白 BeanDefination
对象代表的是 Bean 的描述信息。Bean 并不是从一开始就实例化放入容器的。相反,最开始容器只是拥有它的描述信息,所以我们将此阶段称为 Bean 的准备阶段。根据这些描述信息,Spring 能够创建 Bean,并使它更“完整”。
BeanDefination
在 Spring 中的地位类似于我们的 Class 对象在 JVM 中的地位。
1.1 BeanDefinationRegistry
容器是通过 BeanDefinationRegistry
来注册 BeanDefination
的。
我们可以通过 xml 文件或者 Java 配置的方式来声明 BeanDefination
,并在创建容器的时候指定 xml 文件或者 Java 类,然后,容器会扫描并注册 BeanDefination
。
那么,此阶段我们的机会在哪里呢?其实,Spring 已经包含了足够完善的从定义的 Bean 信息到 BeanDefination
注册。但如果你仍然想自定义 BeanDefination
,或者修改注册的 BeanDefination
,可以通过获取 BeanDefinationRegistry
对象。
Spring 通过回调的方式,调用实现 BeanDefinitionRegistryPostProcessor
接口的类,所以,此类交由 Spring 管理。该类将在后面介绍的刷新阶段回调,此为机会一。
Spring 自身也定义了实现 BeanDefinitionRegistryPostProcessor
接口的类来完成和我们一样想要在运行时自定义加载 BeanDefination
的功能,像 ConfigurationClassPostProcessor
,调用时机和我们自定义的一样,被调用的顺序依赖实现 PriorityOrdered
或 Order
接口。
平等对待第三方,在 dubbo 中见过这种说法,或许对于框架开发,这种做法一直很重要。
1.2 BeanFactory
那么,容器中持有的 Bean 一定需要对应的 BeanDefination
嘛?这不一定,我们可以直接通过 BeanFactory
来注册实例化对象。Spring 在将一些环境信息对象放入容器中,也是这样做的。
此时,我们的机会又在哪里呢?两种方式:
- 实现
BeanFactoryAware
接口。 - 实现
BeanFactoryPostProcessor
接口。
实现用于拓展的接口的类都必须交由 Spring 管理才能触发回调,此为机会二。上述两个回调的时机也都在刷新阶段,后续将详细叙述。
先做个小结,现在,我们知道了,我们可以通过 BeanDefinationRegistry
操作 BeanDefination
,通过 BeanFactory
操作 Bean,那么,如何获得这两个对象便是我们的机会所在。
2. 环境信息
环境信息包括 profile
和 property
,前者可以看作对环境信息做的分类,因为我们需要在不同时候,激活不同的环境,像生产和开发环境。后者是属性,属性可来自不同的属性源。并且,通过环境信息,我们可以将 ${}
占位符替换为具体的属性值。
那么,此时的机会在哪里呢?我们可以自定义一个实现 EnvironmentAware
接口的类,此为机会三。
2. 刷新
刷新阶段涉及到很多回调,我们将一一分析。
2.1 调用 BeanFactoryPostProcessors
此阶段包含了 BeanDefinitionRegistryPostProcessor
接口的回调,因为 BeanDefinitionRegistryPostProcessor
接口继承自 BeanFactoryPostProcessor
。
调用顺序为:
- 调用
BeanDefinitionRegistryPostProcessor
; - 调用
BeanFactoryPostProcessor
;
实现相同接口的对象之间调用顺序将按照PriorityOrdered
和Ordered
接口来。
2.2 注册 BeanPostProcessor
BeanPostProcessor
作用于对象初始化前后,我们实现这个接口便可以在对象初始化前后做处理。同样,实现了该接口后,需要交由 Spring 管理就好。
此阶段仅仅是将 BeanPostProcessor
注册到 BeanFactory
中,并不调用。注册顺序为:
- 实现了
PriorityOrdered
的BeanPostProcessor
; - 实现了
Ordered
的BeanPostProcessor
; - 仅实现
BeanPostProcessor
; - 实现了
MergedBeanDefinitionPostProcessor
;
MergedBeanDefinitionPostProcessor
继承自 BeanPostProcessor
。
2.3 实例化所有非延迟实例化的单例
在实例化单例 Bean 时,第一个回调机会给了 InstantiationAwareBeanPostProcessor
接口,该接口的 postProcessBeforeInstantiation()
方法如果返回了一个对象,该对象将用于存入容器,此为机会四。
如果上面的接口未返回对象,则还允许我们在根据 BeanDefination
创建对象前,再修改 BeanDefination
。第二个回调机会便给了 MergedBeanDefinitionPostProcessor
接口的 postProcessMergedBeanDefinition()
方法。此为机会五。
还有,在存在循环依赖的时候, 如果你想修改你已经放入容器的对象,可以实现 SmartInstantiationAwareBeanPostProcessor
接口,该接口的 getEarlyBeanReference()
方法将被调用。此接口的使用能够保证其它对象依赖注入的是你修改后的对象,容器中也是你修改后的对象。
比如说 A 依赖了 B,B 依赖了 A。在检测到 A 依赖 B 的时候,A 已经实例化完成,这时候该去走创建B 的过程,然后在创建 B 的过程,发现 A 已经创建完成,可以依赖注入,B 的过程正常结束。回到创建 A 的过程,这时,A 也正常注入 B,循环依赖过程完成了。若此时,你再通过
BeanPostProcessor
重新实例化了对象 A 放入容器,这就会导致容器中的 A 和 B 依赖的 A 不是同一个对象。这违背了单例原则,所以,我们可以通过上述描述的接口来保证。更详细的内容可参考我的另一篇博文:如何理解 Spring 循环依赖
紧接着的回调机会给了 InstantiationAwareBeanPostProcessor
接口的 postProcessAfterInstantiation()
方法,此时还未给属性赋值,但我们可以操作实例化的对象了。
再后来,给了我们修改属性值的机会,回调 InstantiationAwareBeanPostProcessor
接口的 postProcessProperties()
方法。如果此方法返回 null ,还可再回调 postProcessPropertyValues
方法。
接下来的过程就是初始化了。初始化之前,如果 Bean 实现了以下接口,那么会回调接口对应的方法:
- BeanNameAware;
- BeanClassLoaderAware;
- BeanFactoryAware;
然后,再回调 BeanPostProcessor
的 postProcessBeforeInitialization
方法。
其实,关于其它 Aware
接口的回调,便是通过 ApplicationContextAwareProcessor
来做的,它是一个 BeanPostProcessor
。所以,如果你自定义了一个 Bean,并且实现了 PriorityOrdered
接口,如果它的优先级过高,那么它可能在 ApplicationContextAwareProcessor
之前调用,这样你某些实现了 Aware
接口的 Bean,可能还尚未获取 aware
注入的对象。
紧接着,就是调用 Bean 的初始化方法,实现了 InitializingBean
的 afterPropertiesSet()
方法或者指定的 init-method
方法将被调用。
初始化完后,便是 BeanPostProcessor
的 postProcessAfterInitialization()
的方法调用。
至此,容器创建过程的回调就介绍完了。
3. 写在最后
我们的机会在于 BeanDefinationRegistry
、BeanFactory
以及处理 Bean。需要注意各个回调接口的调用时机以及条件,这里整理下具体的回调顺序如下:
- 前期(
BeanFactoryPostProcessor
)
- 处理
BeanDefination
:BeanDefinitionRegistryPostProcessor.postProcessBeanDefinitionRegistry()
; - 处理
BeanFactory
:BeanFactoryPostProcessor.postProcessBeanFactory()
;
- 创建 Bean(
BeanPostProcessor
)
-
自定义实例化:
InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation()
; 自定义实例化过程有很多实现方式,比如FactoryBean
或者提供一个Supplier
; -
实例化前修改
BeanDefination
:MergedBeanDefinitionPostProcessor.postProcessMergedBeanDefinition()
; -
实例化后,属性赋值前:
InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation()
-
修改属性(
PropertyValue
):InstantiationAwareBeanPostProcessor.postProcessProperties()
,如果该方法返回 null,还有可以通过postProcessPropertyValues()
方法来修改。 -
属性赋值。
-
初始化前:
BeanNameAware
,BeanClassLoaderAware
,BeanFactoryAware
,BeanPostProcessor.postProcessBeforeInitialization()
。 -
初始化:
InitializingBean.afterPropertiesSet()
或者init-method
。 -
初始化后:
BeanPostProcessor.postProcessAfterInitialization()
。
梳理完上述过程,再结合我们自己的需要,就知道我们如何拓展了。
一个容器的诞生,这个名字听起来很舒服,这让我想到一部电影-传奇的诞生。尽管我所写的是再平凡不过,但我仍愿为此赋予一个美好的寓意。
这是 Spring 闯关指南系列 的第一篇文章,如果你觉得我的文章还不错,并对后续文章感兴趣的话,可以通过扫描下方二维码关注我的公众号。赞!