Spring Bean的生命周期(四) - 初始化阶段
上一章,我们从后置处理器的初始化开始,了解了 bean 在实例化前的一些处理,以及实例化过程中的一些逻辑。bean 对象实例化出来之后,下一步就要给 bean 的属性赋值,以及组件的依赖注入了。
本章主要会涉及到的原理部分:
bean 的属性赋值 & 组件依赖注入
bean 的初始化方法回调
重要的
BeanPostProcessor功能解析
1. doCreateBean
上一章中我们只解析了 doCreateBean 方法中的第一小段,接下来我们继续往下读。
1.1 回调MergedBeanDefinitionPostProcessor
// ......
// Allow post-processors to modify the merged bean definition.
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}
// catch ......
mbd.postProcessed = true;
}
}
// ......这个阶段它会回调所有的 MergedBeanDefinitionPostProcessor ,这个阶段本身没什么好解释的,不过这里面有几个关键的 MergedBeanDefinitionPostProcessor 实现,咱在这里还是有必要展开讲一下的。
1.1.1 InitDestroyAnnotationBeanPostProcessor
顾名思义,它是处理初始化和销毁注解的后置处理器,它的核心处理逻辑如下:
很明显,findLifecycleMetadata 方法是查找 bean 的生命周期元信息,进入到该方法中:
这里又是用缓存机制,把 bean 的初始化和销毁注解信息都保存到 lifecycleMetadataCache 里了。咱主要关注的是 buildLifecycleMetadata 方法,看它都是怎么搞的。
这段源码还是比较容易理解的,它会寻找这个 bean 所属的 Class 中是否有包含初始化注解和销毁注解的方法。而初始化注解、销毁注解分别是啥呢?在子类 CommonAnnotationBeanPostProcessor 中有指定:
好吧,果然还是 JSR 250 的那一套。那合着说,**InitDestroyAnnotationBeanPostProcessor** 就是收集标注了 **@PostConstruct** 和 **@PreDestroy** 注解的后置处理器呗。
1.1.2 CommonAnnotationBeanPostProcessor
呦,马上就到这个子类了,咱看看这个类里有什么扩展:
除了调用父类 InitDestroyAnnotationBeanPostProcessor 的收集动作之外,它在这里还要收集有关注入的注解信息。与上面的 findLifecycleMetadata 方法类似,它也是设置有缓存的机制,我们直接看实际干活的方法了:
由这段节选的源码可见,它还能支持 JAVA EE 规范中的 @WebServiceRef 、@EJB 、@Resource 注解,并封装对应的注解信息。由于我们已经对 EJB 等家伙不关注了,所以这里小伙伴们只需要知道它相当于额外收集了 @Resource 注解的信息即可。
1.1.3 AutowiredAnnotationBeanPostProcessor
有了前面两个后置处理器的知识,这个后置处理器的功能不用看也就能猜得到了吧:它应该是收集自动注入的注解信息的。底层的原理与前两者的逻辑设计一模一样,小册就不重复贴出了,这里重点说一下它支持的注解。
可以发现,AutowiredAnnotationBeanPostProcessor 在构造方法中,就已经指定好它默认支持 @Autowired 注解、@Value 注解,如果 classpath 下有来自 JSR 330 的 @Inject 注解,也会一并支持。
1.1.4 注解后置处理器小结
从这几个后置处理器中,我们就可以发现,可以支持 bean 中标注的注解的后置处理器,它们的处理方式都是先收集注解标注的信息,保存到缓存中,随后再处理时,只需要从缓存中取就可以了。
1.2 早期bean对象引用的获取与缓存
这里有一个获取早期 bean 对象引用的动作,这一步是为了解决 bean 之间的循环依赖问题。这里我们只是说一下这个早期 bean 对象的引用是个什么东西。
从前面的逻辑中,bean 被实例化出来之后还没有进行属性赋值和组件的依赖注入,但此时的 bean 对象已经实实在在的存在了。如果在此期间,有另外的 bean 又需要创建它时,就不应该再创建同样的一个 bean 对象,而是直接拿到该引用即可,这个设计就是为了解决 bean 之间的循环依赖而用(从 getEarlyBeanReference 方法的 javadoc 上也能看到)。
有关 bean 的循环依赖,在本小册中不会有涉及,在我的《SpringBoot 小册》第 15 章 有完整的讲过 SpringFramework 解决循环依赖的全流程,有需要的小伙伴们可以跳转过去学习哦。
1.3 populateBean - 属性赋值+依赖注入
接下来的两步是挨着的:
这里我们先看 populateBean 这一步,由于这个方法中的内容还是比较多,我们拆解开来看。
1.3.1 检查bean&准备
这个地方仅仅是为了检查 BeanWrapper 中是否存在而已,没什么好说的。
1.3.2 回调InstantiationAwareBeanPostProcessor
这个部分很明显就是回调所有的 InstantiationAwareBeanPostProcessor 而已,不过这上面有好长一段注释,我们来看看它讲了些啥:
Give any InstantiationAwareBeanPostProcessors the opportunity to modify the state of the bean before properties are set. This can be used, for example, to support styles of field injection.
在设置属性之前,让任何 InstantiationAwareBeanPostProcessor 都有机会修改 bean 的状态,例如支持属性字段的注入。
哦,合着 postProcessAfterInstantiation 方法也是干预 bean 的属性等东西的呀。而且有一个小细节注意一下,postProcessAfterInstantiation 方法返回的是 boolean 类型,当 postProcessAfterInstantiation 返回 false 时,会直接 return 出去,不再执行下面的属性赋值 + 组件依赖注入的逻辑!(通常这个方法一定是返回 true 的,底层默认也是返回 true )
可能会有小伙伴产生疑惑,那这个回调有什么可以利用的吗?注意思考,当 bean 的创建来到这个位置的时候,此时 bean 的状态如何?还是所有属性都为空吧,而且还都没有执行任何初始化的逻辑吧。所以这个回调的位置,就是让咱在 bean 已经初始化好,但还没有开始属性赋值和依赖注入时切入自定义逻辑。
1.3.3 自动注入支持
这一部分的逻辑是支撑 “自动注入模式” 功能的,我们在第 24 章已经简单的讲过了,这里我们简单地看一下源码的实现就好了。
在上面先把当前正在创建的 bean 的自动注入模式解析出来,之后根据自动注入模式,执行对应的逻辑。这里我们以 byName 为例看一下它里面的实现。
可以发现,这个逻辑是相当的简单吧!就仅仅是查出来,添加上依赖关系,就完事了,没有任何的多余动作,这部分的逻辑也没什么好说的咯。
另外留意一点!在这段代码执行完毕后,有 pvs = newPvs; 的步骤,它将这个过程中需要组件依赖注入的信息也都封装进了 PropertyValues 中了,所以此时 **pvs** 中就应该有一开始封装的信息,以及通过自动注入封装好的依赖信息。
1.3.4 又回调InstantiationAwareBeanPostProcessor
这一段源码又在取那些 InstantiationAwareBeanPostProcessor 了,不过注意这次回调的方法是 **postProcessProperties** 和 postProcessPropertyValues 方法了( postProcessPropertyValues 方法在 SpringFramework 5.1 之后被废弃了)。注意 postProcessProperties 方法会传入 PropertyValues ,也会返回 PropertyValues ,所以这个过程相当于反复给 **PropertyValues** 封装数据。那这个封装的过程我们是有必要了解一下的。
之前在第 27 章我们已经知道,这个时机的回调是属性赋值和组件依赖注入的核心时机,所以我们需要关注这里面一个非常重要的后置处理器:**AutowiredAnnotationBeanPostProcessor** 。
AutowiredAnnotationBeanPostProcessor#postProcessProperties
AutowiredAnnotationBeanPostProcessor 的 postProcessProperties 方法的实现,看上去还蛮简单的:
嗯,看到 findAutowiringMetadata 方法,是不是又想到了前面那个一模一样的套路呢?又是收集所有标注了 @Autowired 、@Value、@Inject 注解的信息,返回回来。不过这里不只是收集了,它要真正的给 bean 对象的属性赋值!
到这里,小伙伴是否有回想起在第 30 章中,我们说过的,在 BeanFactoryPostProcessor 或者 BeanDefinitionRegistryPostProcessor 中无法直接使用 @Autowired 直接注入 SpringFramework 中的内部组件(如 Environment )?现在是否明白这个问题的答案了吗?当 **BeanDefinitionRegistryPostProcessor** 在初始化的阶段,还不存在 **BeanPostProcessor** 呢,所以那些用于支持依赖注入的后置处理器( **AutowiredAnnotationBeanPostProcessor** )还没有被初始化,自然也就没办法支持注入了。正确的做法是借助 **Aware** 接口的回调注入。
metadata.inject
整体逻辑还是非常简单的哈,给一个 bean 进行注入,就相当于给这个 bean 中需要注入的元素依次注入。
继续往下进入到 element.inject 方法,这里面就比较复杂了(因为它有两个重写的复杂方法),我们只需要看一下它的核心工作机制就好了:
嚯,这也太简单了吧,就仅仅是反射注入 / 调用呗?当然啦,太复杂的是这个方法重写的两个子类 AutowiredFieldElement 和 AutowiredMethodElement ,但它们处理的逻辑大同小异,只是处理步骤复杂了好多而已。小伙伴们没有必要深追这里面的实现细节,能对这个步骤的底层基本实现心领神会即可。
1.3.5 属性赋值
到这里为止,PropertyValues 的内容都封装好了,来看最后一段源码:
核心动作就只有 applyPropertyValues 这一个动作,而从方法名上就能 get 到,这个 applyPropertyValues 方法的作用,就是把前面准备好的 **PropertyValues** 对象封装的内容,应用到当前正在创建的 bean 实例上。
接下来咱进入到 applyPropertyValues 方法中一探究竟。。。呃,这个方法实在是太长了,我们依然是拆解来看吧。
预检查和准备
这段很简单啦,如果直接没有要应用的 bean 属性,则直接返回就好,否则它就要准备处理 PropertyValues 了。
重复解析的提前返回
这部分很明显是一个提前返回的动作,if (mpvs.isConverted()) 的动作很明显是检查 PropertyValues 是否已经解析过了,如果已经解析过,则直接应用就行,不需要重复解析。
初始化BeanDefinitionValueResolver
这一段非常短,但这个 BeanDefinitionValueResolver 却很重要!BeanDefinitionValueResolver 的初始化需要传入一个 TypeConverter ,而这个 TypeConverter 是 SpringFramework 中内部用于类型转换的核心 API 。简单的来说,使用 **TypeConverter** 可以将一个 String 类型的数据,转换为特定的所需要的类型的数据。而 BeanDefinitionValueResolver 就利用 TypeConverter ,完成对 bean 实例中需要注入的属性值进行解析,并适配为 bean 属性所需要的类型(如 String → int ,依赖 bean 的名称转为实际 bean 的引用)。
类型转换
这一部分小册就不着重解释了,类型转换不属于我们重点关心的东西,小册在源码的关键位置留了注释,小伙伴们看看就得了。
实际在测试代码中,这个方法压根就没走,都被 AutowiredAnnotationBeanPostProcessor 处理完了,所以这个地方我们一带而过得了。
PropertyValues应用到bean
最后一个步骤,这里要把 PropertyValues 中的属性值全部应用到 bean 对象中了。这一部分来回调的源码比较多,最终的落地在 org.springframework.beans.AbstractNestablePropertyAccessor.PropertyHandler#setValue 中,它的其中一个实现类 FieldPropertyHandler 的方法实现如下:
简直简单的不要再简单,反射赋值,简单粗暴。
到这里,整个 populateBean 的属性赋值动作就全部完成了。
1.4 initializeBean
当 bean 的创建过程走到 initializeBean 方法时,此时 bean 中的属性都是齐全的了,但生命周期的回调都还没有回调,接下来我们研究这个部分。
先总体概览一下源码,这里面包含 4 个步骤,均在源码中加以标注:
下面我们分述这几个步骤。
1.4.1 invokeAwareMethods - 执行Aware回调
invokeAwareMethods(beanName, bean); 这个方法中的定义相当简单,只是判断接口的定义,以及强转后的接口方法调用而已,非常容易理解:
不过这里面有一个问题:有关 ApplicationContext 部分的处理,怎么一个也没有咧???有关这个问题,我们马上在下面就提到了,接着往下来看。
1.4.2 applyBeanPostProcessorsBeforeInitialization
这个逻辑就很简单了,它会依次执行容器中所有 BeanPostProcessor 的 postProcessBeforeInitialization 方法。不过这里有一个分支控制处理:如果处理完的结果返回 null ,则不再执行后面剩余的 BeanPostProcessor ,直接返回上一个 BeanPostProcessor 处理之后的 bean 对象返回。由此我们可以在此处针对自己项目中某些特定的 bean 搞一些特殊的拦截的处理。
只看这个逻辑还是很简单的吧,所以我们要深入其中,看几个 BeanPostProcessor 的实现。
InitDestroyAnnotationBeanPostProcessor
又要看它了,既然它有收集 bean 的初始化和销毁信息,那这里就肯定有回调的时机咯。这个源码的写法我们在上面已经看过好几个差不多的了:
根据上面分析的内容,我们已经知道,findLifecycleMetadata 方法可以把标注了 @PostConstruct 和 @PreDestroy 注解的方法都取出来,所以这里就相当于回调 **@PostConstruct** 标注的方法了!
进入到 metadata.invokeInitMethod 方法中:
又是熟悉的 element.invoke 操作,不过这里就没那么复杂了,点进去看:
嚯,直接反射调用方法啊,也是非常简单粗暴咯。
另外,根据这个源码中的反射细节,我们也就知道一开始学习 bean 的初始化和销毁方法标注时的规定了:对于 @PostConstruct 和 @PreDestroy 标注的方法来讲,方法的访问修饰符无所谓,但一定不能有方法参数。
ApplicationContextAwareProcessor
呦,看到类名了,是不是突然明白前面的 Aware 回调为什么没有 ApplicationContext 相关的操作了?是的,这些回调都在这个 ApplicationContextAwareProcessor 中实现了。由此我们可以先得出一个结论:**BeanFactory** 的注入时机比 **ApplicationContext** 早。
接下来我们看源码的实现:
可以发现,这一个 ApplicationContextAwareProcessor 可以支持 6 个 Aware 接口的回调。至于回调的实现,invokeAwareInterfaces 的操作跟上面 invokeAwareMethods 一模一样,小册就不再贴出啦,小伙伴们可以自行去看一下。
1.4.3 invokeInitMethods - 执行初始化生命周期回调
终于到了我们可能最熟悉的部分了,这里有 InitializingBean 接口的 afterPropertiesSet 方法回调,以及 init-method 的回调:
这部分源码理解起来那是相当的容易了是吧!so 小册也不作过多解释咯。
1.4.4 applyBeanPostProcessorsAfterInitialization
处理逻辑与 applyBeanPostProcessorsBeforeInitialization 部分几乎一模一样,我们还是来关注几个重要的后置处理的实现。
AbstractAutoProxyCreator
这就是之前我们在学习 BeanPostProcessor 中一直提到的创建代理的核心处理器了,它也是 AOP 创建代理对象的核心实现。由于这个逻辑属于 AOP 的内容,咱回头放到 AOP 章节详细展开。
ApplicationListenerDetector
这个 ApplicationListenerDetector 我们在上一章的 BeanPostProcessor 初始化阶段还说过一次,它用来关联所有的监听器引用。同样的,监听器在创建的时候,也需要 ApplicationListenerDetector 把这些监听器挂进 ApplicationContext 中,这样这些监听器才可以被事件广播器使用。
1.5 注册销毁时的回调
如果一个 bean 定义的 class 有实现 DisposableBean 接口,或者声明了 @PreDestroy 注解,或者声明了 destroy-method 方法,则会在 doCreateBean 方法的最后一步,注册一个销毁 bean 的回调钩子:
进入 registerDisposableBeanIfNecessary 方法:
可见,通常情况下可以记录销毁 bean 的回调钩子的原则是:单实例 bean ,并且有定义销毁类型的方法,而这些销毁类型的方法,就是刚才提到的三种类型,也是早在第 13 章我们就学过的类型。
对于这些定义了销毁类型的方法的 bean ,会记录到一个 disposableBeans 的 Map 中:
这个 Map 在下一章的 bean 销毁部分还会提及,小伙伴们可以先留意一下。
到这里,doCreateBean 方法全部执行完毕,一个 bean 已经被成功的创建出来了。
2. SmartInitializingSingleton
在上一章的第 2 节 preInstantiateSingletons 方法中,我们只做了 getBean 方法的全解析,当时在源码的注释中,小册在最后埋了一个伏笔:
这个初始化的最后阶段,是在 SpringFramework 4.1 之后才有的,而支撑这个阶段的核心接口是 SmartInitializingSingleton 。
这个接口在开发中的使用意义不是很大,小伙伴们简单了解下就可以了。
2.1 SmartInitializingSingleton的设计
在 SmartInitializingSingleton 接口的 javadoc 中,可以找到这样的一段话:
This callback variant is somewhat similar to org.springframework.context.event.ContextRefreshedEvent but doesn't require an implementation of org.springframework.context.ApplicationListener, with no need to filter context references across a context hierarchy etc. It also implies a more minimal dependency on just the beans package and is being honored by standalone ListableBeanFactory implementations, not just in an org.springframework.context.ApplicationContext environment.
这个回调变体有点类似于 ContextRefreshedEvent ,但是不需要 ApplicationListener 的实现,不需要在整个上下文层次结构中过滤上下文引用。这也意味着更多对 bean 包的依赖性最小,并且由独立的 ListableBeanFactory 实现兑现,而不仅仅是在 ApplicationContext 环境中。
从这段注释中,我们就可以知道,**SmartInitializingSingleton** 的设计是为了只让 **BeanFactory** 控制的,不需要 **ApplicationContext** 也能实现的 bean 的初始化后处理。而且从包名上也能看得出来:org.springframework.beans.factory ,嗯,是 beans 包下的,与 context 没关联。
这个地方作者认为没有必要深究它的出现缘由,毕竟对于我们开发者来讲,可供切入框架中的时机越多,灵活度也就越高,重要的我们学习,不重要的我们知道一下就好了。
2.2 SmartInitializingSingleton的回调
SmartInitializingSingleton 的回调时机,是所有的非延迟加载的单实例 bean 都初始化完成后,再挨个执行 afterSingletonsInstantiated 方法,调用的方式非常简单,逐个判断就完事了,也没什么好解释的。
3. Lifecycle的回调
在 ApplicationContext 的 refresh 方法最后,初始化完成所有非延迟加载的单实例 bean 之后,会执行 finishRefresh 方法:
可以发现,这里面的处理逻辑还是很有序的,首先把该读取的一些资源缓存都清除掉,然后它初始化了一个生命周期处理器,随后调用它的 onRefresh 方法,接下来广播 ContextRefreshedEvent 事件。而回调 Lifecycle 接口的动作,就发生在这个生命周期处理器中。
3.1 initLifecycleProcessor
由此可以发现,无论 BeanFactory 中有没有 LifecycleProcessor ,它都会保证最终容器中会有,注意它的实现类是 DefaultLifecycleProcessor ,我们下面会进入它的内部实现。
3.2 onRefresh
DefaultLifecycleProcessor 的 onRefresh 方法非常简单,就是启动 bean 。继续往下进到源码中:
这里面就是一个一个调用 Lifecycle 的 start 方法的位置了,但请小伙伴们注意一点:由于 onRefresh 方法中调用的 startBeans(true); 传入的参数是 true ,lifecycleBeans.forEach 部分不会执行,所以在该阶段不会回调 Lifecycle 的 start 方法!
3.3 ApplicationContext#start
那这些 Lifecycle 的 start 方法什么时候才能被调用呢?我们回到测试代码中:
下面我们主动的调用了 ctx.start() 方法,而 ApplicationContext 的 start 方法中调用的 LifecycleProcessor ,会最终调用 startBeans(false); ,激活 Lifecycle 回调的逻辑。
好,了解到 Lifecycle 真正回调的时机,下面我们还是看看 SpringFramework 是如何回调 Lifecycle 的。
3.3.1 startBeans
我们把 startBeans 方法分为两部分来看,前半段的操作很明显是一个分组的动作。这里面的 phase 可以类比为 Order ,LifecycleGroup 可以简单的理解为 List<Lifecycle> 。所以这个过程很明显,就是将所有的 **Lifecycle** 按照不同的 **phase** 分组而已。
3.3.2 start
分组之后,它会根据不同的 phase 排序后依次执行,而执行的 start 方法看上去比较复杂,最终还是调用 Lifecycle 的 start 方法:
等所有 Lifecycle 都执行完毕,一个 bean 的完整初始化生命周期也就结束了。
4. 总结
bean 对象创建完成后,会进行属性赋值、组件依赖注入,以及初始化阶段的方法回调。在 **populateBean** 属性赋值阶段,会事先收集好 bean 中标注了依赖注入的注解( **@Autowired** 、**@Value** 、**@Resource** 、**@Inject** ),之后会借助后置处理器,回调 **postProcessProperties** 方法实现依赖注入。
属性赋值和依赖注入之后,会回调执行 bean 的初始化方法,以及后置处理器的逻辑:首先会执行 Aware 相关的回调注入,之后执行后置处理器的前置回调,在后置处理器的前置方法中,会回调 bean 中标注了 **@PostConstruct** 注解的方法,所有的后置处理器前置回调后,会执行 **InitializingBean** 的 **afterPropertiesSet** 方法,随后是 **init-method** 指定的方法,等这些 bean 的初始化方法都回调完毕后,最后执行后置处理器的后置回调。
全部的 bean 初始化结束后,**ApplicationContext** 的 **start** 方法触发时,会触发实现了 **Lifecycle** 接口的 bean 的 **start** 方法。
最后更新于
这有帮助吗?