再談Spring BeanPostProcessor

之前已經(jīng)就spring中的BeanPostProcessor使用方法以及其實(shí)現(xiàn)細(xì)節(jié)談?wù)撨^(guò),現(xiàn)在從更加宏觀、抽象的角度去理解spring的bpp具體是如何工作的,現(xiàn)在spring自身有多少bpp,如果我們有自定義的bpp需求,應(yīng)該如何實(shí)現(xiàn)。

其中如下demo的代碼位置 GitHub simple-spring

目錄

再談Spring BeanPostProcessor
1、BeanPostProcessor 種類
1.1、BeanPostProcessor 接口
1.2、InstantiationAwareBeanPostProcessor 接口
1.3、MergedBeanDefinitionPostProcessor 接口
2、源碼學(xué)習(xí)
2.1、實(shí)例化前的提前處理
2.2、實(shí)例化后的合并
2.3、實(shí)例化后的數(shù)據(jù)填充
2.4、初始化init方法
3、Demo

1、BeanPostProcessor 種類

image

如上圖,是在IDEA中使用control+H命令顯示出來(lái)的BeanPostProcessor的繼承實(shí)現(xiàn)類關(guān)系圖,重點(diǎn)介紹BeanPostProcessorInstantiationAwareBeanPostProcessorMergedBeanDefinitionPostProcessor三個(gè)接口,后面再繼續(xù)結(jié)合實(shí)際的實(shí)現(xiàn)類,分析spring是如何操作BeanPostProcessor對(duì)象的

閱讀這里面的源碼需要對(duì)Spring 源碼有一定的理解,具體的可看【目錄】Spring 源碼學(xué)習(xí)

1.1、BeanPostProcessor 接口

public interface BeanPostProcessor {
    Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
    // 在調(diào)用bean的init方法(非構(gòu)造方法)前調(diào)用
    // beanName 是在Spring IOC 容器的bean 名稱,返回的對(duì)象需要會(huì)被直接使用
    // 切記!!!默認(rèn)返回bean即可,不要無(wú)緣無(wú)故返回null,會(huì)出現(xiàn)各種NPE的情況
    Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
    // 在調(diào)用bean的init方法(非構(gòu)造方法)后調(diào)用
}

1.2、InstantiationAwareBeanPostProcessor 接口

public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {
    Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException;
    // 在對(duì)象實(shí)例化之前被調(diào)用,傳入的參數(shù)是類型以及bean的那么
    // 如果有返回非空的對(duì)象,則意味著不需要調(diào)用doCreate操作完成對(duì)象實(shí)例化等
    // 同時(shí)返回非空后會(huì)調(diào)用postProcessAfterInitialization方法
    // 注意看,不是postProcessAfterInstantiation而是postProcessAfterInitialization方法
    boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException;
     // 返回的是boolean類型,并不是和postProcessBeforeInstantiation配套使用的,在正常的實(shí)例化之后
     // 主要功能是判斷是否需要完成對(duì)象填充
     // 如果返回false,則意味著不會(huì)完成對(duì)象屬性的填充,例如@Resource導(dǎo)入的對(duì)象還是為null
    PropertyValues postProcessPropertyValues(
            PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException;
    // 提供給已經(jīng)實(shí)例化的對(duì)象一種可以自定義完成對(duì)象pv屬性的修改操作
    // 注意不要隨意返回null,一旦返回null,不會(huì)進(jìn)行對(duì)象填充、對(duì)象依賴等操作
}

1.3、MergedBeanDefinitionPostProcessor 接口

public interface MergedBeanDefinitionPostProcessor extends BeanPostProcessor {
    void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName);
    // 在完成bean的實(shí)例化之后,填充數(shù)據(jù)(populateBean)之前,可自定義的修改beanDefinition內(nèi)容
    // 名字成為merge,合并BPP對(duì)象和對(duì)于的beanDefinition的內(nèi)容
    // 實(shí)際上可以完成任何想任何實(shí)現(xiàn)的功能
}

以上的三個(gè)接口基本上能夠覆蓋所有的BPP特定的功能點(diǎn),從上面圖也可以看出來(lái),spring、springboot很多模塊都有相應(yīng)的對(duì)象實(shí)現(xiàn),完成特定的功能。

2、源碼學(xué)習(xí)

2.1、實(shí)例化前的提前處理

直接定位到AbstractAutowireCapableBeanFactory類的createBean方法,主要介紹的是InstantiationAwareBeanPostProcessor

