Spring注解驱动开发第16讲——面试官再问你BeanPostProcessor的执行流程,就把这篇文章甩给他!
发布日期:2021-06-30 17:56:07 浏览次数:3 分类:技术文章

本文共 8586 字,大约阅读时间需要 28 分钟。

写在前面

在前面的文章中,我们讲述了BeanPostProcessor的postProcessBeforeInitialization()方法和postProcessAfterInitialization()方法在bean初始化的前后调用。而且我们可以自定义类来实现BeanPostProcessor接口,并在postProcessBeforeInitialization()方法和postProcessAfterInitialization()方法中编写我们自定义的逻辑。

今天,我们来一起探讨下BeanPostProcessor的底层原理。

bean的初始化和销毁

我们知道BeanPostProcessor的postProcessBeforeInitialization()方法是在bean的初始化之前被调用;而postProcessAfterInitialization()方法是在bean初始化的之后被调用。并且bean的初始化和销毁方法我们可以通过如下方式进行指定。

(一)通过@Bean指定init-method和destroy-method

@Bean(initMethod="init", destroyMethod="destroy")public Car car() {
return new Car();}

(二)通过让bean实现InitializingBean和DisposableBean这俩接口

package com.meimeixia.bean;import org.springframework.beans.factory.DisposableBean;import org.springframework.beans.factory.InitializingBean;import org.springframework.context.annotation.Scope;import org.springframework.stereotype.Component;@Componentpublic class Cat implements InitializingBean, DisposableBean {
public Cat() {
System.out.println("cat constructor..."); } /** * 会在容器关闭的时候进行调用 */ @Override public void destroy() throws Exception {
// TODO Auto-generated method stub System.out.println("cat destroy..."); } /** * 会在bean创建完成,并且属性都赋好值以后进行调用 */ @Override public void afterPropertiesSet() throws Exception {
// TODO Auto-generated method stub System.out.println("cat afterPropertiesSet..."); }}

(三)使用JSR-250规范里面定义的@PostConstruct和@PreDestroy这俩注解

  • @PostConstruct:在bean创建完成并且属性赋值完成之后,来执行初始化方法
  • @PreDestroy:在容器销毁bean之前通知我们进行清理工作
package com.meimeixia.bean;import javax.annotation.PostConstruct;import javax.annotation.PreDestroy;import org.springframework.stereotype.Component;/** * * @author liayun * */@Componentpublic class Dog {
public Dog() {
System.out.println("dog constructor..."); } // 在对象创建完成并且属性赋值完成之后调用 @PostConstruct public void init() {
System.out.println("dog...@PostConstruct..."); } // 在容器销毁(移除)对象之前调用 @PreDestroy public void destory() {
System.out.println("dog...@PreDestroy..."); } }

(四)通过让bean实现BeanPostProcessor接口

package com.meimeixia.bean;import org.springframework.beans.BeansException;import org.springframework.beans.factory.config.BeanPostProcessor;import org.springframework.core.Ordered;import org.springframework.stereotype.Component;/** * 后置处理器,在初始化前后进行处理工作 * @author liayun * */@Component // 将后置处理器加入到容器中,这样的话,Spring就能让它工作了public class MyBeanPostProcessor implements BeanPostProcessor, Ordered {
@Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// TODO Auto-generated method stub System.out.println("postProcessBeforeInitialization..." + beanName + "=>" + bean); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// TODO Auto-generated method stub System.out.println("postProcessAfterInitialization..." + beanName + "=>" + bean); return bean; } @Override public int getOrder() {
// TODO Auto-generated method stub return 3; }}

通过以上这四种方式,我们就可以对bean的整个生命周期进行控制:

  • bean的实例化:调用bean的构造方法,我们可以在bean的无参构造方法中执行相应的逻辑。
  • bean的初始化:在初始化时,可以通过BeanPostProcessor的postProcessBeforeInitialization()方法和postProcessAfterInitialization()方法进行拦截,执行自定义的逻辑;通过@PostConstruct注解、InitializingBean和init-method来指定bean初始化前后执行的方法,在该方法中咱们可以执行自定义的逻辑。
  • bean的销毁:可以通过@PreDestroy注解、DisposableBean和destroy-method来指定bean在销毁前执行的方法,在该方法中咱们可以执行自定义的逻辑。

所以,通过上述四种方式,我们可以控制Spring中bean的整个生命周期。

BeanPostProcessor源码解析

如果想深刻理解BeanPostProcessor的工作原理,那么就不得不看下相关的源码,我们可以在MyBeanPostProcessor类的postProcessBeforeInitialization()方法和postProcessAfterInitialization()方法这两处打上断点来进行调试,如下所示。

