Spring 源碼(6)BeanFactoryPostProcessor怎麼執行的?

来源:https://www.cnblogs.com/redwinter/archive/2022/04/26/16196359.html
-Advertisement-
Play Games

上一篇文章 https://www.cnblogs.com/redwinter/p/16167214.html 解讀瞭如何進行自定義屬性值的編輯器的解析器,並且還介紹了BeanFactory的準備過程中做了哪些事情。這篇文章繼續介紹AbstractApplicationContext#refresh ...


上一篇文章 https://www.cnblogs.com/redwinter/p/16167214.html 解讀瞭如何進行自定義屬性值的編輯器的解析器,並且還介紹了BeanFactory的準備過程中做了哪些事情。這篇文章繼續介紹AbstractApplicationContext#refresh的方法。

AbstractApplicationContext提供的postProcessBeanFactory空方法

postProcessBeanFactory這個方法沒名字跟BeanFactoryPostProcessor介面中的方法一樣,但是他的功能是提供給子類進行添加一些額外的功能,比如添加BeanPostProcessor介面的實現,或者定製一些其他的功能也是可以的,因為這個方法你可以拿到BeanFactory,自然是可以對他進行一些功能的定製的。

這裡看下Spring 提供的子類GenericWebApplicationContext是如何實現的:

@Override
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
  if (this.servletContext != null) {
    beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext));
    beanFactory.ignoreDependencyInterface(ServletContextAware.class);
  }
  WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext);
  WebApplicationContextUtils.registerEnvironmentBeans(beanFactory, this.servletContext);
}

這裡他註冊了一個ServletContextAwreProcessorbeanFactory中,ServletContexAwareProcessor是一個BeanPostProcessor介面的子類。

重頭戲BeanFactoryPostProcessor

接下來分析AbstractApplicationContext#refresh中的invokeBeanFactoryPostProcessors方法,這個方法用來註冊和執行BeanFactoryPostProcessor的。

直接上源碼:

protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
  // 執行所有的BeanFactoryPostProcessor
  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)
  // aop的處理
  if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
    beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
    beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
  }
}

重點在這裡:

PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