try {
    Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
    if (bean != null) {
          // 如果bean有用,則會(huì)直接返回,不會(huì)再繼續(xù)執(zhí)行doCreateBean方法
        return bean;
    }
}
catch (Throwable ex) {
    throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
            "BeanPostProcessor before instantiation of bean failed", ex);
    // bean創(chuàng)建失敗,在初始化之前錯(cuò)誤了
}

protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
    Object bean = null;
    if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
        // Make sure bean class is actually resolved at this point.
        if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
              // 如果存在InstantiationAwareBeanPostProcessor對(duì)象
            Class<?> targetType = determineTargetType(beanName, mbd);
            if (targetType != null) {
                bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
                // 調(diào)用的是postProcessBeforeInstantiation方法
                if (bean != null) {
                      // 如果返回的數(shù)據(jù)不為null,則調(diào)用的是postProcessAfterInitialization方法
                      // 注意別看錯(cuò)了,一個(gè)是實(shí)例化Instantiation,一個(gè)是初始化Initialization
                    bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
                }
            }
        }
        mbd.beforeInstantiationResolved = (bean != null);
        // 記錄下是否通過(guò)提前批次的處理(如果bean不為null,則肯定是)
    }
    // 最后返回結(jié)果
    return bean;
}

其意思就是利用在實(shí)例化之前檢查是否存在合適的InstantiationAwareBeanPostProcessor對(duì)象,去攔截某些需要被處理的bean提前完成bean的實(shí)例化過(guò)程,不會(huì)去調(diào)用init方法,也沒(méi)有數(shù)據(jù)的填充,@Resource對(duì)象的引入等操作。

2.2、實(shí)例化后的合并

實(shí)例化后,僅僅是完成了對(duì)象最基礎(chǔ)的實(shí)例化工作,還未涉及到填充數(shù)據(jù),init方法執(zhí)行等操作,主要介紹的是MergedBeanDefinitionPostProcessor

synchronized (mbd.postProcessingLock) {
    if (!mbd.postProcessed) {
        try {
              // 合并管理bean
            applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
        }
        catch (Throwable ex) {
            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                    "Post-processing of merged bean definition failed", ex);
        }
        mbd.postProcessed = true;
    }
}

protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName) {
    for (BeanPostProcessor bp : getBeanPostProcessors()) {
        if (bp instanceof MergedBeanDefinitionPostProcessor) {
              // 遍歷所有的BeanPostProcessor
              // 篩選出屬于MergedBeanDefinitionPostProcessor的對(duì)象,調(diào)用postProcessMergedBeanDefinition
            MergedBeanDefinitionPostProcessor bdp = (MergedBeanDefinitionPostProcessor) bp;
            bdp.postProcessMergedBeanDefinition(mbd, beanType, beanName);
        }
    }
}

2.3、實(shí)例化后的數(shù)據(jù)填充

主要的操作在populateBean方法中,涉及到的也是InstantiationAwareBeanPostProcessor

再次強(qiáng)調(diào)InstantiationAwareBeanPostProcessor接口不僅僅是用在實(shí)例化前,在實(shí)例化之后也同樣有用

boolean continueWithPropertyPopulation = true;
// 設(shè)置可以繼續(xù)設(shè)置pv值的boolean對(duì)象為true
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
    for (BeanPostProcessor bp : getBeanPostProcessors()) {
        if (bp instanceof InstantiationAwareBeanPostProcessor) {
              // 如果是InstantiationAwareBeanPostProcessor對(duì)象
            InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
            if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
                  // 調(diào)用postProcessAfterInstantiation
                  // 如果返回false意味著不需要填充pv操作了,直接break
                continueWithPropertyPopulation = false;
                break;
            }
        }
    }
}

if (!continueWithPropertyPopulation) {
     // 上文設(shè)置為false后,直接返回,完全忽略后續(xù)的applyPropertyValues操作
    return;
}
if (hasInstAwareBpps || needsDepCheck) {
    PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
    if (hasInstAwareBpps) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof InstantiationAwareBeanPostProcessor) {
                InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
                // 對(duì)bean對(duì)象的pv值進(jìn)行處理操作
                if (pvs == null) {
                      // 如果返回為null,則直接返回
                      // 所以在自定義設(shè)置BPP的時(shí)候必須注意該方法的返回值
                      // 通過(guò)ide自動(dòng)生成的對(duì)象,其默認(rèn)返回值是null
                    return;
                }
            }
        }
    }
    if (needsDepCheck) {
         // 檢查對(duì)象的依賴問(wèn)題
        checkDependencies(beanName, mbd, filteredPds, pvs);
    }
}

2.4、初始化init方法

在spring中是可以設(shè)置對(duì)象的init方法,在對(duì)象實(shí)現(xiàn)之后,在initializeBean方法中,如下的代碼片段

Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
    wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    // 調(diào)用postProcessBeforeInitialization
}