在这里插入图片描述
随后,我们以Debug的方式来运行IOCTest_LifeCycle类中的test01()方法,运行后的效果如下所示。
在这里插入图片描述
可以看到,程序已经运行到MyBeanPostProcessor类的postProcessBeforeInitialization()方法中了,而且在Eclipse的左上角我们可以清晰的看到方法的调用栈。

通过这个方法调用栈,我们可以详细地分析从运行IOCTest_LifeCycle类中的test01()方法开始,到进入MyBeanPostProcessor类的postProcessBeforeInitialization()方法中的执行流程。只要我们在Eclipse的方法调用栈中找到IOCTest_LifeCycle类的test01()方法,依次分析方法调用栈中在该类的test01()方法上面位置的方法,即可了解整个方法调用栈的过程。要想定位方法调用栈中的方法,只需要在Eclipse的方法调用栈中单击相应的方法即可。

温馨提示:方法调用栈是先进后出的,也就是说,最先调用的方法会最后退出,每调用一个方法,JVM会将当前调用的方法放入栈的栈顶,方法退出时,会将方法从栈顶的位置弹出。

接下来,就跟随着笔者的脚步,一步一步来分析从运行IOCTest_LifeCycle类中的test01()方法开始,到进入MyBeanPostProcessor类的postProcessBeforeInitialization()方法中的执行流程。

第一步,我们在Eclipse的方法调用栈中,找到IOCTest_LifeCycle类的test01()方法并单击,此时Eclipse的主界面会定位到IOCTest_LifeCycle类的test01()方法中,如下所示。

在这里插入图片描述
在IOCTest_LifeCycle类的test01()方法中,首先通过new实例对象的方式创建了一个IOC容器。

第二步,通过Eclipse的方法调用栈继续分析,单击IOCTest_LifeCycle类的test01()方法上面的那个方法,这时会进入AnnotationConfigApplicationContext类的构造方法中。

在这里插入图片描述
可以看到,在AnnotationConfigApplicationContext类的构造方法中会调用refresh()方法。

第三步,我们继续跟进方法调用栈,如下所示,可以看到,方法的执行定位到AbstractApplicationContext类的refresh()方法中的如下那行代码处。

在这里插入图片描述
上面这行代码的作用就是初始化所有的(非懒加载的)单实例bean对象。

AbstractApplicationContext类中的refresh()方法有点长,我怕同学们看不清,所以又截了一张图,如下所示,可以清楚地看到refresh()方法里面调用了finishBeanFactoryInitialization()方法。

在这里插入图片描述
第四步,我们继续跟进方法调用栈,如下所示,可以看到,方法的执行定位到AbstractApplicationContext类的finishBeanFactoryInitialization()方法中的如下那行代码处。
在这里插入图片描述
这行代码的作用同样是初始化所有的(非懒加载的)单实例bean。

AbstractApplicationContext类的finishBeanFactoryInitialization()方法同样有点长,我怕同学们看不清,所以又截了一张图,如下所示。

在这里插入图片描述
第五步,我们继续跟进方法调用栈,如下所示,可以看到,方法的执行定位到DefaultListableBeanFactory类的preInstantiateSingletons()方法的最后一个else分支调用的getBean()方法上。
在这里插入图片描述
DefaultListableBeanFactory类的preInstantiateSingletons()方法同样是有点长,我怕同学们看不清,所以又截了一张图,如下所示。
在这里插入图片描述
第六步,继续跟进方法调用栈,如下所示。
在这里插入图片描述
此时方法定位到AbstractBeanFactory类的getBean()方法中了,在getBean()方法中,又调用了doGetBean()方法。

第七步,继续跟进方法调用栈,如下所示,此时,方法的执行定位到AbstractBeanFactory类的doGetBean()方法中的如下那行代码处。

在这里插入图片描述
可以看到,在Spring内部是通过getSingleton()方法来获取单实例bean的。

第八步,继续跟进方法调用栈,如下所示,此时,方法定位到DefaultSingletonBeanRegistry类的getSingleton()方法中的如下那行代码处。

在这里插入图片描述
可以看到,在getSingleton()方法里面又调用了getObject()方法来获取单实例bean。

第九步,继续跟进方法调用栈,如下所示,此时,方法定位到AbstractBeanFactory类的doGetBean()方法中的如下那行代码处。

在这里插入图片描述
也就是说,当第一次获取单实例bean时,由于单实例bean还未创建,那么Spring会调用createBean()方法来创建单实例bean。

第十步,继续跟进方法调用栈,如下所示,可以看到,方法的执行定位到AbstractAutowireCapableBeanFactory类的createBean()方法中的如下那行代码处。

