【Spring註解驅動開發】面試官再問你BeanPostProcessor的執行流程,就把這篇文章甩給他!

寫在前面

在前面的文章中,咱們講述了BeanPostProcessor的postProcessBeforeInitialization()方法和postProcessAfterInitialization()方法在bean初始化的先後調用,咱們能夠自定義類來實現BeanPostProcessor接口,並在postProcessBeforeInitialization()方法和postProcessAfterInitialization()方法中指定咱們自定義的邏輯。今天,咱們來一塊兒探討下eanPostProcessor底層原理。java

項目工程源碼已經提交到GitHub:https://github.com/sunshinelyz/spring-annotationgit

bean的初始化和銷燬

咱們知道BeanPostProcessor的postProcessBeforeInitialization()方法在bean的初始化以前調用;而postProcessAfterInitialization()方法在bean初始化的以後調用。而bean的初始化和銷燬方法咱們能夠經過以下方式進行指定。github

1.經過@Bean指定init-method和destroy-method

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

2.經過讓Bean實現InitializingBean(定義初始化邏輯)

@Component
public class Cat implements InitializingBean,DisposableBean {
    public Cat(){
        System.out.println("cat constructor...");
    }
    @Override
    public void destroy() throws Exception {
        System.out.println("cat...destroy...");
    }
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("cat...afterPropertiesSet...");
    }
}

3.能夠使用JSR250

  • @PostConstruct:在bean建立完成而且屬性賦值完成;來執行初始化方法。
  • @PreDestroy:在容器銷燬bean以前通知咱們進行清理工做。
@Component
public class Dog implements ApplicationContextAware {
    //@Autowired
    private ApplicationContext applicationContext;
    public Dog(){
        System.out.println("dog constructor...");
    }
    //對象建立並賦值以後調用
    @PostConstruct
    public void init(){
        System.out.println("Dog....@PostConstruct...");
    }
    //容器移除對象以前
    @PreDestroy
    public void detory(){
        System.out.println("Dog....@PreDestroy...");
    }
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

4.能夠使用BeanPostProcessor

/** * 後置處理器:初始化先後進行處理工做 * 將後置處理器加入到容器中 * 在bean初始化先後進行一些處理工做; * postProcessBeforeInitialization:在初始化以前工做 * postProcessAfterInitialization:在初始化以後工做 */
@Component
public class MyBeanPostProcessor implements BeanPostProcessor,Ordered {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        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() {
        return 3;
    }
}

經過這幾種方式,咱們就能夠對bean的整個生命週期進行控制:spring