首先獲取BeanFactoryPostProcessor的集合,這裡獲取到都是用戶在定製BeanFactoryadd加入進去的,進入這個方法:

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

  // Invoke BeanDefinitionRegistryPostProcessors first, if any.
  // 已經處理的Bean
  Set<String> processedBeans = new HashSet<>();
  // 先進性外部BFPP的處理,並且判斷當前Factory是否是BeanDefinitionRegistry
  if (beanFactory instanceof BeanDefinitionRegistry) {
    BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
    // 保存BFPP的Bean
    List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
    // 保存BDRPP的Bean
    List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();
    // 開始處理外部傳入的BFPP
    for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
      // 先處理BDRPP
      if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
        BeanDefinitionRegistryPostProcessor registryProcessor =
          (BeanDefinitionRegistryPostProcessor) postProcessor;
        // 直接調用BDRPP的介面方法,後面的postProcessBeanFactory 方法後面統一處理
        registryProcessor.postProcessBeanDefinitionRegistry(registry);
        // 加入到BFPP的集合中
        registryProcessors.add(registryProcessor);
      }
      else {
        // 加入到BDRPP的集合中
        regularPostProcessors.add(postProcessor);
      }
    }

    // Do not initialize FactoryBeans here: We need to leave all regular beans
    // uninitialized to let the bean factory post-processors apply to them!
    // Separate between BeanDefinitionRegistryPostProcessors that implement
    // PriorityOrdered, Ordered, and the rest.
    // 保存當前的BDRPP
    List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();

    // First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
    // 按類型獲取BeanName
    String[] postProcessorNames =
      beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
    for (String ppName : postProcessorNames) {
      // 判斷當前的beanName是都是實現了PriorityOrdered
      if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
        // 加入到當前註冊的BDRPP集合中
        currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
        // 加入到已經處理的bean集合中
        processedBeans.add(ppName);
      }
    }
    // 對當前的BDRPP進行排序
    sortPostProcessors(currentRegistryProcessors, beanFactory);
    // 將當前的BDRPP全部加入到最前面定義的BDRPP的集合中
    registryProcessors.addAll(currentRegistryProcessors);
    // 執行當前的BDRPP的postProcessBeanDefinitionRegistry方法
    invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
    // 清空當前的BDRPP
    currentRegistryProcessors.clear();

    // Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered.
    // 再次獲取bdrpp,因為上面的執行可能還會加入新的bdrpp進來
    postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
    for (String ppName : postProcessorNames) {
      // 判斷是否已經處理過,並且是否實現了Ordered介面
      if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
        // 加入到當前的BDRPP的集合中
        currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
        // 添加到已經處理的集合中
        processedBeans.add(ppName);
      }
    }
    // 排序
    sortPostProcessors(currentRegistryProcessors, beanFactory);
    // 加入到BDRPP集合中
    registryProcessors.addAll(currentRegistryProcessors);
    // 執行bdrpp的postProcessBeanDefinitionRegistry方法
    invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
    // 清空當前bdrpp集合
    currentRegistryProcessors.clear();

    // Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear.
    boolean reiterate = true;
    // 迴圈去獲取BDRPP,然後進行排序、執行操作,直到所有的BDRPP全部執行完
    while (reiterate) {
      reiterate = false;
      // 獲取BDRPP
      postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
      for (String ppName : postProcessorNames) {
        // 如果已經處理過,就執行BDRPP,並且退出迴圈,否則繼續迴圈
        if (!processedBeans.contains(ppName)) {
          currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
          processedBeans.add(ppName);
          reiterate = true;
        }
      }
      // 排序
      sortPostProcessors(currentRegistryProcessors, beanFactory);
      // 加入到BDRPP集合中
      registryProcessors.addAll(currentRegistryProcessors);
      // 執行bdrpp
      invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
      currentRegistryProcessors.clear();
    }

    // Now, invoke the postProcessBeanFactory callback of all processors handled so far.
    // 執行bdrpp 中的postProcessBeanFactory方法
    invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
    // 執行bfpp 中的postProcessBeanFactory方法
    invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
  }

  else {
    // 如果不是bdrpp,那麼直接執行bfpp的postProcessBeanFactory
    // Invoke factory processors registered with the context instance.
    invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
  }

  // Do not initialize FactoryBeans here: We need to leave all regular beans
  // uninitialized to let the bean factory post-processors apply to them!
  // 獲取BFPP的beanName集合
  String[] postProcessorNames =
    beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);

  // Separate between BeanFactoryPostProcessors that implement PriorityOrdered,
  // Ordered, and the rest.
  // 定義實現了PriorityOrdered的BFPP
  List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
  // 定義實現了Ordered介面的集合
  //		List<String> orderedPostProcessorNames = new ArrayList<>();
  List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>();
  // 定義沒有排序的集合
  //		List<String> nonOrderedPostProcessorNames = new ArrayList<>();
  List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>();
  for (String ppName : postProcessorNames) {
    // 如果已經處理過了就不做處理
    if (processedBeans.contains(ppName)) {
      // skip - already processed in first phase above
    }
    else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
      priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
    }
    else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
      //				orderedPostProcessorNames.add(ppName);
      orderedPostProcessors.add(beanFactory.getBean(ppName,BeanFactoryPostProcessor.class));
    }
    else {
      //				nonOrderedPostProcessorNames.add(ppName);
      nonOrderedPostProcessors.add(beanFactory.getBean(ppName,BeanFactoryPostProcessor.class));
    }
  }

  // First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered.
  // 排序
  sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
  // 先執行PriorityOrdered介面的bfpp
  invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);

  // Next, invoke the BeanFactoryPostProcessors that implement Ordered.
  // 這裡將上面獲取到Ordered介面的BFPP進行集合轉換,然後排序,然後執行,這裡其實可以直接合併,
  // 在上述進行獲取時就放在這個orderedPostProcessors集合中
  //		List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size());
  //		for (String postProcessorName : orderedPostProcessorNames) {
  //			orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
  //		}
  sortPostProcessors(orderedPostProcessors, beanFactory);
  invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);

  // Finally, invoke all other BeanFactoryPostProcessors.
  // 處理沒有排序的
  //		List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());
  //		for (String postProcessorName : nonOrderedPostProcessorNames) {
  //			nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
  //		}
  invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);

  // Clear cached merged bean definitions since the post-processors might have
  // modified the original metadata, e.g. replacing placeholders in values...
  // 清除緩存的元數據,因為經過BFPP的執行,可能BeanDefinition的屬性值已經個變化,比如使用占位符的屬性值
  beanFactory.clearMetadataCache();
}

這個方法大概很長,實際上就做了一下這麼幾點事情:

  • 先執行外部傳入的BeanFactoryPostProcessor的實現
  • 處理時先處理BeanFactoryPostProcessor的子介面BeanDefinitionRegistryPostProcessor的實現
  • 處理BeanDefinitionRegistryPostProcessor實現的時候先處理實現了PriorityOrdered介面的實現
  • 處理完PriorityOrdered介面實現的類之後再處理實現了Ordered介面的實現
  • 處理完Ordered介面的實現類之後處理沒有排序的
  • 處理完BeanDefinitionRegistryPostProcessor的實現之後處理BeanFactoryPostProcessor的實現
  • 處理順序也是PriorityOrededOrdered,沒有排序的

這裡大概邏輯就是這個,看起來可能不是很懂,畫個流程圖:

通過流程圖可以簡化為:先遍歷執行外部傳入的BFPP,再執行BDRPP,再執行BFPP三部分,處理每一部分可能會進行排序操作,排序按照PriorityOrderedOrderednoSort進行排序再執行。

這裡解釋下BeanDefinitionRegistryPostProcessor,這個介面是BeanFactoryPostProcessor,它裡面包含一個方法叫postProcessBeanDefinitionRegistry,這個方法非常重要,在實現類ConfigurationClassPostProcessor中就是使用這個方法進行註解的解析的,而且這個類也是實現SpringBoot自動裝配的關鍵。

