Spring 的 BeanPostProcessor 原理剖析及使用


一差购、接口介紹

????????spring提供了一個接口類 BeanPostProcessor,我們稱其為后置處理器汉嗽,作用是在 bean 的實例化的過程中對 bean 進行自定義的包裝處理欲逃,其提供了兩個方法。先看看?BeanPostProcessor 的定義饼暑。

public interface BeanPostProcessor{?

? ??????public abstract Object postProcessBeforeInitialization(Object obj, String s) throws BeansException;?

? ? ? ? ??public abstract Object postProcessAfterInitialization(Object obj, String s) throws BeansException;??

}

? ? ? ? 根據(jù)類的名稱稳析,我們可以猜測兩個接口方法執(zhí)行的位置:

? ??????1、在bean初始化之前執(zhí)行弓叛;

   2彰居、在bean的初始化之后執(zhí)行。

二撰筷、源碼解析

? ? ? ? 理解其原理以及執(zhí)行的位置陈惰,需要到源碼中尋找答案。跟蹤創(chuàng)建 bean 的實例的 getBean 方法毕籽,層層跟進抬闯,在AbstractAutowireCapableBeanFactory 類中井辆,找到了執(zhí)行方法:

protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {

????????if (System.getSecurityManager() != null) {?

? ??????????????AccessController.doPrivileged(new PrivilegedAction<Object>() {?

? ??????????????????????@Override public?

? ??????????????????????Object run() {?

? ???????????????????????????????invokeAwareMethods(beanName, bean);?

? ??????????????????????????????return null;?

? ??????????????????????}?

? ??????????????}, getAccessControlContext());? ? ? ?

? ? ? ?} else {

? ???????????invokeAwareMethods(beanName, bean);?

? ??????}??

? ??????Object wrappedBean = bean;

? ??????if (mbd == null || !mbd.isSynthetic()) {?

? ??????????????wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);?

? ? ? ? }?

? ? ? ? try {? ? ? ? ??

? ??????????????invokeInitMethods(beanName, wrappedBean, mbd);

? ? ? ? ?} 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);? ??

? ??????}? ? ??

? ??????return wrappedBean;

}? ? ? ?

? ? ? ? 如上加粗部分是主要的代碼,invokeAwareMethods(beanName, bean)画髓、?applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);invokeInitMethods(beanName, wrappedBean, mbd)掘剪、invokeInitMethods(beanName, wrappedBean, mbd)、applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName)奈虾。

? ? ? ? 1夺谁、invokeAwareMethods(beanName, bean),對實現(xiàn)了 aware 接口的 bean 進行特殊的處理肉微,實現(xiàn)aware 接口的 bean 在被初始化后匾鸥,可以取得一些相對應的資源。比如碉纳, applicationAware , beanFactoryAware 等勿负。

? ? ? ? 2applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName)劳曹,后置處理器初始化前方法調(diào)用奴愉。

? ? ? ? 3、invokeInitMethods(beanName, wrappedBean, mbd)铁孵,用于用戶初始化方法的調(diào)用锭硼,如實現(xiàn)了?InitializingBean 接口的 bean ,調(diào)用其?afterPropertiesSet 和 用戶配置的 init-method 方法的調(diào)用蜕劝。

? ? ? ? 進入applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName)和applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)檀头,查看其源碼:

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) {

? ? ? ? ? ? ? ? return result;

? ? ? ? ? ? }

? ? ? ? }

? ? ? ? return result;

}

三、使用

1岖沛、定義接口和實例?

publi interface DemoService {

? ? public?void sayHello();

}

public interface NameInit {?

? ? ?public void setName(String name);??

}

public class DemoServiceImpl implements DemoService,NameInit {

? ??????String name;?

? ??????@Override?

? ? ? ??public void sayHello() {?

? ??????????????System.out.println("hello "+name);?

? ? ? ??}? ? ?

? ??????@Override? ? ??

? ? ? ??public void setName(String name) {??

? ??????????????this.name=name;? ??

? ? ? ? }

}

2暑始、定義bean的配置

<bean id="demoService" class="com.zjl.beanpostprocessor.DemoServiceImpl"> </bean>

3、定義一個BeanPostProcessor?實例

? ??????凡是繼承了NameInit的接口婴削,均實例化廊镜,注入name值。此處定義接口一方面是要使用接口中提供的setName方法唉俗,另一方面減輕系統(tǒng)壓力嗤朴,防止每個bean都進行注入。

public class NameBeanPostProcessor implements BeanPostProcessor {?

? ??????String name;

????????public String getName() {?

? ??????????????return name;

? ???????}?

? ??????public void setName(String name) {?

? ??????????????this.name = name;?

? ??????}?

? ??????@Override? ? ? ? ?

? ??????public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {?

? ??????????????if(bean instanceof NameInit){?

? ??????????????????((NameInit)bean).setName(name);

? ? ? ? ? ? ? ? ? }??

? ??????????????return bean;?

? ??????}? ? ??

? ??????@Override?

? ??????public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {??

? ??????????????return bean;? ??

? ??????}

?}??

