Spring核心源碼深度解析(四) invokeBeanFactoryPostProcessors(上)

invokeBeanFactoryPostProcessors(上)


protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
    //這里的參數getBeanFactoryPostProcessors()講道理很容易誤導大家,我在調試的時候發現它什么都沒返回,注意他們的區別,list和map
    //回到之前我所以提到的實現了BeanFactoryPostProcessor的Config類,按道理應該是有的,但是其實它拿到的是一個list,
    //而我們是放在map里面的,所以這里它只是拿到程序員自己手動add的,注意注解方式也是拿不到的
    PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

    // Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
    // (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
    if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
      beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
      beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
    }
  }

看這里的參數getBeanFactoryPostProcessors()講道理很容易誤導人,我在調試的時候發現它什么都沒返回,注意他們的區別,
image
image

回到之前我所以提到的4個后置處理器類使用的map去存起,所以不是同一個類,而我們的getBeanFactoryPostProcessors()只是拿到程序員自己手動add的,如:
image

,這里會有個坑,如果讀者嘗試在我們的Main函數加上這段代碼:

   public static void main(String[] args) {
    AnnotationConfigApplicationContext annotationConfigApplicationContext=
        new AnnotationConfigApplicationContext(AppConfig.class);
    annotationConfigApplicationContext.addBeanFactoryPostProcessor(new BeanFactoryPostProcessor() {
      @Override
      public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        
      }
    });

  }

然后繼續調試getBeanFactoryPostProcessors(),得出的結果會是多少呢,其實還是0,至于為什么,我們換一種寫法


 AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
annotationConfigApplicationContext.regisrer(Appconfig.class);
annotationConfigApplicationContext.addBeanFactoryPostProcessor();
annotationConfigApplicationContext.refresh();

再次調試getBeanFactoryPostProcessors(),得出結果為1,相信讀者應該有多感悟。搞明白這個之后我們繼續進入invokeBeanFactoryPostProcessors

這個方法,

public static void invokeBeanFactoryPostProcessors(
      ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {

    // Invoke BeanDefinitionRegistryPostProcessors first, if any.
    Set<String> processedBeans = new HashSet<>();
        //注意這里的beanFactory是最基本的子類,這個類包含了如何對bean注冊,創建,維護等功能,所以一定會進去
    if (beanFactory instanceof BeanDefinitionRegistry) {
      BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;

      //這里定義兩個list是為了區分子類是實現了BeanFactoryPostProcessor,而且這里是裝的程序員自己添加的
      // 還是BeanDefinitionRegistryPostProcessor,因為這兩個接口是父子關系,所以功能有所區別
      List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
      List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();
            //這里肯定為空,前面已經說了,我們這里是程序員自己add的,而我們并沒有
      for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
        if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
          BeanDefinitionRegistryPostProcessor registryProcessor =
              (BeanDefinitionRegistryPostProcessor) postProcessor;
          registryProcessor.postProcessBeanDefinitionRegistry(registry);
          registryProcessors.add(registryProcessor);
        }
        else {
          regularPostProcessors.add(postProcessor);
        }
      }

首先遍歷beanFactoryPostProcessors,注意這個參數是我們傳進來,是從getBeanFactoryPostProcessors()獲取的,因此一般情況我們不會手動設置,所以這里都會為空,我們往下
image

在這段代碼同樣是獲取后置處理器,只不過獲取的來源不同,還記得我們之前說過的那4個后置處理器嗎?其中有一個Spring的關鍵類ConfigurationClassPostProcessor,不知道讀者還是否記得這個類,這里Spring就會把它取出來,我們Dubug調試一下
image

獲取這個ConfigurationClassPostProcessor類后,Spring會合并之前兩個循環的后置處理器,然后到主干方法invokeBeanDefinitionRegistryPostProcessors


image

我們在跟蹤進去:


image

看第二句代碼String[] candidateNames = registry.getBeanDefinitionNames();這里會拿出6個Name,添加到configCandidates里,看調試結果回想我在前文所提出的注冊的后置處理器就明白為什么是6個,其目的就是為了找出我們的配置類,如果找到就標記成full,有些讀者可能了解Spring的@configuration注解是配置類的意思,但不知道讀者是否了解過加和不加這個注解的作用是什么,這里我們把@configuration注解注釋掉,嘗試運行會發現結果并不會報錯,


image

至于為什么Spring搞這么個注解,筆者會在后文對@configuration的作用以及源碼實現進行詳細介紹,我們繼續回到代碼:


image

這里我們就拿到了我們的配置類,既然拿到了配置類,那么下一步就對配置類進行解析,看代碼:


image

看到這句代碼 parser.parse();這句代碼是Spring解析的核心代碼,我們點進去


image

這里判斷需要解析的BeanDefinition是那種類型,這里回到我們的入口


image

,我們注冊的是AppConfig配置類,但其實我們還可以配置普通類,比如我們直接將我們的User類放進去


image

隨后我們在調試parse->processConfigurationClass->


image

-> doProcessConfigurationClass(configClass, sourceClass);


image

看第一段代碼,如果我們的配置類有標識了@Component 這樣的類

如果有就會返回ture,但可以看到我們并沒有標志@Component,缺能夠進入這個判斷,看下面代碼:
image

,看到這里想到大家應該明白了吧。好了我們回到代碼進入->processMemberClasses 進行分析,我們先在配置類添加一段代碼
image
image

,可以看到已經找到這么一個類,這段代碼我們點到為止,因為下面的配置類信息解析包含了這里的設計思想,所以這里不重點分析。


image

首先取出我們的掃描注解


image

然后執行 Set<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan,
sourceClass.getMetadata().getClassName());這段代碼,這里就是解析的核心代碼,我們跟進去,


