Spring注解驅(qū)動之BeanFactoryPostProcessor原理解析
概述
我們現(xiàn)在來學(xué)習(xí)一下Spring里面的一些擴展原理,希望大家通過這些原理的學(xué)習(xí),對Spring里面的運行機制,包括其內(nèi)部的工作原理,能有一個非常深刻的認識,為以后學(xué)習(xí)Spring里面的其他框架會有較大的幫助。
BeanFactoryPostProcessor的調(diào)用時機
BeanFactoryPostProcessor其實就是BeanFactory(創(chuàng)建bean的工廠)的后置處理器。
看到BeanFactoryPostProcessor會聯(lián)想到BeanPostProcessor,之前說過它是bean的后置處理器,并且是在bean創(chuàng)建對象初始化前后進行攔截工作的。

看完接口上的描述后,我們可以指定BeanFactoryPostProcessor的調(diào)用時機。意思是在IOC容器的BeanFactory標準初始化完成之后,修改IOC容器里面的BeanFactory。
什么是標準初始化么?后面描述是所有的bean定義已經(jīng)被加載了,但是還沒有bean被初始化。
總結(jié):BeanFactoryPostProcessor的調(diào)用時機是在BeanFactory標準化之后,我們可以定制、修改BeanFactory里面的一些內(nèi)容,此時,所有的bean定義已經(jīng)被加載到BeanFactory中了,但是bean的實例還沒創(chuàng)建。
案例實踐
首先編寫一個類實現(xiàn)BeanFactoryPostProcessor接口。
package com.meimeixia.ext;
import java.util.Arrays;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Component;
@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("MyBeanFactoryPostProcessor...postProcessBeanFactory..."); // 這個時候我們所有的bean還沒被創(chuàng)建
// 但是我們可以看一下通過Spring給我們傳過來的這個beanFactory,我們能拿到什么
int count = beanFactory.getBeanDefinitionCount(); // 我們能拿到有幾個bean定義
String[] names = beanFactory.getBeanDefinitionNames(); // 除此之外,我們還能拿到每一個bean定義的名字
System.out.println("當(dāng)前BeanFactory中有" + count + "個Bean");
System.out.println(Arrays.asList(names));
}
}
注意,我們自己編寫的MyBeanFactoryPostProcessor類要想讓Spring知道,并且還要能被使用起來,那么它一定就得被加到容器中,為此,我們可以在其上標注一個@Component注解。
然后創(chuàng)建一個配置類,例如ExtConfig,在該配置類上使用@ComponentScan注解來配置包掃描。
package com.meimeixia.ext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import com.meimeixia.bean.Blue;
@ComponentScan("com.meimeixia.ext")
@Configuration
public class ExtConfig {
@Bean
public Blue blue() {
return new Blue();
}
}
package com.meimeixia.bean;
public class Blue {
public Blue() {
System.out.println("blue...constructor");
}
public void init() {
System.out.println("blue...init...");
}
public void destory() {
System.out.println("blue...destory...");
}
}
編寫測試類
package com.meimeixia.test;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.meimeixia.ext.ExtConfig;
public class IOCTest_Ext {
@Test
public void test01() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ExtConfig.class);
// 關(guān)閉容器
applicationContext.close();
}
}
接下來測試下BeanFactoryPostProcessor的調(diào)用時機。


我們看到自己編寫的BeanFactoryPostProcessor在Blue類的無參構(gòu)造器創(chuàng)建Blue對象之前就已經(jīng)工作了。我們看看到Blue組件注冊到容器中的名字,只是此刻還沒有創(chuàng)建對象。
說明BeanFactoryPostProcessor是在所有的bean定義信息都被加載之后才調(diào)用的。
源碼分析

鼠標單擊Eclipse左上角方法調(diào)用棧中的IOCTest_Ext.test01() line:12,這時程序來到了IOCTest_Ext類的test01方法中,如下圖所示。

繼續(xù)跟進代碼,可以看到創(chuàng)建IOC容器時,最后還得刷新容器,如下圖所示。

繼續(xù)跟進代碼,可以看到在刷新容器的過程中,還得執(zhí)行在容器中注冊的BeanFactoryPostProcessor(BeanFactory的后置處理器)的方法。

那具體是怎么來執(zhí)行BeanFactoryPostProcessor的呢?我們繼續(xù)跟進代碼,發(fā)現(xiàn)又調(diào)用了一個invokeBeanFactoryPostProcessors方法,如下圖所示。


下面我們來仔細分析一下PostProcessorRegistrationDelegate類中的invokeBeanFactoryPostProcessors方法具體都做了哪些操作。

會發(fā)現(xiàn)其遍歷了所有的BeanFactoryPostProcessor組件,我們自己編寫的實現(xiàn)了BeanFactoryPostProcessor接口的MyBeanFactoryPostProcessor類肯定也屬于其中,所以會被遍歷到,然后便會執(zhí)行其postProcessBeanFactory方法。

小結(jié)
經(jīng)過源碼分析,我們可以得出這樣一個結(jié)論:首先從IOC容器中找到所有類型是BeanFactoryPostProcessor的組件,然后再來執(zhí)行它們其中的方法,而且是在初始化創(chuàng)建其他組件前面執(zhí)行。
為什么在初始化其他組件前面執(zhí)行的呢,之前我們分析AOP原理是,bean的初始化是放在finishBeanFactoryInitialization(beanFactory)方法執(zhí)行的。

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java中的System.arraycopy()淺復(fù)制方法詳解
這篇文章主要介紹了Java中的System.arraycopy()淺復(fù)制方法詳解,Java數(shù)組的復(fù)制操作可以分為深度復(fù)制和淺度復(fù)制,簡單來說深度復(fù)制,可以將對象的值和對象的內(nèi)容復(fù)制;淺復(fù)制是指對對象引用的復(fù)制,需要的朋友可以參考下2023-11-11
關(guān)于BufferedReader的read()和readLine()的區(qū)別
這篇文章主要介紹了關(guān)于BufferedReader的read()和readLine()的區(qū)別,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12
Mybatis自定義TypeHandler解決特殊類型轉(zhuǎn)換問題詳解
這篇文章主要介紹了Mybatis自定義TypeHandler解決特殊類型轉(zhuǎn)換問題詳解,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11
Intellij無法創(chuàng)建java文件解決方案
這篇文章主要介紹了Intellij無法創(chuàng)建java文件解決方案,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-10-10
Spring?Boot中application配置文件的生效順序及應(yīng)用范圍
Spring?Boot的一個重要特性就是它的自動配置,這一特性在很大程度上依賴于名稱為application的配置文件,本文將詳細介紹在Spring?Boot中,這些配置文件的加載順序以及每份文件的應(yīng)用范圍,需要的朋友可以參考下2024-03-03

