Spring官網閱讀(六)容器的擴展點(一)BeanFactoryPostProcessor

以前的文章咱們已經學習完了BeanDefinition的基本概念跟合併,其中屢次提到了容器的擴展點,這篇文章咱們就開始學習這方面的知識。這部份內容主要涉及官網中的1.8小結。按照官網介紹來講,容器的擴展點能夠分類三類,BeanPostProcessor,BeanFactoryPostProcessor以及FactoryBean。本文咱們主要學習BeanFactoryPostProcessor,對應官網中內容爲1.8.2小節java

01程序員

總覽web

先看看官網是怎麼說的:spring

從上面這段話,咱們能夠總結以下幾點:sql

  1. BeanFactoryPostProcessor能夠對Bean配置元數據進行操做。也就是說,Spring容器容許 BeanFactoryPostProcessor讀取指定Bean的配置元數據,並能夠在Bean被實例化以前修改它。這裏說的配置元數據其實就是咱們以前講過的 BeanDefinition
  2. 咱們能夠配置多個 BeanFactoryPostProcessor,而且只要咱們配置的 BeanFactoryPostProcessor同時實現了 Ordered接口的話,咱們還能夠控制這些 BeanFactoryPostProcessor執行的順序

接下來,咱們經過Demo來感覺下BeanFactoryPostProcessor的做用:apache

02微信

示例app

這裏就以官網上的demo爲例:編輯器

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="locations" value="classpath:com/something/jdbc.properties"/>
</bean>

<bean id="dataSource" destroy-method="close"
        class="org.apache.commons.dbcp.BasicDataSource">

    <property name="driverClassName" value="${jdbc.driverClassName}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</bean>
# jdbc.properties
jdbc.driverClassName=org.hsqldb.jdbcDriver
jdbc.url=jdbc:hsqldb:hsql://production:9002
jdbc.username=sa
jdbc.password=root

在上面的例子中,咱們配置了一個PropertyPlaceholderConfigurer,爲了方便理解,咱們先分析下這個類,其UML類圖以下:ide

在這裏插入圖片描述
  • Ordered用於決定執行順序
  • PriorityOrdered,這個接口直接繼承了 Ordered接口,而且沒有作任何擴展。只是做爲一個標記接口,也用於決定 BeanFactoryPostProcessor的執行順序。在後文源碼分析時,咱們會看到他的做用
  • Aware相關的接口咱們在介紹Bean的生命週期回調時統一再分析,這裏暫且無論
  • FunctionalInterface,這是 java8新增的一個接口,也只是起一個標記做用,標記該接口是一個函數式接口。
  • BeanFactoryPostProcessor,表明這個類是一個Bean工廠的後置處理器。
  • PropertiesLoaderSupport,這個類主要包含定義了屬性的加載方法,包含的屬性以下:
// 本地屬性,能夠直接在XML中配置
@Nullable
protected Properties[] localProperties;

// 是否用本地的屬性覆蓋提供的文件中的屬性,默認不會
protected boolean localOverride = false;

// 根據地址找到的對應文件
@Nullable
private Resource[] locations;

// 沒有找到對應文件是否拋出異常,false表明不拋出
private boolean ignoreResourceNotFound = false;

// 對應文件資源的編碼
@Nullable
private String fileEncoding;

// 文件解析器
private PropertiesPersister propertiesPersister = new DefaultPropertiesPersister();
  • PropertyResourceConfigurer,這個類主要能夠對讀取到的屬性進行一些轉換
  • PlaceholderConfigurerSupport,主要負責對佔位符進行解析。其中幾個屬性以下:
// 默認解析的前綴
public static final String DEFAULT_PLACEHOLDER_PREFIX = "${";
// 默認解析的後綴
public static final String DEFAULT_PLACEHOLDER_SUFFIX = "}";
// 屬性名稱跟屬性值的分隔符
public static final String DEFAULT_VALUE_SEPARATOR = ":";
  • PropertyPlaceholderConfigurer繼承了上面這些類的全部功能,同時能夠配置屬性的解析順序
// 不在系統屬性中查找
public static final int SYSTEM_PROPERTIES_MODE_NEVER = 0;

