Spring中bean工廠后置處理器(BeanFactoryPostProcessor)使用

Spring中bean工廠后置處理器也就是BeanFactoryPostProcessor接口主届,是用來(lái)干什么的呢厂抽?我們都知道一個(gè)好的框架必備的特性至少得有開(kāi)閉原則羡蛾,可擴(kuò)展性评架。
在前面的文章里SpringIOC源碼閱讀—BeanDefinitionDocumentReader我們了解到Spring對(duì)bean定義的載入有很多種方式眷茁,讀取的過(guò)程是可插拔的,不論何種形式纵诞,spring的IOC容器只要獲得了bean定義信息上祈,都可以正常工作。而我們熟知的配置讀取方式就是XML文件,如果你希望登刺,可以自己定制配置信息的讀取過(guò)程籽腕,這就是Spring的特性體現(xiàn)之一。
同樣BeanFactoryPostProcessor也是Spring可擴(kuò)展性的一個(gè)體現(xiàn)纸俭,我們讀一下這個(gè)接口的源碼

public interface BeanFactoryPostProcessor {

    /**
     * Modify the application context's internal bean factory after its standard
     * initialization. All bean definitions will have been loaded, but no beans
     * will have been instantiated yet. This allows for overriding or adding
     * properties even to eager-initializing beans.
     * @param beanFactory the bean factory used by the application context
     * @throws org.springframework.beans.BeansException in case of errors
     */
    void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;

}

注釋的意思是允許我們?cè)诠S里所有的bean被加載進(jìn)來(lái)后但是還沒(méi)初始化前节仿,對(duì)所有bean的屬性進(jìn)行修改也可以add屬性值。
下面通過(guò)實(shí)例看下效果

beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<!-- 普通的bean -->
 <bean id="user" init-method="init" class="xz.quartz.analysis.User"></bean>
 <!-- BeanFactroy后置處理器 -->
 <bean id="beanfactorypostpro" class="xz.quartz.analysis.beanfactorypostpro"></bean>
</beans>

我們定義一個(gè)普通bean和一個(gè)BeanFactroy后置處理器

User類(lèi)

package xz.quartz.analysis;

public class User {

    String name;
    String age;
    
    public User() {
        System.out.println("construtor");
        System.out.println("name:"+name+",age:"+age);
    }
    
    public void go(){
        System.out.println("age:"+age);
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getAge() {
        return age;
    }
    public void setAge(String age) {
        this.age = age;
    }
}

BeanFactroy后置處理器類(lèi)

package xz.quartz.analysis;

import org.springframework.beans.BeansException;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;

public class beanfactorypostpro implements BeanFactoryPostProcessor{

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        System.out.println("******調(diào)用BeanFactoryPostProcessor開(kāi)始");
        //獲取到Spring中所有的beanName
        String[] beanStr = beanFactory.getBeanDefinitionNames();
        //循環(huán)bean做出自定義的操作
        for (String beanName : beanStr) {
            System.out.println("bean name:"+beanName);
            if ("user".equals(beanName)) {
                BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
                System.out.println("修改user的age值");
                beanDefinition.getPropertyValues().add("age", "20");
            }
        }
        System.out.println("******調(diào)用BeanFactoryPostProcessor結(jié)束");
    }
}

在上面BeanFactoryPostProcessor的實(shí)現(xiàn)類(lèi)里通過(guò)我寫(xiě)的注釋可以看到這個(gè)工廠后置器獲取到所有的beanName掉蔬,并且可以對(duì)特定的bean或者全部bean進(jìn)行操作廊宪,這也就是后置器的核心所在之處。

main方法

public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:beans.xml");
        System.out.println("獲取到User實(shí)例 ");
        ((User)applicationContext.getBean("user")).go();
    }

主函數(shù)中第一行初始化的時(shí)候女轿,讀取beans.xml時(shí)箭启,會(huì)獲取實(shí)現(xiàn)了BeanFactoryPostProcessor接口的bean,然后把它作為后置處理器類(lèi)來(lái)處理蛉迹。
然后我們通過(guò)調(diào)用user的go方法傅寡,輸出age的值來(lái)看下

******調(diào)用BeanFactoryPostProcessor開(kāi)始
bean name:user
修改user的age值
bean name:beanfactorypostpro
******調(diào)用BeanFactoryPostProcessor結(jié)束
construtor
name:null,age:null
獲取到User實(shí)例 
age:20

我們?cè)贐eanFactroy后置處理器類(lèi)里給user的age屬性成功賦值成20,大家通過(guò)程序?qū)嵗梢钥吹胶笾锰幚砥鞯淖饔煤陀梅恕?/p>

在這里提出兩個(gè)疑問(wèn)

1.后置處理器和bean指定的init-method還有構(gòu)造方法的順序是誰(shuí)先誰(shuí)后呢北救?
2.這三個(gè)方法同時(shí)對(duì)屬性賦值荐操,最終會(huì)是哪個(gè)起作用呢?

