天天看點

beanfactorypostprocessor_Spring之BeanFactoryPostProcessor 和BeanPostProcessor

beanfactorypostprocessor_Spring之BeanFactoryPostProcessor 和BeanPostProcessor
概述

BeanFactoryPostProcessor 和 BeanPostProcessor 這兩個接口,都是 Spring 初始化 bean 時對外暴露的擴充點,一般叫做 Spring 的 Bean 後置處理器接口,作用是為 Bean 的初始化前後 提供可擴充的空間。兩個接口名稱看起來很相似,但作用和使用場景卻略有不同。

Spring 中 bean 的生命周期圖:

beanfactorypostprocessor_Spring之BeanFactoryPostProcessor 和BeanPostProcessor

由上圖可以看到,Spring 中的 BeanFactoryPostProcessor 在執行個體化之前被調用,而 BeanPostProcessor 則是在執行個體化過程中使用。

BeanFactoryPostProcessor

@FunctionalInterface
           

實作該接口,可以在 Spring 建立 bean 之前修改 bean 的定義屬性。也就是說,Srping 允許 BeanFactoryPostProcessor 在容器執行個體化 bean 之前讀取配置中繼資料,并可以根據需要進行修改。例如可以把 bean 的 Scope 從 singleton 改為 prototype ,也可以把 property 的值給修改掉。另外可以同時配置多個 BeanFactoryPostProcessor,并通過 order 屬性來控制 BeanFactoryPostProcessor 的執行順序 ( 在實作 BeanFactoryPostProcessor 時應該考慮實作 Ordered 接口 )。

BeanFactoryPostProcessor 是在 Spring 容器加載了定義 bean 的 XML 檔案之後,在 bean 執行個體化之前執行的。接口方法的入參是 ConfigurrableListableBeanFactory 類型,使用該參數可以擷取到相關的 bean 的定義資訊。

實際分析

XML 檔案配置

首先是 XML 檔案,

<?xml version="1.0" encoding="UTF-8"?>
           

這其中出現了變量:user.name,這是 spring 的分散配置,可以在另外的配置檔案中為 user.name 指定值,例如在 bean.properties 檔案中定義:

user.name = hresh
           

當通路名為 user 的 bean 時,其 name 屬性就會被字元串 hresh 替換,那 spring 架構是怎麼知道存在這樣的配置檔案呢,這個就是 PropertyPlaceholderConfigurer,需要在配置檔案中添加一下代碼:

<bean 
           

PropertyPlaceholderConfigurer

在這個 bean 中指定了配置檔案的位置。其實還是有個問題,這個 userHandler 隻不過是 spring 架構管理的一個 bean,并沒有被别的 bean 或者對象引用,spring 的beanFactory 是怎麼知道這個需要從這個 bean 中擷取配置資訊呢?我們看下 PropertyPlaceholderConfigurer 這個類的層次結構,如下圖:

beanfactorypostprocessor_Spring之BeanFactoryPostProcessor 和BeanPostProcessor

從上圖可以看到 PropertyPlaceholderConfigurer 間接的繼承了 BeanFactoryPostProcessor 接口,這是一個特别的接口,當 Spring 加載任何實作了該接口的 bean 的配置時,都會在 bean 工廠載入所有 bean 的配置之後執行 postProcessBeanFactory 方法。在 PropertyResourceConfigurer 類中實作了 postProcessBeanFactory 方法,該方法中先後調用了 mergeProperties、convertProperties、processProperties 這三個方法 ,先得到配置,将得到的配置轉換為合适的類型,最後将配置内容告知 BeanFactory。

正是通過實作 BeanFactoryPostProcessor 接口,BeanFactory 會在執行個體化任何 bean之前獲得配置資訊,進而能夠正确的解析 bean 描述檔案中的變量引用。

public 
           

關于 PropertyPlaceholderConfigurer 類的詳細解析,有興趣的朋友可以閱讀:【Spring源碼分析】.properties檔案讀取及占位符${...}替換源碼解析

自定義BeanFactoryPostProcessor

編寫實作了 BeanFactoryPostProcessor 接口的 MyBeanFactoryPostProcessor 的容器後處理器,如下代碼:

public 
           

然後在配置檔案中注冊這個 bean,如下:

<bean 
           

最後編寫測試代碼:

@Test
           

此時的執行結果為:

對容器進行處理後
           

再編寫一個實作了 BeanFactoryPostProcessor 接口的 UserFactoryPostProcessor 類,用來修改 bean 的屬性和 Scope。

public 
           

然後在配置檔案中注冊這個 bean,如下:

<bean 
           

調用測試代碼,結果為:

對容器進行處理後
           

如果要控制 BeanFactoryPostProcessor 的實作類的執行順序 ,可以這樣來修改代碼:

public 
           

再次調用測試代碼,輸出結果為:

調用UserFactoryPostProcessor的postProcessBeanFactory方法
           

從結果可以看出,通過實作 Ordered 接口,并重寫 getOrder 方法,UserFactoryPostProcessor 比 MyBeanFactoryPostProcessor 更早執行。

BeanPostProcessor

public 
           

BeanPostProcessor 可以在 spring 容器執行個體化 bean 之後,在執行 bean 的初始化方法前後,添加一些自己的處理邏輯。 這裡說的初始化方法,指的是以下兩種:

  1. bean 實作 了 InitializingBean 接口,對應的方法為 afterPropertiesSet 。
  2. 在 XML 檔案中定義 bean 的時候,<bean>标簽有個屬性叫做 init-method,來指定初始化方法。
注意:BeanPostProcessor 是在 spring 容器加載了 bean 的定義檔案并且執行個體化 bean 之後執行的。BeanPostProcessor 的執行順序是在 BeanFactoryPostProcessor 之後。

實戰分析

首先我們定義一個 bean 類:

public 
           

接着再定義一個 BeanFactoryPostProcessor 實作類:

public 
           

再定義一個 BeanPostProcessor 實作類:

public 
           

修改 XML 檔案:

<?xml version="1.0" encoding="UTF-8"?>
           

最後編寫測試代碼:

@Test
           

執行結果為:

調用PersonFactoryPostProcessor的postProcessBeanFactory方法
           
分析

從上述結果可以看出, BeanFactoryPostProcessor 在 bean 執行個體化之前被調用,注意在 PersonFactoryPostProcessor 的 postProcessBeanFactory 方法中隻是修改了 bean 的定義資訊,即将 age 值由18改為23,此時 bean 還未執行個體化。 之後執行個體化bean,在此過程中,先調用 BeanPostProcessor 實作類中的 postProcessBeforeInitialization 方法,然後調用實作了 InitializingBean接口的 bean 類中的 afterPropertiesSet 方法,如果設定的有 init-method 方法,則也會被調用,最後再調用 BeanPostProcessor 實作類中的 postProcessAfterInitialization 方法。

如果有多個 bean 類的情況呢,BeanFactoryPostProcessor 和 BeanPostProcessor 又是如何工作的?

複用上文中的 User 類和 UserFactoryPostProcessor,然後修改 PersonFactoryPostProcessor 。

public 
           

在 XML 檔案中增加對 User 類的定義

<bean 
           

修改測試代碼

@Test
           

執行結果為:

調用UserFactoryPostProcessor的postProcessBeanFactory方法
           

從結果中可以得知,BeanFactoryPostProcessor 的實作類都需要在 XML 檔案中進行配置。當你有多個 bean 類時,就需要實作多個 BeanFactoryPostProcessor ,然後在 XML 檔案中進行配置,當然你也可以隻寫在一個實作類當中,比如說在 PersonFactoryPostProcessor 類中這樣定義:

public 
           

這樣就不用在 XML 檔案中配置,但是這樣不利于代碼的維護。

反觀 BeanPostProcessor,隻需要定義一個實作類,如果需要對執行個體化的 bean 對象進行修改,可以讓 bean 類實作 InitializingBean 接口,然後編寫 afterPropertiesSet 方法,這樣遇到多個 bean 類的時候也比較友善處理。

總結

BeanFactoryPostProcessor 和 BeanPostProcessor 都是服務于 bean 的生命周期中的,隻是使用場景和作用略有不同。

BeanFactoryPostProcessor 作用于 bean 執行個體化之前,讀取配置中繼資料,并且可以修改;而 BeanPostProcessor 作用于 bean 的執行個體化過程中,然後可以改變 bean 執行個體(例如從配置中繼資料建立的對象)。

繼續閱讀