  • 從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()方法中打上斷點來進行調試。以下所示。app

在這裏插入圖片描述

隨後,咱們以Debug的方式來運行BeanLifeCircleTest類的testBeanLifeCircle04()方法,運行後的效果以下所示。
在這裏插入圖片描述
ide

能夠看到,程序已經運行到MyBeanPostProcessor類的postProcessBeforeInitialization()方法中,在IDEA的左下角咱們能夠清晰的看到方法的調用棧,以下所示。post

在這裏插入圖片描述

經過這個方法調用棧,咱們能夠詳細的分析從運行BeanLifeCircleTest類的testBeanLifeCircle04()方法開始,到進入MyBeanPostProcessor類的postProcessBeforeInitialization()方法的執行流程。只要咱們在IDEA的方法調用棧中找到BeanLifeCircleTest類的testBeanLifeCircle04()方法,依次分析方法調用棧中在BeanLifeCircleTest類的testBeanLifeCircle04()方法上面位置的方法,便可瞭解整個方法調用棧的過程。要想定位方法調用棧中的方法,只須要在IDEA的方法調用棧中單擊相應的方法便可。學習

注意:方法調用棧是先進後出的,也就是說,最早調用的方法會最後退出,每調用一個方法,JVM會將當前調用的方法放入棧的棧頂,方法退出時,會將方法從棧頂的位置彈出。有關方法調用的具體細節內容,後續會在【JVM】專欄詳細介紹,這裏,小夥伴們就先了解到此便可。this

接下來,咱們在IDEA的方法調用棧中,找到BeanLifeCircleTest類的testBeanLifeCircle04()方法並單擊,此時IDEA的主界面會定位到BeanLifeCircleTest類的testBeanLifeCircle04()方法,以下所示。

在這裏插入圖片描述

在BeanLifeCircleTest類的testBeanLifeCircle04()方法中,首先經過new實例對象的方式建立了一個IOC容器。接下來,經過IDEA的方法調用棧繼續分析,接下來,進入的是AnnotationConfigApplicationContext類的構造方法。

在這裏插入圖片描述

在AnnotationConfigApplicationContext類的構造方法中會調用refresh()方法。咱們跟進方法調用棧,以下所示。

在這裏插入圖片描述

能夠看到,方法的執行定位到AbstractApplicationContext類的refresh()方法中的以下代碼行。

finishBeanFactoryInitialization(beanFactory);

這行代碼的做用就是:初始化全部的(非懶加載的)單實例bean對象。

咱們繼續跟進方法調用棧,以下所示。

在這裏插入圖片描述

此時,方法的執行定位到AbstractApplicationContext類的finishBeanFactoryInitialization()方法的以下代碼行。

beanFactory.preInstantiateSingletons();

這行代碼的做用一樣是:初始化全部的(非懶加載的)單實例bean。

咱們繼續跟進方法調用棧,以下所示。

在這裏插入圖片描述

能夠看到,方法的執行定位到DefaultListableBeanFactory的preInstantiateSingletons()方法的最後一個else分支調用的getBean()方法上。繼續跟進方法調用棧,以下所示。
在這裏插入圖片描述

此時方法定位到AbstractBeanFactory類中的getBean()方法中,在getBean()方法中,又調用了doGetBean()方法,經過方法調用棧咱們能夠得知方法的執行定位到AbstractBeanFactory類中的doGetBean()方法的以下代碼段。

在這裏插入圖片描述

能夠看到,在Spring內部是經過getSingleton()來獲取單實例bean的,咱們繼續跟進方法調用棧,以下所示。

在這裏插入圖片描述

此時,方法定位到了DefaultSingletonBeanRegistry了類的getSingleton()方法的以下代碼行。

singletonObject = singletonFactory.getObject();

繼續跟進方法調用棧,以下所示。
在這裏插入圖片描述

此時,方法會定位到AbstractBeanFactory類的doGetBean()方法中的以下代碼行。

return createBean(beanName, mbd, args);

也就是說,當第一次獲取單實例bean時,因爲單實例bean還未建立,Spring會調用createBean()方法來建立單實例bean。繼續跟進方法調用棧,以下所示。

在這裏插入圖片描述

能夠看到,方法的執行定位到AbstractAutowireCapableBeanFactory類的createBean()方法的以下代碼行。

Object beanInstance = doCreateBean(beanName, mbdToUse, args);

能夠看到,Spring中建立單實例bean調用的是doCreateBean()方法。沒錯,繼續跟進方法調用棧,以下所示。

在這裏插入圖片描述

方法的執行已經定位到AbstractAutowireCapableBeanFactory類的doCreateBean()方法的以下代碼行。

exposedObject = initializeBean(beanName, exposedObject, mbd);

繼續跟進方法調用棧,以下所示。

在這裏插入圖片描述

方法的執行定位到AbstractAutowireCapableBeanFactory類的initializeBean()方法的以下代碼行。

wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);

小夥伴們須要重點留意一下這個applyBeanPostProcessorsBeforeInitialization()方法。回過頭來咱們再來看AbstractAutowireCapableBeanFactory類的doCreateBean()方法中的以下代碼行。

exposedObject = initializeBean(beanName, exposedObject, mbd);