// 若是在配置文件中沒有找到,再去系統屬性中查找
public static final int SYSTEM_PROPERTIES_MODE_FALLBACK = 1;

// 先查找系統屬性,沒查到再去查找配置文件中的屬性
public static final int SYSTEM_PROPERTIES_MODE_OVERRIDE = 2;

對這個類有一些瞭解以後,咱們回到以前的例子中,爲何在jdbc.properties文件中配置的屬性值會被應用到

BasicDataSource這個Bean上呢?爲了幫助你們理解,我畫了一個圖:

在這裏插入圖片描述

這個流程就如上圖,能夠看到咱們經過PropertyPlaceholderConfigurer這個特殊的BeanFactoryPostProcessor完成了BeanDefinition中的屬性值中的佔位符的替換。在BeanDefinition被解析出來後,Bean實例化以前對其進行了更改了。

在上圖中,建立Bean的過程咱們暫且無論,還有一個問題咱們須要弄清楚,Spring是如何掃描並解析成BeanDefinition的呢?這裏就不得不提到咱們接下來須要分析的這個接口了:BeanDefinitionRegistryPostProcessor,就將其簡稱爲bdrpp

03

bdrpp

咱們先來看一下這個接口的UML類圖:

在這裏插入圖片描述

從上圖中,咱們能夠得出兩個結論:

  1. BeanDefinitionRegistryPostProcessor直接繼承了 BeanFactoryPostProcessor,因此它也是一個Bean工廠的後置處理器
  2. Spring只提供了一個內置的 BeanDefinitionRegistryPostProcessor的實現類,這個類就是 ConfigurationClassPostProcessor,實際上咱們上面說的掃描解析成 BeanDefinition的過程就是由這個類完成的

咱們來看下這個接口定義:

public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
 void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;

}

public interface BeanFactoryPostProcessor {
 void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;

}

相比於正常的BeanFactoryPostProcessorBeanDefinitionRegistryPostProcessor多提供了一個方法,那麼多提供的這個方法有什麼用呢?這個方法會在何時執行呢?這裏我先說結論:

這個方法的左右也是爲了擴展,相比於BeanFactoryPostProcessorpostProcessBeanFactory方法,這個方法的執行時機會更加靠前,Spring自身利用這個特性完成了BeanDefinition的掃描解析。咱們在對Spring進行擴展時,也能夠利用這個特性來完成掃描這種功能,好比最新版的Mybatis就是這麼作的。關於Mybatis跟Spring整合的過程,我打算在寫完Spring的掃描以及容器的擴展點這一系列文章後單獨用一篇文章來進行分析。

接下來,咱們直接分析其源碼,驗證上面的結論。

04

源碼解析

在分析源碼前,咱們看看下面這個圖,以便你們對Spring的執行流程有個大概的瞭解:

在這裏插入圖片描述

上圖表示的是形如AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Config.class)的執行流程。咱們此次分析的代碼主要是其中的3-5-1流程。對於的代碼以下(代碼比較長,咱們拆分紅兩部分分析):

