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

寫在前面

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

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

bean的初始化和銷燬

我們知道BeanPostProcessor的postProcessBeforeInitialization()方法在bean的初始化之前調用;而postProcessAfterInitialization()方法在bean初始化的之後調用。而bean的初始化和銷燬方法我們可以通過如下方式進行指定。

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的整個生命週期進行控制:

  • 從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的方式來運行BeanLifeCircleTest類的testBeanLifeCircle04()方法,運行後的效果如下所示。
在這裏插入圖片描述

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

在這裏插入圖片描述

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

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

接下來,我們在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註解驅動開發不再迷茫。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章