ConfigurationClassPostProcessor這個類是什麼時候加入到Spring容器的呢?

在我們啟動容器的時候,Spring會進行BeanDefinition的掃描,如果我們在xml配置文件中開啟了註解掃描:

<context:component-scan base-package="com.redwinter.test"/>

那麼這個時候就會自動添加多個BeanDefinitionSpring容器中,beanNameorg.springframework.context.annotation.internalConfigurationAnnotationProcessor,其他還有幾個:

前面的文章 https://www.cnblogs.com/redwinter/p/16165878.html 講到自定義標簽,在spring解析xml時分為預設的命名空間和自定義的命名空間的,而context就是自定義的命名空間,這個標簽的解析器為ComponentScanBeanDefinitionParser,這個類中的parse方法就是解析邏輯處理:

@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {
  String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);
  basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
  String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
                                                            ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
  // Actually scan for bean definitions and register them.
  // 配置掃描器
  ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
  // 掃描BeanDefinition,在指定的包下
  Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
  // 註冊組件
  registerComponents(parserContext.getReaderContext(), beanDefinitions, element);

  return null;
}

這個方法執行流程:

  • 創建一個配置掃描器
  • 掃描指定包下標有註解的類並解析為BeanDefinition
  • 執行registerComponents方法,註冊組件

registerComponents方法裡面就是添加ConfigurationClassPostProcessor的地方,由於代碼太多這裡只貼部分代碼:

// ...省略部分代碼
Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);
		// 判斷註冊器中個是否包含org.springframework.context.annotation.internalConfigurationAnnotationProcessor
		// 不包含就加入一個ConfigurationClassPostProcessor的BeanDefinition
		// 用於解析註解
		if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
			// 創建一個BeanDefinition為ConfigurationClassPostProcessor
			RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
			def.setSource(source);
			// 註冊一個beanName為org.springframework.context.annotation.internalConfigurationAnnotationProcessor
			// 的BeanDefinition,class為ConfigurationClassPostProcessor
			beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
		}
		// 創建一個AutowiredAnnotationBeanPostProcessor的BeanDefinition
		// 用於自動裝配
		if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
			RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
			def.setSource(source);
			beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
		}
// ...省略部分代碼

源碼中註冊了一個beanNameCONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME常量的名字,這個常量就是org.springframework.context.annotation.internalConfigurationAnnotationProcessorclassConfigurationClassPostProcessor

那註解的解析是如何進行解析的呢?由於篇幅過長,下一篇再來解析。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、使用writing-mode(推薦使用) writing-mode:翻譯過來是“寫字 — 模式”,文本在水平或垂直方向上如何排布 有以下幾個屬性值: horizontal-tb: 水平展示,也就是橫著展示文字,最平常預設的樣式 vertical-rl:垂直展示,也就是上面圖片上實現的樣式 ver ...
  • 今天,群友問了這樣一個問題,如下所示的滑鼠跟隨交互效果,如何實現: 簡單分析一下,這個交互效果主要有兩個核心: 藉助了 CSS 3D 的能力 元素的旋轉需要和滑鼠的移動相結合 本文,就將講述如何使用純 CSS 實現類似的交互效果,以及,藉助 JavaScript 綁定滑鼠事件,快速還原上述效果。 純 ...
  • KISS原則 kiss原則的英文描述有好幾個版本,比如下麵這幾個。 Keep It Simple and Stupid. Keep It Short and Simple. Keep It Simple and Straightforward. 它們意思都差不多,翻譯成中文就是儘量保持簡單。我們知道 ...
  • 本文再寫一篇和具體業務邏輯幾乎無關的公共服務應用監控平臺。PowerDotNet自研的應用監控平臺系統,是服務治理的重要拼圖,和服務治理平臺配合使用效果更好。 監控開源產品非常豐富,站在巨人的肩膀上,PowerDotNet自研的監控平臺,除了基本的監控功能,還可以通過加一層代理,將應用接入開源監控軟 ...
  • 解釋器模式是什麼 解釋器是一種行為型設計模式,指給分析對象定義一個語言,並定義該語言的文法表示,再設計一個解析器來解釋語言中的句子。也就是說,用編譯語言的方式來分析應用中的實例。這種模式實現了文法表達式處理的介面,該介面解釋一個特定的上下文。 為什麼用解釋器模式 在軟體開發中,會遇到有些問題多次重覆 ...
  • 幫助你在一天之內通過這篇文章快速學習SpringBoot框架以及各種開發必備的工具與插件!!! ...
  • Java面試題資料合集 2021年最新版--Java+最常見的+200++面試題彙總+答案總結彙總.pdf BIO,NIO,AIO,Netty面試題 35道.pdf Dubbo面試題 47道.pdf ElasticSearch面試題 30道.pdf Git常用命令面試題 60道.pdf Java基礎 ...
  • public class SprinklerSystem { private String value1, value2, value3; private WaterSource source = new WaterSource(); private int i; private float f; ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...