BeanDefinitionRegistryPostProcessor執行流程
public static void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
    Set<String> processedBeans = new HashSet<>();
    // 這個if基本上必定會成立,除非咱們手動new了一個beanFactory
    if (beanFactory instanceof BeanDefinitionRegistry) {
        
        BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
       
        // 存儲了只實現了BeanFactoryPostProcessor接口的後置處理器
        List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
        
        // 存儲了實現了BeanDefinitionRegistryPostProcessor接口的後置處理器
        List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();
  
        // 這個beanFactoryPostProcessors集合通常狀況下都是空的,除非咱們手動調用容器的addBeanFactoryPostProcessor方法
        for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
            if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
                BeanDefinitionRegistryPostProcessor registryProcessor = (BeanDefinitionRegistryPostProcessor) postProcessor;
               
                // 執行實現了BeanDefinitionRegistryPostProcessor接口的後置處理器的postProcessBeanDefinitionRegistry方法,注意這裏執行的不是postProcessBeanFactory方法,咱們上面已經講過了,實現了BeanDefinitionRegistryPostProcessor接口的後置處理器有兩個方法,一個是從父接口中繼承而來的postProcessBeanFactory方法,另外一個是這個接口特有的postProcessBeanDefinitionRegistry方法
                registryProcessor.postProcessBeanDefinitionRegistry(registry);
               
                // 保存執行過了的BeanDefinitionRegistryPostProcessor,這裏執行過的BeanDefinitionRegistryPostProcessor只是表明它的特有方法:postProcessBeanDefinitionRegistry方法執行過了,可是千萬記得,它還有一個標準的postProcessBeanFactory,也就是從父接口中繼承的方法還未執行
                registryProcessors.add(registryProcessor);
           
            } else {
               
                // 將只實現了BeanFactoryPostProcessor接口的後置處理器加入到集合中
                regularPostProcessors.add(postProcessor);
            }
        }
  // 保存當前須要執行的實現了BeanDefinitionRegistryPostProcessor接口的後置處理器
        List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();
  // 從容器中獲取到全部實現了BeanDefinitionRegistryPostProcessor接口的Bean的名字
        String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, truefalse);
        for (String ppName : postProcessorNames) {
            // 判斷這個類是否還實現了PriorityOrdered接口
            if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
                // 若是知足條件,會將其建立出來,同時添加到集合中
                // 正常狀況下,只會有一個,就是Spring容器本身提供的ConfigurationClassPostProcessor,Spring經過這個類完成了掃描以及BeanDefinition的功能
                currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                processedBeans.add(ppName);
            }
        }
        // 根據實現的PriorityOrdered接口進行拍訊
        sortPostProcessors(currentRegistryProcessors, beanFactory);
  
        // 將當前將要執行的currentRegistryProcessors所有添加到registryProcessors這個集合中
        registryProcessors.addAll(currentRegistryProcessors);
        
        // 執行後置處理器的邏輯,這裏只會執行BeanDefinitionRegistryPostProcessor接口的postProcessBeanDefinitionRegistry方法
        invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
        
        // 清空集合
        currentRegistryProcessors.clear();
  
        // 這裏從新獲取實現了BeanDefinitionRegistryPostProcesso接口的後置處理器的名字,思考一個問題:爲何以前獲取了一次不能直接用呢?還須要獲取一次呢?這是由於,在咱們上面執行過了BeanDefinitionRegistryPostProcessor中,能夠在某個類中,咱們擴展的時候又註冊了一個實現了BeanDefinitionRegistryPostProcessor接口的後置處理器
        postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, truefalse);
        for (String ppName : postProcessorNames) {
            // 確保沒有被處理過而且實現了Ordered接口
            if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
                // 加入到當前須要被執行的集合中
                currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                processedBeans.add(ppName);
            }
        }
       
        // 根據ordered接口進行排序
        sortPostProcessors(currentRegistryProcessors, beanFactory);
       
        // 將當前將要執行的currentRegistryProcessors所有添加到registryProcessors這個集合中
        registryProcessors.addAll(currentRegistryProcessors);
       
        // 執行後置處理器的邏輯,這裏只會執行BeanDefinitionRegistryPostProcessor接口的postProcessBeanDefinitionRegistry方法
        invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
        
        // 清空集合
        currentRegistryProcessors.clear();
  
        // 接下來這段代碼是爲了確認全部實現了BeanDefinitionRegistryPostProcessor的後置處理器可以執行完,之全部要一個循環中執行,也是爲了防止在執行過程當中註冊了新的BeanDefinitionRegistryPostProcessor
        boolean reiterate = true;
        while (reiterate) {
            reiterate = false;
            // 獲取普通的BeanDefinitionRegistryPostProcessor,不須要實現PriorityOrdered或者Ordered接口
            postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, truefalse);
            for (String ppName : postProcessorNames) {
                if (!processedBeans.contains(ppName)) {
                    // 只要發現有一個須要執行了的後置處理器,就須要再次循環,由於執行了這個後置處理可能會註冊新的BeanDefinitionRegistryPostProcessor
                    currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                    processedBeans.add(ppName);
                    reiterate = true;
                }
            }
            sortPostProcessors(currentRegistryProcessors, beanFactory);
            registryProcessors.addAll(currentRegistryProcessors);
            invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
            currentRegistryProcessors.clear();
        }
    ......
