Java中的BeanPostProcessor——連接Spring IOC和AOP的橋樑

Java中的BeanPostProcessor——連接Spring IOC和AOP的橋樑

可獲得兩大新人禮包

36份一線互聯網Java面試電子書

84個Java稀缺面試題視頻


之前都是從大Boss的視角,來介紹Spring,比如IOC、AOP。

今天換個視角,從一個小嘍囉出發,來加深對Spring的理解。

這個小嘍囉就是,BeanPostProcessor(下面簡稱BBP)。

講解思路:

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

怎麼用

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

試試便知。

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

@Componentpublic class Bean4BBP { private static final Logger log = LoggerFactory.getLogger(Bean4BBP.class); public Bean4BBP(){ log.info("construct Bean4BBP"); }}

然後再寫一個BeanPostProcessor,這時發現它是一個接口,沒關係,那就寫一個類實現它,CustomBeanPostProcessor:

@Componentpublic 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 CustomBeanPostProcessorconstruct Bean4BBPprocess bean before initializationprocess bean after initialization

BBP對象首先被創建,然後創建Bean4BBP對象,接著再先後執行BBP對象的postProcessBeforeInitialization和postProcessAfterInitialization方法。

結論:“對象後處理器”,指的是“對象創建後處理器”。

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

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

觸發時機

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

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

Java中的BeanPostProcessor——連接Spring IOC和AOP的橋樑

在initializeBean方法裡面,先後調用了applyBeanPostProcessorsBeforeInitialization和applyBeanPostProcessorsAfterInitialization方法,這兩個方法內部,則分別去遍歷系統裡所有的BBP,然後逐個執行這些BBP對象的postProcessBeforeInitialization和postProcessAfterInitialization方法,去處理對象,以applyBeanPostProcessorsBeforeInitialization為例:

Java中的BeanPostProcessor——連接Spring IOC和AOP的橋樑

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

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

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

@Componentpublic 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 CustomBeanPostProcessorconstruct Bean4BBPprocess bean before initializationinit Bean4BBPprocess bean after initialization

BBP是什麼時候被初始化的

從上面的代碼片段,我們已經知道,在對象創建之後,需要遍歷BBP列表,對對象進行處理。

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

那麼BBP都是在什麼時候被創建的呢?

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

斷點進來,繼續利用調用棧,我們找尋到了AbstractApplicationContext的refresh()方法,這個方法裡面調用了registerBeanPostProcessors方法,裡頭就已經把BBP列表創建好了,而普通對象的創建,是在之後的finishBeanFactoryInitialization方法裡執行的:

Java中的BeanPostProcessor——連接Spring IOC和AOP的橋樑

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

Java中的BeanPostProcessor——連接Spring IOC和AOP的橋樑

(看到BBP在哪了嗎?)

BBP的典型使用 - AOP

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

Java中的BeanPostProcessor——連接Spring IOC和AOP的橋樑

調試信息顯示,aspectService是一個…$$EnhanceBySpringCGlib的對象,這其實和Spring AOP用到的動態代理有關。

關於Spring AOP的原理,可以參考我之前的回答:什麼是面向切面編程AOP? - Javdroider Hong的回答 - 知乎

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

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

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

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

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

Java中的BeanPostProcessor——連接Spring IOC和AOP的橋樑

具體的創建代理對象並返回的邏輯,在postProcessAfterInitialization方法中,大家自行欣賞。

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

BBP是連接IOC和AOP的橋樑。

總結

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

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

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



分享到:


相關文章: