BeanPostProcessor —— 連接Spring IOC和AOP的橋梁

轉(zhuǎn)自:http://www.itdecent.cn/p/f607ecc99c00
之前都是從大Boss的視角,來介紹Spring,比如IOC、AOP。
今天換個視角,從一個小嘍啰出發(fā),來加深對Spring的理解。
這個小嘍啰就是,BeanPostProcessor(下面簡稱BPP)。

講解思路:

  • BPP怎么用 —— 先學會怎么用,再去看原理
  • BPP的觸發(fā)時機 —— 在整個Spring Bean初始化流程中的位置
  • BPP自己又是什么時候被創(chuàng)建的?
  • BPP是如何連接IOC和AOP的?

怎么用

BeanPostProcessor,直譯過來,就是“對象后處理器”,那么這個“后”,是指什么之后呢?

試試便知。

我們先寫一個對象,Bean4BBP(本文的所有代碼,可到Github上下載):

@Component
public class Bean4BBP {

    private static final Logger log = LoggerFactory.getLogger(Bean4BBP.class);

    public Bean4BBP(){
        log.info("construct Bean4BBP");
    }
}

然后再寫一個BeanPostProcessor,這時發(fā)現(xiàn)它是一個接口,沒關系,那就寫一個類實現(xiàn)它,CustomBeanPostProcessor:

@Component
public class CustomBeanPostProcessor implements BeanPostProcessor {

    private static final Logger log = LoggerFactory.getLogger(CustomBeanPostProcessor.class);

    public CustomBeanPostProcessor() {
        log.info("construct CustomBeanPostProcessor");
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof Bean4BBP) {
            log.info("process bean before initialization");
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof Bean4BBP) {
            log.info("process bean after initialization");
        }
        return bean;
    }
}

然后啟動我們的Spring Boot項目(直接運行Application類),看這幾條日志打印的順序:

construct CustomBeanPostProcessor
construct Bean4BBP
process bean before initialization
process bean after initialization

BBP對象首先被創(chuàng)建,然后創(chuàng)建Bean4BBP對象,接著再先后執(zhí)行BBP對象的postProcessBeforeInitialization和postProcessAfterInitialization方法。

結(jié)論:“對象后處理器”,指的是“對象創(chuàng)建后處理器”。

我們可以利用它,在對象創(chuàng)建之后,對對象進行修改(有什么場合需要用到?思考題,文末回答。)

那么,為什么要分postProcessBeforeInitialization和postProcessAfterInitialization呢?這里的Initialization是什么意思?

觸發(fā)時機

我們只需要在CustomBeanPostProcessor的postProcessBeforeInitialization和postProcessAfterInitialization方法里,打上兩個斷點,一切自然明了。

斷點進來,跟著調(diào)用棧這點蛛絲馬跡往回走,真相大白:

在initializeBean方法里面,先后調(diào)用了applyBeanPostProcessorsBeforeInitialization和applyBeanPostProcessorsAfterInitialization方法,這兩個方法內(nèi)部,則分別去遍歷系統(tǒng)里所有的BBP,然后逐個執(zhí)行這些BBP對象的postProcessBeforeInitialization和postProcessAfterInitialization方法,去處理對象,以applyBeanPostProcessorsBeforeInitialization為例:

那么夾在applyBeanPostProcessorsBeforeInitialization和applyBeanPostProcessorsAfterInitialization方法中間的invokeInitMethods方法是做什么的呢?

其實這個方法就是Spring提供的,用于對象創(chuàng)建完之后,針對對象的一些初始化操作。這就好比你創(chuàng)建了一個英雄之后,你需要給他進行一些能力屬性的初始化、服裝初始化一樣。

要驗證這一點,很簡單,只需讓Bean4BBP實現(xiàn)InitializingBean接口:

@Component
public class Bean4BBP implements InitializingBean {

    private static final Logger log = LoggerFactory.getLogger(Bean4BBP.class);

    public Bean4BBP(){
        log.info("construct Bean4BBP");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        log.info("init Bean4BBP");
    }
}

然后重新啟動工程,打印順序如下:

construct CustomBeanPostProcessor
construct Bean4BBP
process bean before initialization
init Bean4BBP
process bean after initialization

BBP是什么時候被初始化的
從上面的代碼片段,我們已經(jīng)知道,在對象創(chuàng)建之后,需要遍歷BBP列表,對對象進行處理。

這也就意味著,BBP對象,必須在普通對象創(chuàng)建之前被創(chuàng)建。

那么BBP都是在什么時候被創(chuàng)建的呢?

要回答這個問題,非常簡單,我們只需要在CustomBeanPostProcessor的構造函數(shù)里打個斷點(這下看到先學會用,再了解原理的好處了吧)

斷點進來,繼續(xù)利用調(diào)用棧,我們找尋到了AbstractApplicationContext的refresh()方法,這個方法里面調(diào)用了registerBeanPostProcessors方法,里頭就已經(jīng)把BBP列表創(chuàng)建好了,而普通對象的創(chuàng)建,是在之后的finishBeanFactoryInitialization方法里執(zhí)行的:


網(wǎng)上有個圖畫的特別好,很好的展示了BBP在Spring對象初始化流程的位置:


看到BBP在哪了嗎?)

BBP的典型使用 - AOP

不知道大家在使用Spring AOP時,有沒有發(fā)現(xiàn),帶有切面邏輯的對象,注入進來之后,都不是原來的對象了,比如下圖:


調(diào)試信息顯示,aspectService是一個...$$EnhanceBySpringCGlib的對象,這其實和Spring AOP用到的動態(tài)代理有關。

這也就意味著,最終放進Spring容器的,必須是代理對象,而不是原先的對象,這樣別的對象在注入時,才能獲得帶有切面邏輯的代理對象。

那么Spring是怎么做到這一點的呢?正是利用了這篇文章講到的BBP。

顯然,我只需要寫一個BBP,在postProcessBeforeInitialization或者postProcessAfterInitialization方法中,對對象進行判斷,看他需不需要織入切面邏輯,如果需要,那我就根據(jù)這個對象,生成一個代理對象,然后返回這個代理對象,那么最終注入容器的,自然就是代理對象了。

這個服務于Spring AOP的BBP,叫做AnnotationAwareAspectJAutoProxyCreator.

利用idea的diagram功能,可以看出它和BBP的關系:

具體的創(chuàng)建代理對象并返回的邏輯,在postProcessAfterInitialization方法中,大家自行欣賞。

可以說,如果沒有BBP,那么Spring AOP就只能叫AOP。

BBP是連接IOC和AOP的橋梁。

總結(jié)

這篇文章,主要通過對BBP的講解,串聯(lián)起之前講到的關于Spring的知識,希望能夠加深大家對Spring的理解。

最后,回到開頭提出的四個問題:

BBP怎么用 —— 先學會怎么用,再去看原理
BBP的觸發(fā)時機 —— 在整個Spring Bean初始化流程中的位置
BBP自己又是什么時候被創(chuàng)建的?
BBP是如何連接IOC和AOP的?
也許你弄懂了,也許沒懂,沒關系,這篇文章還是跟以前的文章風格不太一樣的,比較嚴肅,有些門檻。

參考

《Spring揭秘》

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容