BeanFactoryPostProcessor執行流程:
 ......承接上半部分代碼......

        // 這裏開始執行單獨實現了BeanFactoryPostProcessor接口的後置處理器
        // 1.先執行實現了BeanDefinitionRegistryPostProcessor的BeanFactoryPostProcessor,在前面的邏輯中咱們只執行了BeanDefinitionRegistryPostProcessor特有的postProcessBeanDefinitionRegistry方法,它的postProcessBeanFactory方法尚未被執行,它會在這裏被執行
        invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
        // 2.執行直接實現了BeanFactoryPostProcessor接口的後置處理器
        invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
    } else {
  // 正常狀況下,進不來這個判斷,不用考慮
        invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
    }
  // 獲取全部實現了BeanFactoryPostProcessor接口的後置處理器,這裏會獲取到已經執行過的後置處理器,因此後面的代碼會區分已經執行過或者未執行過
  String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, truefalse);

  // 保存直接實現了BeanFactoryPostProcessor接口和PriorityOrdered接口的後置處理器
  List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();

  // 保存直接實現了BeanFactoryPostProcessor接口和Ordered接口的後置處理器
  List<String> orderedPostProcessorNames = new ArrayList<>();

  // 保存直接實現了BeanFactoryPostProcessor接口的後置處理器,不包括那些實現了排序接口的類
  List<String> nonOrderedPostProcessorNames = new ArrayList<>();
  for (String ppName : postProcessorNames) {
   if (processedBeans.contains(ppName)) {
                // 已經處理過了,直接跳過
   } else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
                // 符合條件,加入到以前申明的集合
    priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
   } else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
    orderedPostProcessorNames.add(ppName);
   } else {
    nonOrderedPostProcessorNames.add(ppName);
   }
  }

  // 先執行實現了BeanFactoryPostProcessor接口和PriorityOrdered接口的後置處理器
  sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
  invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);

  // 再執行實現了BeanFactoryPostProcessor接口和Ordered接口的後置處理器
  List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>();
  for (String postProcessorName : orderedPostProcessorNames) {
   orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
  }
  sortPostProcessors(orderedPostProcessors, beanFactory);
  invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);

  // 最後執行BeanFactoryPostProcessor接口的後置處理器,不包括那些實現了排序接口的類
  List<`1> nonOrderedPostProcessors = new ArrayList<>();
  for (String postProcessorName : nonOrderedPostProcessorNames) {
   nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
  }
  invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);

  // 將合併的BeanDefinition清空,這是由於咱們在執行後置處理器時,可能已經修改過了BeanDefinition中的屬性,因此須要清空,以便於從新合併
  beanFactory.clearMetadataCache();

經過源碼分析,咱們能夠將整個Bean工廠的後置處理器的執行流程總結以下:

首先,要明白一點,上圖分爲左右兩個部分,表明的不是兩個接口,而是兩個方法

  • 一個是 BeanDefinitionRegistryPostProcessor特有的 postProcessBeanDefinitionRegistry方法
  • 另一個是 BeanFactoryPostProcessorpostProcessBeanFactory方法

這裏咱們以方法爲維度區分更好說明問題,postProcessBeanDefinitionRegistry方法的執行時機早於postProcessBeanFactory。而且他們按照上圖從左到右的順序進行執行。

另外在上面進行代碼分析的時候不知道你們有沒有發現一個問題,當在執行postProcessBeanDefinitionRegistry方法時,Spring採用了循環的方式,不斷的查找是否有新增的BeanDefinitionRegistryPostProcessor,就是下面這段代碼:

boolean reiterate = true;
   while (reiterate) {
    reiterate = false;
    postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, truefalse);
    for (String ppName : postProcessorNames) {
     if (!processedBeans.contains(ppName)) {
      currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
      processedBeans.add(ppName);
      reiterate = true;
     }
    }
    sortPostProcessors(currentRegistryProcessors, beanFactory);
    registryProcessors.addAll(currentRegistryProcessors);
    invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
    currentRegistryProcessors.clear();
   }

可是在執行postProcessBeanFactory並無進行相似的查找。這是爲何呢?

筆者本身認爲主要是設計使然,Spring在設計時postProcessBeanFactory這個方法不是用於從新註冊一個Bean的,而是修改。咱們能夠看下這個方法上的這段java doc

 /**
  * Modify the application context's internal bean factory after its standard
  * initialization. All bean definitions will have been loaded, but no beans
  * will have been instantiated yet. This allows for overriding or adding
  * properties even to eager-initializing beans.
  * @param beanFactory the bean factory used by the application context
  * @throws org.springframework.beans.BeansException in case of errors
  */

 void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;

其中最重要的一段話:All bean definitions will have been loaded,全部的beanDefinition都已經被加載了。

咱們再對比下postProcessBeanDefinitionRegistry這個方法上的java doc

 /**
  * Modify the application context's internal bean definition registry after its
  * standard initialization. All regular bean definitions will have been loaded,
  * but no beans will have been instantiated yet. This allows for adding further
  * bean definitions before the next post-processing phase kicks in.
  * @param registry the bean definition registry used by the application context
  * @throws org.springframework.beans.BeansException in case of errors
  */

 void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;

你們注意這段話,This allows for adding further bean definitions before the next post-processing phase kicks in.容許咱們在下一個後置處理器執行前添加更多的BeanDefinition

從這裏,我相信你們更加能理解爲何postProcessBeanDefinitionRegistry這個方法的執行時機要早於postProcessBeanFactory了。

05

幾個問題

一、可不能夠在BeanFactoryPostProcessor去建立一個Bean,這樣有什麼問題?

從技術上來講這樣是能夠的,可是正常狀況下咱們不應這樣作,這是由於可能會存在該執行的Bean工廠的後置處理器的邏輯沒有被應用到這個Bean上。

二、BeanFactoryPostProcessor能夠被配置爲懶加載嗎?

不能配置爲懶加載,即便配置了也不會生效。咱們將Bean工廠後置處理器配置爲懶加載這個行爲就沒有任何意義

06

總結

在這篇文章中,咱們最須要了解及掌握的就是BeanFactoryPostProcessor執行的順序,總結以下:

  • 先執行直接實現了 BeanDefinitionRegistryPostProcessor接口的後置處理器,全部實現了 BeanDefinitionRegistryPostProcessor接口的類有兩個方法,一個是特有的 postProcessBeanDefinitionRegistry方法,一個是繼承子父接口的 postProcessBeanFactory方法。
    • postProcessBeanDefinitionRegistry方法早於 postProcessBeanFactory方法執行,對於 postProcessBeanDefinitionRegistry的執行順序又遵循以下原子
    • 執行完全部的 postProcessBeanDefinitionRegistry方法後,再執行實現了 BeanDefinitionRegistryPostProcessor接口的類中的 postProcessBeanFactory方法
    1. 先執行實現了 PriorityOrdered接口的類中的 postProcessBeanDefinitionRegistry方法
    2. 再執行實現了 Ordered接口的類中的 postProcessBeanDefinitionRegistry的方法
    3. 最後執行沒有實現上面兩個接口的類中的 postProcessBeanDefinitionRegistry的方法
  • 再執行直接實現了 BeanFactoryPostProcessor接口的後置處理器
    1. 先執行實現了 PriorityOrdered接口的類中的 postProcessBeanFactory方法
    2. 再執行實現了 Ordered接口的類中的 postProcessBeanFactory的方法
    3. 最後執行沒有實現上面兩個接口的類中的 postProcessBeanFactory的方法
往期精選
Spring官網閱讀(一)容器及實例化
Spring官網閱讀(二)依賴注入及方法注入
Spring官網閱讀(三)自動注入
Spring官網閱讀(四)BeanDefinition 上
Spring官網閱讀(五)BeanDefinition 下
您點的每一個贊,我都認真當成了喜歡

本文分享自微信公衆號 - 程序員DMZ(programerDmz)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索