二、spring ioc之BeanPostProcessor解析

Spring的bean能夠高度擴展,BeanPostProcessor功不可沒,這個接口可以對bean實例做一些自定義修改,比如spring aop就是利用這個接口實現對bean的動態代理。

如何用

使用方法很簡單,實現BeanPostProcessor,然后將自定義的BeanPostProcessor聲明為一個bean就可以了。spring會自動掃描所有實現BeanPostProcessor的bean。

@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println(beanName + " MyBeanPostProcessor#MyBeanPostProcessor");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println(beanName + " MyBeanPostProcessor#postProcessAfterInitialization");
        return bean;
    }
}

BeanPostProcessor子接口

子接口內容摘抄自 https://fangjian0423.github.io/2017/06/20/spring-bean-post-processor/

BeanPostProcessor還有一些子接口的定義,分別實現擴展不同的功能,類圖如下所示:


image.png

InstantiationAwareBeanPostProcessor

InstantiationAwareBeanPostProcessor接口繼承自BeanPostProcessor接口。多出了3個方法:

//實例化之前調用
Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException;

//實例化之后調用
boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException;

//在這還可以改變屬性
PropertyValues postProcessPropertyValues(
    PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName)
    throws BeansException;
  1. InstantiationAwareBeanPostProcessor接口繼承BeanPostProcessor接口,它內部提供了3個方法,再加上BeanPostProcessor接口內部的2個方法,所以實現這個接口需要實現5個方法。InstantiationAwareBeanPostProcessor接口的主要作用在于目標對象的實例化過程中需要處理的事情,包括實例化對象的前后過程以及實例的屬性設置
  2. postProcessBeforeInstantiation方法是最先執行的方法,它在目標對象實例化之前調用,該方法的返回值類型是Object,我們可以返回任何類型的值。由于這個時候目標對象還未實例化,所以這個返回值可以用來代替原本該生成的目標對象的實例(比如代理對象)。如果該方法的返回值代替原本該生成的目標對象,后續只有postProcessAfterInitialization方法會調用,其它方法不再調用;否則按照正常的流程走
  3. postProcessAfterInstantiation方法在目標對象實例化之后調用,這個時候對象已經被實例化,但是該實例的屬性還未被設置,都是null。如果該方法返回false,會忽略屬性值的設置;如果返回true,會按照正常流程設置屬性值
  4. postProcessPropertyValues方法對屬性值進行修改(這個時候屬性值還未被設置,但是我們可以修改原本該設置進去的屬性值)。如果postProcessAfterInstantiation方法返回false,該方法不會被調用。可以在該方法內對屬性值進行修改
  5. 父接口BeanPostProcessor的2個方法postProcessBeforeInitialization和postProcessAfterInitialization都是在目標對象被實例化之后,并且屬性也被設置之后調用的
  6. Instantiation表示實例化,Initialization表示初始化。實例化的意思在對象還未生成,初始化的意思在對象已經生成

SmartInstantiationAwareBeanPostProcessor

SmartInstantiationAwareBeanPostProcessor接口繼承InstantiationAwareBeanPostProcessor接口。多出了3個方法:

// 預測Bean的類型,返回第一個預測成功的Class類型,如果不能預測返回null
Class<?> predictBeanType(Class<?> beanClass, String beanName) throws BeansException;
// 選擇合適的構造器,比如目標對象有多個構造器,在這里可以進行一些定制化,選擇合適的構造器
// beanClass參數表示目標實例的類型,beanName是目標實例在Spring容器中的name
// 返回值是個構造器數組,如果返回null,會執行下一個PostProcessor的determineCandidateConstructors方法;否則選取該PostProcessor選擇的構造器
Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName) throws BeansException;
// 獲得提前暴露的bean引用。主要用于解決循環引用的問題
// 只有單例對象才會調用此方法
Object getEarlyBeanReference(Object bean, String beanName) throws BeansException;
  1. SmartInstantiationAwareBeanPostProcessor接口繼承InstantiationAwareBeanPostProcessor接口,它內部提供了3個方法,再加上父接口的5個方法,所以實現這個接口需要實現8個方法。SmartInstantiationAwareBeanPostProcessor接口的主要作用也是在于目標對象的實例化過程中需要處理的事情。它是InstantiationAwareBeanPostProcessor接口的一個擴展。主要在Spring框架內部使用
  2. predictBeanType方法用于預測Bean的類型,返回第一個預測成功的Class類型,如果不能預測返回null。主要在于BeanDefinition無法確定Bean類型的時候調用該方法來確定類型
  3. determineCandidateConstructors方法用于選擇合適的構造器,比如類有多個構造器,可以實現這個方法選擇合適的構造器并用于實例化對象。該方法在postProcessBeforeInstantiation方法和postProcessAfterInstantiation方法之間調用,如果postProcessBeforeInstantiation方法返回了一個新的實例代替了原本該生成的實例,那么該方法會被忽略
  4. getEarlyBeanReference主要用于解決循環引用問題。比如ReferenceA實例內部有ReferenceB的引用,ReferenceB實例內部有ReferenceA的引用。首先先實例化ReferenceA,實例化完成之后提前把這個bean暴露在ObjectFactory中,然后populate屬性,這個時候發現需要ReferenceB。然后去實例化ReferenceB,在實例化ReferenceB的時候它需要ReferenceA的實例才能繼續,這個時候就會去ObjectFactory中找出了ReferenceA實例,ReferenceB順利實例化。ReferenceB實例化之后,ReferenceA的populate屬性過程也成功完成,注入了ReferenceB實例。提前把這個bean暴露在ObjectFactory中,這個ObjectFactory獲取的實例就是通過getEarlyBeanReference方法得到的

BeanPostProcessor

BeanPostProcessor接口是最頂層的接口,接口定義:

public interface BeanPostProcessor {
    // 初始化之前的操作
    Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
    // 初始化之后的操作
    Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}
  1. postProcessBeforeInitialization是指bean在初始化之前需要調用的方法
  2. postProcessAfterInitialization是指bean在初始化之后需要調用的方法
  3. postProcessBeforeInitialization和postProcessAfterInitialization方法被調用的時候。這個時候bean已經被實例化,并且所有該注入的屬性都已經被注入,是一個完整的bean
  4. 這2個方法的返回值可以是原先生成的實例bean,或者使用wrapper包裝這個實例

何時調用

在我們調用getBean()方法時(也可能是注入時),會調用AbstractAutowireCapableBeanFactory的creatBean()方法。在創建bean的過程中,會調用BeanPostProcessor。


image.png

在AbstractAutowireCapableBeanFactory類中,有三個方法分別是:applyBeanPostProcessorsBeforeInitialization、applyBeanPostProcessorsAfterInitialization、applyBeanPostProcessorsBeforeInstantiation,從名字就可以看出來是調用BeanPostProcessors的各種方法,我們分析一下applyBeanPostProcessorsBeforeInitialization。

@Override
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 List<BeanPostProcessor> getBeanPostProcessors() {
    return this.beanPostProcessors;
}

代碼邏輯并不復雜,遍歷所有的beanPostProcessors,然后執行postProcessBeforeInitialization方法,其他方法也是類似的執行方式。beanPostProcessors是類的成員變量,那么這個變量什么時候添加值進去的呢?

何時注冊

這個可以追溯到容器創建的時候,在AbstractApplicationContext的refresh()方法中,調用了registerBeanPostProcessors(beanFactory),這個方法實現邏輯是掃描所有實現了BeanPostProcessor的類型,并把它們注冊到beanPostProcessors中,代碼邏輯不難,可以自己去源碼中查看這個方法的實現邏輯。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,362評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,577評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,486評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,852評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,600評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,944評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,944評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,108評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,652評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,385評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,616評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,111評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,798評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,205評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,537評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,334評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,570評論 2 379

推薦閱讀更多精彩內容

  • 1.1 spring IoC容器和beans的簡介 Spring 框架的最核心基礎的功能是IoC(控制反轉)容器,...
    simoscode閱讀 6,745評論 2 22
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,828評論 18 139
  • 本來是準備看一看Spring源碼的。然后在知乎上看到來一個帖子,說有一群**自己連Spring官方文檔都沒有完全讀...
    此魚不得水閱讀 6,950評論 4 21
  • 文章作者:Tyan博客:noahsnail.com | CSDN | 簡書 3.8 Container Exten...
    SnailTyan閱讀 1,245評論 0 6
  • 上柜近兩年 整天蝸居在小柜臺里 透過那厚厚的玻璃 看盡世間百態 每天經手上百筆業務 碰到形形色色的人 有些的確友好...
    曦月L閱讀 393評論 0 0