image

看第一段代碼new ClassPathBeanDefinitionScanner,這里讀者可以回顧下我們在Spring源碼第三章執行構造器提到過一個scanner,筆者曾說那個scanner并不是Spring在解析時內部使用的掃描器,而當前筆者已經給出證明,當然之前定義的this.scanner有什么作用,筆者猜測可能是交給外部工程師自己操作的吧,好了話不過說,我們回到代碼,接著Spring又拿到了generatorClass,隨后給scanner賦予一些屬性,最后來到核心代碼
image

我們進入doscan方法


image

這里我們看到在經過了findCandidateComponents(basePackage);這個方法后,Spring就已經拿到我了我們定義的注解類,我們繼續進入


image
image

可以看出Spring真正解決包掃描結束是采用的ASM字節碼提取技術,筆者對此也是一知半解,所以就不多做介紹了,有興趣的讀者們可以自行深入研究,這里我就不做文章了,我們還是回到代碼,下面的代碼不太重要,因此Spring拿到掃描出來的類后就返回,好的我們繼續回到代碼


image

在對普通注解類解析完后,會將普通類拿出來作為當做配置類繼續遞歸解析,當然通常都會跳過,然后開始對@import注解解析,有些讀者可能還沒見過這個@import類,我們看下@Import注解類


image

,它的作用很清晰,目的就是向Spring容器注冊類,看下實例:


image

這樣就和在MyImportBeanDefinitionRegistrar加上@Component是一樣效果,這里源碼的細節我帶著大家細看了,其目的就是解析@import三種類:普通類,ImportSelector,ImportBeanDefinitionRegistrar。這里我將筆記注釋貼出來,大家自行理解:

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
      Collection<SourceClass> importCandidates, boolean checkForCircularImports) {

    if (importCandidates.isEmpty()) {
      return;
    }

    if (checkForCircularImports && isChainedImportOnStack(configClass)) {
      this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
    }
    else {
      this.importStack.push(configClass);
      try {
        for (SourceClass candidate : importCandidates) {
          //這里是處理ImportSelector,轉換成bd,放入一個集合
          if (candidate.isAssignable(ImportSelector.class)) {
            // Candidate class is an ImportSelector -> delegate to it to determine imports
            Class<?> candidateClass = candidate.loadClass();
            ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
            ParserStrategyUtils.invokeAwareMethods(
                selector, this.environment, this.resourceLoader, this.registry);
            //這里是否是延遲執行,如果目標對象實現的是DeferredImportSelector,就表示需要延遲
            if (selector instanceof DeferredImportSelector) {
              this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
            }
            else {
              //這里是遞歸,因為Import的類可能還有Import,如果沒有會遞歸到下面的普通類放入集合
              String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
              Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
              processImports(configClass, currentSourceClass, importSourceClasses, false);
            }
          }
          //ImportBeanDefinitionRegistrar這個是Import的第二種類,實現可以直接拿到注冊器
          else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
            // Candidate class is an ImportBeanDefinitionRegistrar ->
            // delegate to it to register additional bean definitions
            Class<?> candidateClass = candidate.loadClass();
            ImportBeanDefinitionRegistrar registrar =
                BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
            ParserStrategyUtils.invokeAwareMethods(
                registrar, this.environment, this.resourceLoader, this.registry);
            //將實現了ImportBeanDefinitionRegistrar的子類放進importBeanDefinitionRegistrars,后面spring會對其處理
            configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
          }
          else {
            // Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
            // process it as an @Configuration class
            //普通import類和ImportSelector都放進這個configurationClasses
            this.importStack.registerImport(
                currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
            processConfigurationClass(candidate.asConfigClass(configClass));
          }
        }
      }
      catch (BeanDefinitionStoreException ex) {
        throw ex;
      }
      catch (Throwable ex) {
        throw new BeanDefinitionStoreException(
            "Failed to process import candidates for configuration class [" +
            configClass.getMetadata().getClassName() + "]", ex);
      }
      finally {
        this.importStack.pop();
      }
    }
  }

讀者這里重點對ImportBeanDefinitionRegistrar的作用進行分享,看下面實例:


image

如果我們繼承了ImportBeanDefinitionRegistrar,那么我們就可以拿到BeanDefinitionRegistry,并就可以手動的向Spring添加一個 BeanDefinition,我們調試一下


image

當前我們的BeanDefinitionMap是11個,等到我們執行完這段代碼后就會變成12個,看下圖:


image

,我們改變了BeanDefinitionMap,而BeanDefinitionMap在Spring創建對象起到了絕對作用,所以我們就改變了Bean的創建,而這就是擴展Spring的一個接口之一,后期我們有機會的話我們會重點分享Spring-Mybatis和Spring-AOP是如何利用這些接口拓展的。
在對@Import注解解析完之后,又開始對配置類的@Bean解析


image

直到這里我們才算是對parser.parse(candidates);代碼執行完畢了,最后我們將這塊整體流程用一張圖簡介
image

,然后我們繼續回到代碼,在執行完parse方法后


image

然后做校驗,重點從這里開始->this.reader.loadBeanDefinitions(configClasses);,我們一直更進去會知道


image

這里就是對我們之前存儲的@Import注解類進行運行,并且會執行自定義的@Import類,到目前為止我們總算跑完了
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);這句代碼


image

接來下的內容筆者打算分開描述。

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