沒錯,在AbstractAutowireCapableBeanFactory類的doCreateBean()方法中調用的initializeBean()方法中調用了後置處理器的邏輯。小夥伴們須要注意一下,在AbstractAutowireCapableBeanFactory類的doCreateBean()方法中調用的initializeBean()方法以前,調用了一個populateBean()方法,代碼行以下所示。

populateBean(beanName, mbd, instanceWrapper);

咱們點到這個populateBean()方法中,看下這個方法執行了哪些邏輯,以下所示。

populateBean()方法一樣是AbstractAutowireCapableBeanFactory類中的方法,populateBean()方法的代碼比較多,其實邏輯很是簡單,populateBean()方法作的工做就是爲bean的屬性賦值。也就是說,在Spring中會先調用populateBean()方法爲屬性賦好值,而後再調用initializeBean()方法。接下來,咱們好好分析下initializeBean()方法,爲了方便,我將Spring中AbstractAutowireCapableBeanFactory類的initializeBean()方法的代碼拿出來,以下所示。

protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
    if (System.getSecurityManager() != null) {
        AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
            invokeAwareMethods(beanName, bean);
            return null;
        }, getAccessControlContext());
    }
    else {
        invokeAwareMethods(beanName, bean);
    }

    Object wrappedBean = bean;
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    }

    try {
        invokeInitMethods(beanName, wrappedBean, mbd);
    }
    catch (Throwable ex) {
        throw new BeanCreationException(
            (mbd != null ? mbd.getResourceDescription() : null),
            beanName, "Invocation of init method failed", ex);
    }
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }

    return wrappedBean;
}

在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()方法中具體執行了哪些邏輯,applyBeanPostProcessorsBeforeInitialization()方法位於AbstractAutowireCapableBeanFactory類中,源碼以下所示。

@Override
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
    throws BeansException {

    Object result = existingBean;
    for (BeanPostProcessor processor : getBeanPostProcessors()) {
        Object current = processor.postProcessBeforeInitialization(result, beanName);
        if (current == null) {
            return result;
        }
        result = current;
    }
    return result;
}

能夠看到,在applyBeanPostProcessorsBeforeInitialization()方法中,會遍歷全部BeanPostProcessor對象,執行全部BeanPostProcessor對象的postProcessBeforeInitialization()方法,一旦BeanPostProcessor對象的postProcessBeforeInitialization()方法返回null,則後面的BeanPostProcessor對象再也不執行,直接退出for循環。

看Spring源碼,咱們看到一個細節, 在Spring中調用initializeBean()方法以前,調用了populateBean()方法來爲bean的屬性賦值。

咱們將關鍵代碼的調用過程使用以下僞代碼表述出來。

populateBean(beanName, mbd, instanceWrapper);
initializeBean(beanName, exposedObject, mbd){
    applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    invokeInitMethods(beanName, wrappedBean, mbd);
    applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}

也就是說,在Spring中,調用initializeBean()方法以前,調用了populateBean()方法爲bean的屬性賦值,爲bean的屬性賦好值以後,再調用initializeBean()方法進行初始化。

在initializeBean()中,調用自定義的初始化方法invokeInitMethods()以前,調用了applyBeanPostProcessorsBeforeInitialization()方法,而在調用自定義的初始化方法invokeInitMethods()以後,調用了applyBeanPostProcessorsAfterInitialization()方法。整個bean的初始化過程就結束了。

好了,我們今天就聊到這兒吧!別忘了給個在看和轉發,讓更多的人看到,一塊兒學習一塊兒進步!!

項目工程源碼已經提交到GitHub:https://github.com/sunshinelyz/spring-annotation

寫在最後

若是以爲文章對你有點幫助,請微信搜索並關注「 冰河技術 」微信公衆號,跟冰河學習Spring註解驅動開發。公衆號回覆「spring註解」關鍵字,領取Spring註解驅動開發核心知識圖,讓Spring註解驅動開發再也不迷茫。

相關文章
相關標籤/搜索