try {
    invokeInitMethods(beanName, wrappedBean, mbd);
    // 動(dòng)態(tài)代理反射invoke調(diào)用初始化方法
    // 就是在這里獲取到bean的init方法調(diào)用信息,invoke調(diào)用
}
catch (Throwable ex) {
    throw new BeanCreationException(
            (mbd != null ? mbd.getResourceDescription() : null),
            beanName, "Invocation of init method failed", ex);
}

if (mbd == null || !mbd.isSynthetic()) {
    wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    // 唯一一個(gè)被調(diào)用了2次的地方的方法postProcessAfterInitialization
}
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
        throws BeansException {
    Object result = existingBean;
    for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
        result = beanProcessor.postProcessBeforeInitialization(result, beanName);
        if (result == null) {
            return result;
        }
    }
    return result;
}

public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
        throws BeansException {
    Object result = existingBean;
    for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
        result = beanProcessor.postProcessAfterInitialization(result, beanName);
        if (result == null) {
              // 都有個(gè)這樣的操作,返回為null,則退出
            return result;
        }
    }
    return result;
}

3、Demo

public class CustomInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {

    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        if (beanName.equals("student")) {
            Student student = new Student("bpp", 20);
            return student;
        }

        if (beanName.equals("superStudent")) {
            SuperStudent studentAndTeacher = new SuperStudent();
            studentAndTeacher.setName("zhangsan");
            return studentAndTeacher;
        }

        return null;
    }

    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        return true;
    }

    public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
        //return pvs;
        return null;
    }

    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}
public class SuperStudent {

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Resource
    private Teacher teacher;

    public void doInit() {
        System.out.println("superstudent init");
    }

    public void doSet() {
        if (teacher != null) {
            teacher.setAge(19);
            teacher.setName("superStudent");
            System.out.println(teacher.toString());
        } else {
            System.out.println("teacher is null");
        }
    }

    @Override
    public String toString() {
        return "SuperStudent{" +
                "name='" + name + '\'' +
                ", teacher=" + teacher +
                '}';
    }
}
public static void runBPP() {
    ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(
            new String[]{"context-bpp.xml"});
    SuperStudent student = (SuperStudent) applicationContext.getBean("superStudent");
    Teacher teacher = (Teacher) applicationContext.getBean("teacher");
    System.out.println(teacher.toString());
    System.out.println(student.toString());
    student.doSet();
    System.out.println(teacher.toString());
}

如下圖幾種樣例的代碼

  • 返回pv為null
    image

很清楚在SuperStudent的對(duì)象是null,并沒(méi)有完成填充操作,但是調(diào)用了init方法

  • 返回pv為原值,不作任何改變
    image

完成了填充操作,而且這個(gè)SuperStudent的teacher對(duì)象是和Spring IOC 容器內(nèi)的保持一致

  • 設(shè)置postProcessBeforeInstantiation
    image

SuperStudent返回輸出的對(duì)象,teacher沒(méi)有值,而且也沒(méi)有調(diào)用其init的方法

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,702評(píng)論 6 534
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,615評(píng)論 3 419
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,606評(píng)論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,044評(píng)論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,826評(píng)論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,227評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,307評(píng)論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,447評(píng)論 0 289
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,992評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,807評(píng)論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,001評(píng)論 1 370
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,550評(píng)論 5 361
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,243評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,667評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,930評(píng)論 1 287
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,709評(píng)論 3 393
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,996評(píng)論 2 374

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,785評(píng)論 18 139
  • 1.1 spring IoC容器和beans的簡(jiǎn)介 Spring 框架的最核心基礎(chǔ)的功能是IoC(控制反轉(zhuǎn))容器,...
    simoscode閱讀 6,741評(píng)論 2 22
  • 本來(lái)是準(zhǔn)備看一看Spring源碼的。然后在知乎上看到來(lái)一個(gè)帖子,說(shuō)有一群**自己連Spring官方文檔都沒(méi)有完全讀...
    此魚不得水閱讀 6,946評(píng)論 4 21
  • Spring Web MVC Spring Web MVC 是包含在 Spring 框架中的 Web 框架,建立于...
    Hsinwong閱讀 22,502評(píng)論 1 92
  • 幾年之前我家也曾經(jīng)養(yǎng)過(guò)一只狗,出生沒(méi)幾天就來(lái)我家,剛來(lái)的時(shí)候,還不是很會(huì)爬高,因?yàn)槲壹铱蛷d是有歩級(jí)的,它還不能爬上...
    惠芝閱讀 382評(píng)論 2 2