在这里插入图片描述
AbstractAutowireCapableBeanFactory类中的createBean()方法同样也是太长,不太方便观看,所以我就又截了一张图,如下所示。
在这里插入图片描述
可以看到,Spring中创建单实例bean调用的是doCreateBean()方法。

第十一步,继续跟进方法调用栈,如下所示,此时,方法的执行已经定位到AbstractAutowireCapableBeanFactory类的doCreateBean()方法中的如下那行代码处了。

在这里插入图片描述
在initializeBean()方法里面会调用一系列的后置处理器。

第十二步,继续跟进方法调用栈,如下所示,此时,方法的执行定位到AbstractAutowireCapableBeanFactory类的initializeBean()方法中的如下那行代码处。

在这里插入图片描述
小伙伴们需要重点留意一下这个applyBeanPostProcessorsBeforeInitialization()方法。

回过头来我们再来看看AbstractAutowireCapableBeanFactory类的doCreateBean()方法中的如下这行代码。

在这里插入图片描述
没错,在以上initializeBean()方法中调用了后置处理器的逻辑,这我上面已经说到了。小伙伴们需要特别注意一下,在AbstractAutowireCapableBeanFactory类的doCreateBean()方法中,调用initializeBean()方法之前,还调用了一个populateBean()方法,我也在上图中标注出来了。

我们点进去这个populateBean()方法中,看下这个方法到底执行了哪些逻辑,如下所示。

在这里插入图片描述
populateBean()方法同样是AbstractAutowireCapableBeanFactory类中的方法,它里面的代码比较多,但是逻辑非常简单,populateBean()方法做的工作就是为bean的属性赋值。也就是说,在Spring中会先调用populateBean()方法为bean的属性赋好值,然后再调用initializeBean()方法。

接下来,我们好好分析下initializeBean()方法,为了方便,我将Spring中AbstractAutowireCapableBeanFactory类的initializeBean()方法的代码特意提取出来了,如下所示。

在这里插入图片描述
在initializeBean()方法中,调用了invokeInitMethods()方法,代码行如下所示。

invokeInitMethods(beanName, wrappedBean, mbd);

invokeInitMethods()方法的作用就是执行初始化方法,这些初始化方法包括我们之前讲的:在XML配置文件的标签中使用init-method属性指定的初始化方法;在@Bean注解中使用initMehod属性指定的方法;使用@PostConstruct注解标注的方法;实现InitializingBean接口的方法等。

在调用invokeInitMethods()方法之前,Spring调用了applyBeanPostProcessorsBeforeInitialization()这个方法,代码行如下所示。

wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);

在调用invokeInitMethods()方法之后,Spring又调用了applyBeanPostProcessorsAfterInitialization()这个方法,如下所示。

wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);

这里,我们先来看看applyBeanPostProcessorsBeforeInitialization()方法中具体执行了哪些逻辑,该方法位于AbstractAutowireCapableBeanFactory类中,源码如下所示。

在这里插入图片描述
可以看到,在applyBeanPostProcessorsBeforeInitialization()方法中,会遍历所有BeanPostProcessor对象,然后依次执行所有BeanPostProcessor对象的postProcessBeforeInitialization()方法,一旦BeanPostProcessor对象的postProcessBeforeInitialization()方法返回null以后,则后面的BeanPostProcessor对象便不再执行了,而是直接退出for循环。这些都是我们看源码看到的。

看Spring源码,我们还看到了一个细节,在Spring中调用initializeBean()方法之前,还调用了populateBean()方法来为bean的属性赋值, 这在上面我也已经说过了。

经过上面的一系列的跟踪源码分析,我们可以将关键代码的调用过程使用如下伪代码表述出来。

populateBean(beanName, mbd, instanceWrapper); // 给bean进行属性赋值initializeBean(beanName, exposedObject, mbd){
applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); invokeInitMethods(beanName, wrappedBean, mbd); // 执行自定义初始化 applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);}

也就是说,在Spring中,调用initializeBean()方法之前,调用了populateBean()方法为bean的属性赋值,为bean的属性赋好值之后,再调用initializeBean()方法进行初始化。

在initializeBean()中,调用自定义的初始化方法(即invokeInitMethods())之前,调用了applyBeanPostProcessorsBeforeInitialization()方法,而在调用自定义的初始化方法之后,又调用了applyBeanPostProcessorsAfterInitialization()方法。至此,整个bean的初始化过程就这样结束了。

转载地址:https://liayun.blog.csdn.net/article/details/110442093 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:Spring注解驱动开发第17讲——BeanPostProcessor在Spring底层是如何使用的?看完这篇我懂了!!
下一篇:Spring注解驱动开发第15讲——关于BeanPostProcessor后置处理器,你了解多少?

发表评论

最新留言

不错!
[***.144.177.141]2024年04月24日 14时21分45秒