對(duì)上面的兩個(gè)問(wèn)題珍策,我們改造下代碼托启,先把構(gòu)造方法加上賦值過(guò)程

User類(lèi)

package xz.quartz.analysis;

public class User {
    String name;
    String age;
    
    void init(){
        System.out.println("init");
    }
    
    public User() {
        System.out.println("construtor");
        name = "zx-construtor";
        age = "zx-construtor";
        System.out.println("name:"+name+",age:"+age);
    }
    
    public void go(){
        System.out.println("最終age的值:"+age);
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getAge() {
        return age;
    }
    public void setAge(String age) {
        this.age = age;
    }
}

輸出結(jié)果:

******調(diào)用BeanFactoryPostProcessor開(kāi)始
bean name:user
修改user的age值
bean name:beanfactorypostpro
******調(diào)用BeanFactoryPostProcessor結(jié)束
construtor
name:zx-construtor,age:zx-construtor
init
name:zx-construtor,age:20
獲取到User實(shí)例 
最終age的值:20

可以看到程序先執(zhí)行后置器將值改為20然后執(zhí)行的構(gòu)造方法攘宙,改為zx-construtor屯耸,但是最終age的值為20到這里疑問(wèn)就來(lái)了蹭劈,為什么后執(zhí)行的賦值沒(méi)起作用呢疗绣?這個(gè)我后面解讀源碼的時(shí)候再聊。

我們繼續(xù)把init-method方法加上

package xz.quartz.analysis;

public class User {
    String name;
    String age;
    
    void init(){
        System.out.println("init");
        name = "zx-init";
        age = "zx-init";
        System.out.println("name:"+name+",age:"+age);
    }
    
    public User() {
        System.out.println("construtor");
        name = "zx-construtor";
        age = "zx-construtor";
        System.out.println("name:"+name+",age:"+age);
    }
    
    public void go(){
        System.out.println("最終age的值:"+age);
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getAge() {
        return age;
    }
    public void setAge(String age) {
        this.age = age;
    }
}

看輸出結(jié)果

******調(diào)用BeanFactoryPostProcessor開(kāi)始
bean name:user
修改user的age值
bean name:beanfactorypostpro
******調(diào)用BeanFactoryPostProcessor結(jié)束
construtor
name:zx-construtor,age:zx-construtor
init
name:zx-init,age:zx-init
獲取到User實(shí)例 
最終age的值:zx-init

通過(guò)上面兩個(gè)例子我們可以知道這三個(gè)方法的執(zhí)行順序

bean工廠后置處理器—>構(gòu)造方法—>init-method铺韧。

而賦值結(jié)果的疑問(wèn)后續(xù)通過(guò)源碼來(lái)解釋吧多矮!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市哈打,隨后出現(xiàn)的幾起案子塔逃,更是在濱河造成了極大的恐慌,老刑警劉巖前酿,帶你破解...
    沈念sama閱讀 207,248評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件患雏,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡罢维,警方通過(guò)查閱死者的電腦和手機(jī)淹仑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門(mén)丙挽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人匀借,你說(shuō)我怎么就攤上這事颜阐。” “怎么了吓肋?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,443評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵凳怨,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我是鬼,道長(zhǎng)肤舞,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,475評(píng)論 1 279
  • 正文 為了忘掉前任均蜜,我火速辦了婚禮李剖,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘囤耳。我一直安慰自己篙顺,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布充择。 她就那樣靜靜地躺著德玫,像睡著了一般。 火紅的嫁衣襯著肌膚如雪椎麦。 梳的紋絲不亂的頭發(fā)上宰僧,一...
    開(kāi)封第一講書(shū)人閱讀 49,185評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音铃剔,去河邊找鬼撒桨。 笑死查刻,一個(gè)胖子當(dāng)著我的面吹牛键兜,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播穗泵,決...
    沈念sama閱讀 38,451評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼普气,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了佃延?” 一聲冷哼從身側(cè)響起现诀,我...
    開(kāi)封第一講書(shū)人閱讀 37,112評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎履肃,沒(méi)想到半個(gè)月后仔沿,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,609評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡尺棋,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評(píng)論 2 325
  • 正文 我和宋清朗相戀三年封锉,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,163評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡成福,死狀恐怖碾局,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情奴艾,我是刑警寧澤净当,帶...
    沈念sama閱讀 33,803評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站蕴潦,受9級(jí)特大地震影響像啼,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜潭苞,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評(píng)論 3 307
  • 文/蒙蒙 一埋合、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧萄传,春花似錦甚颂、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,357評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至衍菱,卻和暖如春赶么,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背脊串。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,590評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工辫呻, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人琼锋。 一個(gè)月前我還...
    沈念sama閱讀 45,636評(píng)論 2 355
  • 正文 我出身青樓放闺,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親缕坎。 傳聞我的和親對(duì)象是個(gè)殘疾皇子怖侦,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評(píng)論 2 344