這幾天正在複習Spring
的相關內容,在瞭解bean
的生命週期的時候,發現其中涉及到一個特殊的接口——BeanPostProcessor
接口。因爲網上沒有找到比較好的博客,全部最後花了好幾個小時,經過Spring
的官方文檔對它作了一個大體的瞭解,下面就來簡單介紹一下這個接口。html
有時候,咱們但願Spring
容器在建立bean
的過程當中,可以使用咱們本身定義的邏輯,對建立的bean
作一些處理,或者執行一些業務。而實現方式有多種,好比自定義bean
的初始化話方法等,而BeanPostProcessor
接口也是用來實現相似的功能的。java
若是咱們但願容器中建立的每個單例bean
,在建立的過程當中能夠執行一些自定義的邏輯,那麼咱們就能夠編寫一個類,並讓他實現BeanPostProcessor
接口,而後將這個類註冊到一個容器中。容器在建立bean
的過程當中,會優先建立實現了BeanPostProcessor
接口的bean
,而後,在建立其餘bean
的時候,會將建立的每個bean
做爲參數,調用BeanPostProcessor
的方法。而BeanPostProcessor
接口的方法,便是由咱們本身實現的。下面就來具體介紹一下BeanPostProcessor
的使用spring
咱們先看一看BeanPostProcessor
接口的代碼:app
public interface BeanPostProcessor { // 注意這個方法名稱關鍵的是before這個單詞 Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException; // 注意這個方法名稱關鍵的是after這個單詞 Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException; }
能夠看到,BeanPostProcessor
接口只有兩個抽象方法,由實現這個接口的類去實現(後面簡稱這兩個方法爲before
和after
),這兩個方法有着相同的參數:less
bean
的引用;bean
的名稱; 那這兩個方法什麼時候執行呢?這就涉及到Spring
中,bean
的生命週期了。下面引用《Spring實戰》
中的一張圖,這張圖表現了bean
的生命週期,而Spring
容器建立bean
的具體過程,請參考這篇博客——簡單談談Spring的IoC。ide
上圖中標紅的兩個地方就是BeanPostProcessor
中兩個方法的執行時機。Spring
容器在建立bean
時,若是容器中包含了BeanPostProcessor
的實現類對象,那麼就會執行這個類的這兩個方法,並將當前正在建立的bean
的引用以及名稱做爲參數傳遞進方法中。這也就是說,BeanPostProcessor
的做用域是當前容器中的全部bean
(不包括一些特殊的bean
,這個後面說)。post
值得注意的是,咱們能夠在一個容器中註冊多個不一樣的BeanPostProcessor
的實現類對象,而bean
在建立的過程當中,將會輪流執行這些對象實現的before
和after
方法。那執行順序如何肯定呢?Spring
提供了一個接口Ordered
,咱們可讓BeanPostProcessor
的實現類實現這個Ordered
接口,並實現接口的getOrder
方法。這個方法的返回值是一個int
類型,Spring
容器會經過這個方法的返回值,對容器中的多個BeanPostProcessor
對象進行從小到大排序,而後在建立bean
時依次執行它們的方法。也就是說,getOrder
方法返回值越小的BeanPostProcessor
對象,它的方法將越先被執行。測試
下面就來寫一個簡單的demo
,來看看BeanPostProcessor
的效果。首先定義兩個普通的bean
,就叫User
和Car
吧:this
public class User { private String name; private int age; // ... 省略getter和setter... } public class Car { private int speed; private double price; // ... 省略getter和setter... }
在定義一個BeanPostProcessor
的實現類,重寫接口的方法:lua
public class PostBean implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { // 輸出信息,方便咱們看效果 System.out.println("before -- " + beanName); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { // 輸出信息,方便咱們看效果 System.out.println("after -- " + beanName); return bean; } }
咱們直接使用一個Java
類做爲Spring
的配置,就不使用xml
配置文件了。配置以下,在這個配置類中,聲明瞭User
、Car
以及PostBean
這三個bean
的工廠方法,前兩個是普通bean
,而PostBean
是實現BeanPostProcessor
的bean
:
@Configuration public class BeanConfig { // 在Spring中註冊User這個bean @Bean public User user() { return new User(); } // 在Spring中註冊Car這個bean @Bean public Car car() { return new Car(); } // 在Spring中註冊PostBean這個bean,這個bean實現了BeanPostProcessor接口 @Bean public PostBean postBean() { return new PostBean(); } }
好,有了上面四個類,就能夠開始測試了,下面是測試方法:
@Test public void testConfig() { ApplicationContext context = new AnnotationConfigApplicationContext(BeanConfig.class); }
上面這個方法啥也不幹,就是建立一個Spring
的上下文對象,也就是Spring
的IoC
容器。這個容器將去加載BeanConfig
這個類的配置,而後建立配置類中聲明的對象。在建立User和Car的過程當中,就會執行BeanPostProcessor
實現類的方法。咱們看看執行結果:
before -- org.springframework.context.event.internalEventListenerProcessor after -- org.springframework.context.event.internalEventListenerProcessor before -- org.springframework.context.event.internalEventListenerFactory after -- org.springframework.context.event.internalEventListenerFactory before -- car after -- car before -- user after -- user
能夠看到,BeanPostProcessor
的before
方法和after
方法都被調用了四次,最後兩次調用時,傳入的參數正是咱們本身定義的Bean
——User
和Car
。那爲何調用了四次呢,明明咱們只定義了兩個普通bean
。咱們看上面的輸出發現,前兩次調用,傳入的bean
是Spring
內部的組件。Spring
在初始化容器的過程當中,會建立一些本身定義的bean
用來實現一些功能,而這些bean
,也會執行咱們註冊進容器中的BeanPostProcessor
實現類的方法。
BeanPostProcessor
這個接口,在使用的過程當中,其實還有許多的限制和坑點,若不瞭解的話,可能會讓你對某些結果感到莫名其妙。下面我就來簡單地說一說:
(一)BeanPostProcessor依賴的bean,不會執行BeanPostProcessor的方法
當咱們在BeanPostProcessor
的實現類中,依賴了其餘的bean
,那麼被依賴的bean
被建立時,將不會執行它所在的BeanPostProcessor
實現類實現的方法,好比咱們修改PostBean
的實現,以下所示:
@Component public class PostBean implements BeanPostProcessor, Ordered { // 讓PostBean依賴User @Autowired private User user; @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; } }
此時,容器在建立User
這個bean
時,不會執行PostBean
實現的兩個方法,由於因爲PostBean
依賴於user
,因此user
須要在PostBean
以前建立完成,這也就意味着在user
建立時,PostBean
還未初始化完成,因此不會調用它的方法。
(二)BeanPostProcessor以及依賴的bean沒法使用AOP
如下是Spring
官方文檔中的一段話:
Because AOP auto-proxying is implemented as a
BeanPostProcessor
itself, neitherBeanPostProcessor
s nor the beans they reference directly are eligible for auto-proxying, and thus do not have aspects woven into them.
上面這段話的意思大體是說,Spring
的AOP
代理就是做爲BeanPostProcessor
實現的,因此咱們沒法對BeanPostProcessor的實現類使用AOP織入通知,也沒法對BeanPostProcessor的實現類依賴的bean使用AOP織入通知。Spring
的AOP
實現我暫時尚未研究過,因此上面的說AOP
做爲BeanPostProcessor
實現的意思我不是特別明白,可是咱們如今只須要關注BeanPostProcessor
以及它依賴的bean
都沒法使用AOP
這一點。爲了驗證上面的說法,我稍微修改一下2.3
中的例子,來測試一波。
首先,咱們修改2.3
中用到的PostBean
和User
這兩個類,讓PostBean
依賴User
這個類,同時爲了輸出更加地簡單,咱們將before
和after
方法中的println
語句刪了:
@Component public class PostBean implements BeanPostProcessor, Ordered { // 讓PostBean依賴User @Autowired private User user; @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; } // 此方法用來測試AOP,做爲切點 public void testAOP() { System.out.println("Post Bean"); } } @Component public class User { private String name; private int age; // ... 省略getter和setter... // 此方法用來測試AOP,用做切點 public void testAOP() { System.out.println("user bean"); } }
而後,咱們定義一個AOP
的切面,在切面中將PostBean
的testAOP
方法做爲切點,代碼以下:
@Aspect public class BeanPostProcessorAspect { // 此方法織入PostBean的testAOP方法 @Before("execution(* cn.tewuyiang.pojo.PostBean.testAOP(..))") public void before() { System.out.println("before1"); } // 此方法織入User的testAOP方法 @Before("execution(* cn.tewuyiang.pojo.User.testAOP(..))") public void before2() { System.out.println("before2"); } }
好,這就準備完畢,能夠開始測試了。咱們此次使用Spring
註解掃描來配置bean
以及爲bean
注入依賴,測試代碼以下:
@Test public void testConfig() { ApplicationContext context = new AnnotationConfigApplicationContext(AutoConfig.class); // 獲取User這個bean,執行測試AOP的方法 User user = context.getBean(User.class); user.testAOP(); // 獲取PostBean這個bean,執行測試AOP的方法 PostBean bean = context.getBean(PostBean.class); bean.testAOP(); } 輸出以下: user bean post Bean
從輸出中能夠看到,使用AOP
織入的前置通知沒有執行,這也就驗證了上面所說的,BeanPostProcessor
的實現類以及實現類依賴的bean
,沒法使用AOP
爲其織入通知。可是這個限制具體有到什麼程度,我也不是很肯定,由於我使用xml
配置依賴,以及上面使用註解掃描兩種方式,AOP
織入都無法使用,可是我在使用@Bean
這種配置方式時,被依賴的bean
卻成功執行了通知。因此,關於此處提到的限制,還須要深刻了解Spring
容器的源碼實現才能下定論。
(三)註冊BeanPostProcessor的方式以及限制
咱們如何將BeanPostProcessor
註冊到Spring
容器中?方式主要有兩種,第一種就是上面一直在用的,將其聲明在Spring
的配置類或xml
文件中,做爲普通的bean
,讓ApplicationContext
對象去加載它,這樣它就被自動註冊到容器中了。並且Spring
容器會對BeanPostProcessor
的實現類作特殊處理,即會將它們挑選出來,在加載其餘bean
前,優先加載BeanPostProcessor
的實現類。
還有另一種方式就是使用ConfigurableBeanFactory
接口的addBeanPostProcessor
方法手動添加,ApplicationContext
對象中組合了一個ConfigurableBeanFactory
的實現類對象。可是這種方式添加BeanPostProcessor
有一些缺點。首先,咱們一建立Spring
容器,在配置文件中配置的單例bean
就會被加載,此時addBeanPostProcessor
方法尚未執行,那咱們手動添加的BeanPostProcessor
也就沒法做用於這些bean
了,因此手動添加的BeanPostProcessor
只能做用於那些延遲加載的bean
,或者非單例bean
。
還有一個就是,使用addBeanPostProcessor方式添加的BeanPostProcessor,Ordered接口的做用將失效,而是以註冊的順序執行。咱們前面提過,Ordered
接口用來指定多個BeanPostProcessor
實現的方法的執行順序。這是Spring
官方文檔中提到的:
While the recommended approach for
BeanPostProcessor
registration is throughApplicationContext
auto-detection (as described above), it is also possible to register them programmatically against aConfigurableBeanFactory
using theaddBeanPostProcessor
method. This can be useful when needing to evaluate conditional logic before registration, or even for copying bean post processors across contexts in a hierarchy. Note however thatBeanPostProcessor
s added programmatically do not respect theOrdered
interface. Here it is the order of registration that dictates the order of execution. Note also thatBeanPostProcessor
s registered programmatically are always processed before those registered through auto-detection, regardless of any explicit ordering.
(四)使用@Bean配置BeanPostProcessor的限制
若是咱們使用Java
類的方式配置Spring
,並使用@Bean
聲明一個工廠方法返回bean
實例,那麼返回值的類型必須是BeanPostProcessor
類型,或者等級低於BeanPostProcessor
的類型。這裏很差口頭描述,直接看代碼吧。如下是一個BeanPostProcessor
的實現類,它實現了多個接口:
/** * 此BeanPostProcessor的實現類,還實現了Ordered接口 */ public class PostBean implements BeanPostProcessor, Ordered { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("before -- " + beanName); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("after -- " + beanName); return bean; } @Override public int getOrder() { return 0; } }
咱們在配置類中,聲明PostBean
能夠有如下幾種方式:
@Configuration public class BeanConfig { // 方式1:PostBean @Bean public PostBean postBean() { return new PostBean(); } // 方式2:返回值爲BeanPostProcessor @Bean public BeanPostProcessor postBean() { return new PostBean(); } // 方式3:返回值爲Ordered @Bean public Ordered postBean() { return new PostBean(); } }
以上三種方式均可以讓Spring
容器建立PostBean
實例對象,由於PostBean
實現了BeanPostProcessor
和Ordered
接口,因此它也是這兩種類型的對象。可是須要注意,上面三種方式中,只有第一種和第二種方式,會讓Spring
容器將PostBean
看成BeanPostProcessor
處理;而第三種方式,則會被看成一個普通Bean
處理,實現BeanPostProcessor
的兩個方法都不會被調用。由於在PostBean
的繼承體系中,Ordered
和BeanPostProcessor
是同級別的,Spring
沒法識別出這個Ordered
對象,也是一個BeanPostProcessor
對象;可是使用PostBean
卻能夠,由於PostBean
類型就是BeanPostProcessor
的子類型。因此,在使用@Bean聲明工廠方法返回BeanPostProcessor實現類對象時,返回值必須是BeanPostProcessor類型,或者更低級的類型。Spring
官方文檔中,這一部分的內容以下:
Note that when declaring a
BeanPostProcessor
using an@Bean
factory method on a configuration class, the return type of the factory method should be the implementation class itself or at least theorg.springframework.beans.factory.config.BeanPostProcessor
interface, clearly indicating the post-processor nature of that bean. Otherwise, theApplicationContext
won’t be able to autodetect it by type before fully creating it. Since aBeanPostProcessor
needs to be instantiated early in order to apply to the initialization of other beans in the context, this early type detection is critical.
以上就對BeanPostProcessor
的功能、使用以及須要注意的問題作了一個大體的介紹。須要注意的是,上面所提到的問題,可能根據不一樣的狀況,會有不一樣的結果,由於文檔中的資料只是簡單地提了幾句,並不詳細,上面的內容大部分都是我基於官方文檔的描述,以及本身的測試得出,因此可能並不許確。還須要本身在實踐中去嘗試,或者閱讀源碼,才能完全瞭解BeanPostProcessor
的執行機制。
以上描述若存在錯誤或不足,但願可以提出來,由於這一部份內容,我也不太瞭解,因此但願有人幫忙指正。