5互躬、定義bean播赁,注入name的值

<bean id="nameBeanPostProcessor" class="com.zjl.beanpostprocessor.NameBeanPostProcessor">

? ??????<property name="name" value="zhangsan"></property>??

</bean>

6颂郎、定義另一個BeanPostProcessor 吼渡,僅打印日志

public class LogBeanPostProcessor implements BeanPostProcessor {?

? ??????@Override? ? ? ? ? ? ? ? ? ??

? ? ? ? ?public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {?

? ??????????????System.out.println("正在處理"+beanName); return bean;??

? ??????}?

? ??????@Override? ? ??

? ??????public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {?

? ??????????????System.out.println("已經(jīng)處理完成"+beanName);? ?

? ???????????????return bean;

?????????}? ?

?}?

7、定義bean

<bean id="logBeanPostProcessor" class="com.zjl.beanpostprocessor.LogBeanPostProcessor"> </bean>

8乓序、測試類

public class BeanPostProcessorTest {?

? ??????public static void main(String[] args) {?

? ??????????????ApplicationContext context=new FileSystemXmlApplicationContext("beanpostprocessor.xml");?

? ??????????????DemoService demoService=(DemoService) context.getBean("demoService");?

? ??????????????demoService.sayHello();

?????????}? ? ? ?

}

9寺酪、測試結(jié)果

正在處理demoService?

已經(jīng)處理完成demoService?

hello zhangsan?

10坎背、總結(jié)

  兩個方法均在bean實例化期間已經(jīng)完成;

 ????name屬性是根據(jù)NameInit接口自動注入寄雀;

  由于兩個方法執(zhí)行的時間特殊性得滤,所以打印日志和記錄時間意義不大,主要還是用于注入屬性和完善配置盒犹。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末懂更,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子急膀,更是在濱河造成了極大的恐慌沮协,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件卓嫂,死亡現(xiàn)場離奇詭異慷暂,居然都是意外死亡,警方通過查閱死者的電腦和手機晨雳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進店門行瑞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人餐禁,你說我怎么就攤上這事血久。” “怎么了坠宴?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵洋魂,是天一觀的道長。 經(jīng)常有香客問我喜鼓,道長副砍,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任庄岖,我火速辦了婚禮豁翎,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘隅忿。我一直安慰自己心剥,他們只是感情好,可當我...
    茶點故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布背桐。 她就那樣靜靜地躺著优烧,像睡著了一般。 火紅的嫁衣襯著肌膚如雪链峭。 梳的紋絲不亂的頭發(fā)上畦娄,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天,我揣著相機與錄音,去河邊找鬼熙卡。 笑死杖刷,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的驳癌。 我是一名探鬼主播滑燃,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼颓鲜!你這毒婦竟也來了表窘?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤甜滨,失蹤者是張志新(化名)和其女友劉穎蚊丐,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體艳吠,經(jīng)...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡麦备,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了昭娩。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片凛篙。...
    茶點故事閱讀 39,722評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖栏渺,靈堂內(nèi)的尸體忽然破棺而出呛梆,到底是詐尸還是另有隱情,我是刑警寧澤磕诊,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布填物,位于F島的核電站,受9級特大地震影響霎终,放射性物質(zhì)發(fā)生泄漏滞磺。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一莱褒、第九天 我趴在偏房一處隱蔽的房頂上張望击困。 院中可真熱鬧,春花似錦广凸、人聲如沸阅茶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽脸哀。三九已至,卻和暖如春扭吁,著一層夾襖步出監(jiān)牢的瞬間撞蜂,已是汗流浹背白筹。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留谅摄,地道東北人。 一個月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓系馆,卻偏偏與公主長得像送漠,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子由蘑,可洞房花燭夜當晚...
    茶點故事閱讀 44,614評論 2 353

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

  • org.springframework.beans: org.springframework.beans.fact...
    過河卒sc閱讀 590評論 1 1
  • 終于拿到offer了闽寡,但是還是想試試下周幾家大公司,努力準備吧尼酿,不可懈狄贰!那什么是依賴注入呢? 所謂依賴注入裳擎,就是...
    Ernest_Chou閱讀 897評論 0 0
  • 被現(xiàn)實生活 壓迫的人們 胸口的呼息 異常的疼痛涎永, 身體的上下部份 戴著沉重的鐵鏈 和牢固的手鐐 但是,追求自由 與...
    坡山俯愿君閱讀 188評論 1 1
  • 潯陽舊時月鹿响,南湖靜水光羡微。 夜游李公堤,云廬在遠方惶我。 凄凄冬日雨妈倔,貽我錦香囊盯蝴。 天涯無期覓听怕,知君在故鄉(xiāng)捧挺。
    楚云安閱讀 285評論 2 2
  • 這么多年過去了鸣峭,依然回不去的是18歲摊溶。 我相信很多人都有同感莫换,我們的18歲都是那樣的美好坷剧,陽光明媚惫企,充滿朝氣狞尔,開心...
    sizeof閱讀 390評論 0 0