Spring核心源碼深度解析(五) invokeBeanFactoryPostProcessors(下)

?invokeBeanFactoryPostProcessors(下)

在上一小節(jié)我們分析完了invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);這個方法干了那些事情,現(xiàn)在我們繼續(xù)對后文做分析昏兆,如圖:
image

上圖Spring進行循環(huán)蕴茴,判斷是否有遺漏掉的后置處理器沒有解析谬哀,一般情況下,currentRegistryProcessors會是0奶卓,我們運行代碼都會直接跳過晴音,繼續(xù)往下走:


image

這里是Spring最后一次對BeanDefinitionRegistryPostProcessor后置處理器進行解析肘交,主要是針對一些父類層級關系玻孟,通常也為0唆缴,所以依舊不會有什么作用。好了黍翎,接下來到了關鍵代碼了


image

我們對invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);進行跟蹤->postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) 面徽,我們將代碼停在下圖:


image

注意AppConfig.class的變化


image

這里它還是一個普通的類,我們調(diào)試下一步


image

可以發(fā)現(xiàn)匣掸,Spring將其增強成為了cglib增強類趟紊,那么AppConfig為什么會增強呢?我們下面進行詳細分析

@Configuration

當我們將 @Configuration取消時旺聚,


image

我們調(diào)試停在上面的代碼


image

可以看到這里的配置類并沒有被增強织阳,所以可以得出結論,我們的@Configuration可以將我們的普通類進行增強砰粹,不知讀者是否還記得我們在上一章所講full嗎,如果是 @Configuration類唧躲,Spring會認為這是一個配置類就會將其標識full,而這個full就是代表配置類是否需要增強碱璃,后面我們會在源碼中進行證明弄痹,好了,說了這么多嵌器,相信讀者應該對 @Configuration
有一定的了解肛真,現(xiàn)在我們來探討為什么Spring要增強 配置類,這里我們對加上@Configuration和取消@Configuration進行測試爽航,我們看下面代碼:


image

第一種情況-》取消@Configuration


image

第二種情況-》加上@Configuration


image

我們可以看到有了@Configuration蚓让,我們的user1()和user2()方法返回的對象是同一個對象,當然這里還有第三種情況加上static讥珍,讀者可以自行測試看看又是什么情況历极,結果會發(fā)現(xiàn)代理功能會失效,至于為什么會失效我們在后面的源碼會詳細說明衷佃。好了我們大概知道了@Configuration的作用趟卸,那么Spring是如何對@Configuration進行增強的呢,我們回到源碼中:


image

進入enhanceConfigurationClasses

public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
    Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
?
    //找到config注解類氏义,這里就可以看到full和lite了锄列,如果full會變成增強類,目的是為了實現(xiàn)單例
    for (String beanName : beanFactory.getBeanDefinitionNames()) {
      BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
      if (ConfigurationClassUtils.isFullConfigurationClass(beanDef)) {
        if (!(beanDef instanceof AbstractBeanDefinition)) {
          throw new BeanDefinitionStoreException("Cannot enhance @Configuration bean definition '" +
              beanName + "' since it is not stored in an AbstractBeanDefinition subclass");
        }
        else if (logger.isInfoEnabled() && beanFactory.containsSingleton(beanName)) {
          logger.info("Cannot enhance @Configuration bean definition '" + beanName +
              "' since its singleton instance has been created too early. The typical cause " +
              "is a non-static @Bean method with a BeanDefinitionRegistryPostProcessor " +
              "return type: Consider declaring such methods as 'static'.");
        }
        configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);
      }
    }
    if (configBeanDefs.isEmpty()) {
      // nothing to enhance -> return immediately
      return;
    }
?
    ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
    for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {
      AbstractBeanDefinition beanDef = entry.getValue();
      // If a @Configuration class gets proxied, always proxy the target class
      beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
      try {
        // Set enhanced subclass of the user-specified bean class
        Class<?> configClass = beanDef.resolveBeanClass(this.beanClassLoader);
        if (configClass != null) {
          Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
          if (configClass != enhancedClass) {
            if (logger.isTraceEnabled()) {
              logger.trace(String.format("Replacing bean definition '%s' existing class '%s' with " +
                  "enhanced class '%s'", entry.getKey(), configClass.getName(), enhancedClass.getName()));
            }
            beanDef.setBeanClass(enhancedClass);
          }
        }
      }
      catch (Throwable ex) {
        throw new IllegalStateException("Cannot load configuration class: " + beanDef.getBeanClassName(), ex);
      }
    }
  }

這里(ConfigurationClassUtils.isFullConfigurationClass(beanDef))會判斷有沒有標識了FULL的配置類惯悠,如果沒有下面configBeanDefs
會為空直接返回邻邮,如果不為空,對配置類增強吮螺,我們鎖定這段代碼Class<?> enhancedClass = enhancer.enhance(configClass,
this.beanClassLoader);-》Class<?> enhancedClass createClass(newEnhancer(configClass,classLoader));-》
newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader)最后來到了這里:


image

在對@Configuration做詳細分析時饶囚,筆者可以先自行思考帕翻,Spring對配置類進行增強之后如何做到采用不同的方式去創(chuàng)建對象,為什么在Spring的容器里都只會存放一份Bean萝风,如果要實現(xiàn)這種功能嘀掸,是不是需要改變創(chuàng)建對象的方式呢?那么既然我們的工廠我們的@Bean.class是創(chuàng)建對象交給Spring管理规惰,那么如果我們創(chuàng)建對象的方式先通過工廠里面獲取睬塌,如果獲取不到我們再創(chuàng)建對象交給Spring管理是不是就能夠?qū)崿F(xiàn)呢?好了歇万,我想讀者應該有些思路了揩晴,我們再看一張圖


image

圖上大致講解了配置類轉變?yōu)樵鰪婎惡蟠蟾攀鞘裁礃幼拥模瑥膱D上我們可以看到增強代理類有個私有變量贪磺,這個變量存放著我們的工廠追他,那么為什么需要這個工廠我想讀者應該有所頓悟犀农。好了我們再回到源碼:


image

這里Spring是采用CGLIB增強瘪弓,如果讀者沒有了解過CGLIB的話乓旗,最好在網(wǎng)上了解下CGLIB的使用,這里不再贅述刹前。首先將目標類交給enhancer泳赋,其次添加了一個接口,EnhanceConfiguration.class喇喉,這個類是CGLIB實現(xiàn)增強的核心祖今,我們點進這個接口會發(fā)現(xiàn)它實現(xiàn)了BeanFactoryAwae接口,然后我們繼續(xù)往下走拣技,看到setStrategy(newBeanFactoryAwareGeneratorStrategy(classLoader))千诬,我們點進去
image
image

如圖我們可以大概才到,Spring在這里添加了一個策略膏斤,這個策略是聲明一個變量名為"$$beanFactory"的公共屬性大渤,再回想我們之前添加的接口是不是可以得到工廠,那么我么你現(xiàn)在還差一步掸绞,如何將前面的原料加工就是我們代理需要去做的事情:


image

這個Callback筆者就不分析了,因為涉及到CGLIB的知識耕捞,如果讀者有情趣可以自行研究衔掸。好了我們生成代理后還差一件事情,那就是如何將我們拿到的工廠賦值給代理類得變量中俺抽,看下面代碼


image

我們點進去


image

這里讀者是不是豁然開朗敞映,是不是覺得Spring的設計如神一般的存在啊,好了當我們了解到這里其實
invokeBeanFactoryPostProcessors這個方法也差不多結束了磷斧,當然還有一些更細節(jié)的地方筆者會作為單獨的章節(jié)進行描述振愿,哎捷犹,總算是到了一半了,什么才一半冕末?萍歉,畢竟我們真正的對象實例還沒開始呢,好的我們在下一小節(jié)會對Spring剩余部分進行深度分析档桃。

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末枪孩,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子藻肄,更是在濱河造成了極大的恐慌蔑舞,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件嘹屯,死亡現(xiàn)場離奇詭異攻询,居然都是意外死亡,警方通過查閱死者的電腦和手機州弟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進店門钧栖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人呆馁,你說我怎么就攤上這事桐经。” “怎么了浙滤?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵阴挣,是天一觀的道長。 經(jīng)常有香客問我纺腊,道長畔咧,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任揖膜,我火速辦了婚禮誓沸,結果婚禮上,老公的妹妹穿的比我還像新娘壹粟。我一直安慰自己拜隧,他們只是感情好,可當我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布趁仙。 她就那樣靜靜地躺著洪添,像睡著了一般。 火紅的嫁衣襯著肌膚如雪雀费。 梳的紋絲不亂的頭發(fā)上干奢,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天,我揣著相機與錄音盏袄,去河邊找鬼忿峻。 笑死薄啥,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的逛尚。 我是一名探鬼主播垄惧,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼黑低!你這毒婦竟也來了赘艳?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤克握,失蹤者是張志新(化名)和其女友劉穎蕾管,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體菩暗,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡掰曾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了停团。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片旷坦。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖佑稠,靈堂內(nèi)的尸體忽然破棺而出秒梅,到底是詐尸還是另有隱情,我是刑警寧澤舌胶,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布捆蜀,位于F島的核電站,受9級特大地震影響幔嫂,放射性物質(zhì)發(fā)生泄漏辆它。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一履恩、第九天 我趴在偏房一處隱蔽的房頂上張望锰茉。 院中可真熱鬧,春花似錦切心、人聲如沸飒筑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽扬霜。三九已至,卻和暖如春而涉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背联予。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工啼县, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留材原,地道東北人。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓季眷,卻偏偏與公主長得像余蟹,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子子刮,可洞房花燭夜當晚...
    茶點故事閱讀 45,037評論 2 355