Spring原始碼:Bean工廠的後置處理器 invokeBeanFactoryPostProcessors

語言: CN / TW / HK

持續創作,加速成長!這是我參與「掘金日新計劃 · 6 月更文挑戰」的第28天,點選檢視活動詳情

在 xml 解析中我們知道了標籤定義的例項通過解析為 Beandefinition 載入到 Spring 容器中,而解析的具體方法就在AbstractApplicationContext#refresh()方法中。而在容器例項化 Bean 之前,可以修改Beandefinition的資訊,比如是否延遲載入、加入一些新的 Bean 的定義資訊等,其具體實現就是 Bean 工廠的後置處理器BeanFactoryPostProcessor,並且通過invokeBeanFactoryPostProcessors()呼叫。

image-20211027104119398

1. Bean工廠的後置處理器

invokeBeanFactoryPostProcessors()主要完成對介面BeanFactoryPostProcessorBeanDefinitionRegistryPostProcessor的呼叫,在例項化之前,用此介面完成對 BeanDefinition 的動態修改

  • BeanFactoryPostProcessor,Bean工廠的後置處理器,用於Bean定義註冊之後bean例項化之前;
  • BeanDefinitionRegistryPostProcessor,用於Bean定義註冊之前。

image-20211027114328239

它的繼承關係如下:

image-20211027115726399

1.1 BeanDefinitionRegistryPostProcessor

該介面是用來向容器中註冊 BeanDefinition 的,BeanDefinition 的數目變多。

它的介面方法postProcessBeanDefinitionRegistry()執行的時機是,所有的“常規bean定義”都已註冊完畢,該方法允許新增進一步的bean定義註冊到容器中。

這裡的“常規bean定義”指的是,在容器refresh前就已經註冊好的bean定義。

1.2 BeanFactoryPostProcessor

該介面是用來修改容器中的BeanDefinition的,BeanDefinition的數目不變。

它的介面方法postProcessBeanFactory()執行的時機是,所有的BeanDefinition都已經註冊完畢,不可能再增多了,該方法允許去修改BeanDefinition的一些屬性。

1.3 手動建立BeanDefinition並新增到容器

常規的,如果要把一個 Bean 載入到 Spring 容器中,可以通過註解或者 XML 配置的方式,但實際上還有另外一種方法,就是手動新增到容器中去,如下面這個類:

java import lombok.Data; ​ @Data public class CustomBeanDefinition { ​    private String name; ​ }

可以看到,類上並沒有添加註解,當然 XML 檔案中也不會配置,如果要把他新增到 Spring 容器中怎麼辦呢?

新建一個類實現BeanDefinitionRegistryPostProcessor,如下:

java import org.springframework.beans.BeansException; import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; import org.springframework.beans.factory.support.GenericBeanDefinition; import org.springframework.stereotype.Component; ​ @Component public class CustomBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor { ​    @Override    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {        //建立一個 BeanDefinition 物件        GenericBeanDefinition beanDefinition = new GenericBeanDefinition();        beanDefinition.setBeanClass(CustomBeanDefinition.class);        MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();        propertyValues.addPropertyValue("name","手動建立BeanDefinition");        //註冊到註冊中心去        registry.registerBeanDefinition("customBeanDefinition",beanDefinition);   } ​    @Override    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {        //TODO 修改屬性   } }

spring.xml 配置如下:

image-20211027162930902

測試類:

java @Test public void test() {    ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");    CustomBeanDefinition customBeanDefinition = applicationContext.getBean(CustomBeanDefinition.class);    System.out.println(customBeanDefinition.getName()); }

測試結果:

image-20211027163208969

2. invokeBeanFactoryPostProcessors

BeanDefinition註冊完畢之後,在refresh()方法中呼叫後置處理器:

image-20211027164113997

可以看到有一個代理類(PostProcessorRegistrationDelegate)專門來負責呼叫後置處理器方法,其中第二個引數就是我們手動新增的後置處理器例項(該方法的主要目的是拿到當前上下文中已經註冊的 BeanFactoryPostProcessor,但在預設情況下是返回空的,一般情況下沒有人新增,所以為空)。

/** BeanFactoryPostProcessors to apply on refresh. */ private final List<BeanFactoryPostProcessor> beanFactoryPostProcessors = new ArrayList<>(); ​ public List<BeanFactoryPostProcessor> getBeanFactoryPostProcessors() {   return this.beanFactoryPostProcessors; }

在進入到代理的回撥方法,由於該方法非常複雜,這裡通過片段的方式來分析,但也會把每一步的程式碼分析貼出來。

2.1 Spring 執行順序

我們知道在 Spring 原始碼中大量的使用了策略模式,也就意味著有很多實現相同介面的實現類,所以在 Spring 容器載入的時候就存在優先順序問題,如 BeanFactoryPostProcessorBeanDefinitionRegistryPostProcessor 的實現類大多數都有存在優先順序的現象。

那麼這些實現類的排序是如何定義的呢?

1、在Spring中,對類的順序的處理是有統一的方案的,就是介面或註解。常見的就是@Order註解,實際上它的頂層是Ordered介面:

image-20211027171934096

需要注意的是,優先順序最高的卻是負數最小值,優先順序最低的卻是正數最大值。即數值越小優先順序越高。

2、其次是 PriorityOrdered 介面,它繼承了Ordered介面,從名字就能看出來該介面的優先順序比 Ordered 介面要高。

image-20211027172154819

3、第三種就是既沒有實現介面也沒有標註解的類。

2.2 後置處理器的具體呼叫

上面為什麼要將類的執行順序,是因為在後置處理器的具體呼叫過程中就是按照上面的順序來處理的,大致的呼叫規則如下:

  1. 先呼叫手動新增的後置處理器,再呼叫作為 BeanDenifition 註冊的後置處理器;
  2. 先呼叫 BeanDefinitionRegistryPostProcessor 後置處理器並呼叫其postProcessBeanDefinitionRegistry()方法,再呼叫 BeanFactoryPostProcessor 後置處理器並呼叫其postProcessBeanFactory()方法;
  3. 先呼叫實現 PriorityOrdered 介面的,再呼叫實現 Ordered 介面的,最後是沒有實現介面的。

按照上面的規則,在結合具體的程式碼,如下:

第一步,先呼叫手動新增的後置處理器:

image-20211027173614045

第二步,呼叫 BeanDefinitionRegistryPostProcessor 後置處理器,也就是配置檔案或者註解載入的類,但呼叫的前提是上面規則的第 3 點,按照實現的排序介面排序呼叫並排序。

先呼叫實現了 PriorityOrdered 介面的 BeanDenifition 後置處理器。

image-20211027191536991

第三步,呼叫實現了 Ordered 介面的 BeanDenifition 後置處理器。

image-20211027192419196

第四步,再通過迴圈呼叫容器中剩餘所有的 BeanDenifition 後置處理器。

image-20211027193030077

此處為什麼要通過迴圈一直呼叫呢?因為這是在註冊 BeanDenifition ,而且註冊的BeanDenifition 可能又是一個BeanDenifition 註冊後置處理器。

這很好理解,就像買飲料遇到再來一瓶一樣的道理。

你買了 10 瓶,全部開啟,有 8 個再來一瓶,老闆又給了你 8 瓶,再全部開啟,有 5 個再來一瓶,老闆再給你 5 瓶,你接著再開啟。

如此反覆,直到沒有遇到再來一瓶為止。

呼叫至此,所有註冊 BeanDenifition 的方法都已經調完,這意味著 BeanDenifition 註冊已經完畢,BeanDenifition 的數目也不會再增多了。

簡單來說就是前面 4 步操作獲取 BeanDefinitionRegistry 物件,如果我們能獲取到這個物件就意味著可以獲取這個物件中註冊的所有 BeanDefinition 物件,我們擁有這個物件就可以完成裡面所有 BeanDefinition 物件的修改和新增操作等等

第五步,呼叫所有 BeanDefinitionRegistry 的 postProcessBeanFactory() 方法,按需對 BeanDefinition 進行修改或完善,執行順序和上面保持一致。

image-20211027213800794

到這裡,入參 beanFactoryPostProcessors 和容器中的所有 BeanDefinitionRegistryPostProcessor 已經全部處理完畢,下一步開始處理容器中的所有 BeanFactoryPostProcessor。

我們看原始碼實際上可以發現,儘管它的程式碼很長,但實際上和前面非常類似,只是呼叫的物件變為了BeanFactoryPostProcessor ,包括優先順序等等。

image-20211027215332643

唯一的不同點就是優先順序直接通過一個迴圈就解決了,而不是每次都從容器中獲取,原因是BeanDefinitionRegistryPostProcessor 處理遠端之後 BeanDenifition 數量不會再變了,從容器中獲取一次即可,一個迴圈就可以按實現的不同排序介面把它們分開。

到這,所有的 BeanDenifition 都已經修改完畢,BeanDenifition 的屬性不會再有任何變化了。

3. 完整程式碼解析

java public static void invokeBeanFactoryPostProcessors( ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) { // Invoke BeanDefinitionRegistryPostProcessors first, if any. // 先呼叫 BeanDefinitionRegistryPostProcessors 介面 Set<String> processedBeans = new HashSet<>(); // 判斷傳入的 beanFactory 是否實現了BeanDefinitionRegistry if (beanFactory instanceof BeanDefinitionRegistry) { // 強轉 BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory; // 用來儲存 BeanFactoryPostProcessor 型別的後置處理器 List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>(); // 儲存 BeanDefinitionRegistryPostProcessor 型別的後置處理器 List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>(); //迴圈傳遞進來的 beanFactoryPostProcessors for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) { //判斷後置處理器是不是BeanDefinitionRegistryPostProcessor if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) { BeanDefinitionRegistryPostProcessor registryProcessor = (BeanDefinitionRegistryPostProcessor) postProcessor; //呼叫它的後置方法 registryProcessor.postProcessBeanDefinitionRegistry(registry); //新增到registryProcessors(用於最後執行postProcessBeanFactory方法) registryProcessors.add(registryProcessor); } else { /** * 若沒有實現BeanDefinitionRegistryPostProcessor介面,那麼他就是 BeanFactoryPostProcessor * 把當前的後置處理器加入到regularPostProcessors中 */ 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. //定義一個集合用於儲存當前準備建立的 BeanDefinitionRegistryPostProcessor List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>(); // First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered. /*------------------------------------------begin--------------------------------------*/ //第一步:去容器中獲取BeanDefinitionRegistryPostProcessor的bean的處理器名稱 String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false); for (String ppName : postProcessorNames) { //判斷是否實現了 PriorityOrdered 排序介面 if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) { currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class)); processedBeans.add(ppName); } } //對currentRegistryProcessors集合中BeanDefinitionRegistryPostProcessor進行排序 sortPostProcessors(currentRegistryProcessors, beanFactory); registryProcessors.addAll(currentRegistryProcessors); /** * 執行 postProcessBeanDefinitionRegistry 方法 * 在該處,將執行 ConfigurationClassPostProcessor,對配置類進行掃描,並註冊BeanDefinition * 用於進行 bean 定義的載入 如我們的包掃描,@import等 */ invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry); currentRegistryProcessors.clear(); // Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered. //接下來,呼叫實現 Ordered 的 BeanDefinitionRegistryPostProcessors postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false); for (String ppName : postProcessorNames) { //判斷是否實現了 Ordered 排序介面 if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) { currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class)); processedBeans.add(ppName); } } //排序 sortPostProcessors(currentRegistryProcessors, beanFactory); //加入到 registryProcessors 中 registryProcessors.addAll(currentRegistryProcessors); //執行 postProcessBeanDefinitionRegistry 方法 invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry); currentRegistryProcessors.clear(); // Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear. boolean reiterate = true; while (reiterate) { reiterate = false; //找出所有實現BeanDefinitionRegistryPostProcessor介面的類 postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false); for (String ppName : postProcessorNames) { //跳過已經執行的 if (!processedBeans.contains(ppName)) { currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class)); processedBeans.add(ppName); /** * 如果有BeanDefinitionRegistryPostProcessor被執行, 則有可能會產生新的BeanDefinitionRegistryPostProcessor * 因此這邊將reiterate賦值為true, 代表需要再迴圈查詢一次 */ reiterate = true; } } //排序 sortPostProcessors(currentRegistryProcessors, beanFactory); //加入到 registryProcessors 中 registryProcessors.addAll(currentRegistryProcessors); //執行 postProcessBeanDefinitionRegistry 方法 invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry); currentRegistryProcessors.clear(); } /*------------------------------------------end--------------------------------------*/ // Now, invoke the postProcessBeanFactory callback of all processors handled so far. /** * 呼叫所有 BeanDefinitionRegistryPostProcessor後置處理器 的 postProcessBeanFactory方法 * 因為BeanDefinitionRegistryPostProcessor 繼承自 BeanFactoryPostProcessor */ invokeBeanFactoryPostProcessors(registryProcessors, beanFactory); /** * 最後, 呼叫入參beanFactoryPostProcessors中的普通BeanFactoryPostProcessor的postProcessBeanFactory方法 */ invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory); } else { // Invoke factory processors registered with the context instance. invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory); } /** * 到這裡,入參 beanFactoryPostProcessors 和容器中的所有 BeanDefinitionRegistryPostProcessor 已經全部處理完畢, * 下一步開始處理容器中的所有 BeanFactoryPostProcessor。 * * 我們看原始碼實際上可以發現,儘管它的程式碼很長,但實際上和前面非常類似, * 只是呼叫的物件變為了BeanFactoryPostProcessor ,包括優先順序等等。 */ // Do not initialize FactoryBeans here: We need to leave all regular beans // uninitialized to let the bean factory post-processors apply to them! String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false); // Separate between BeanFactoryPostProcessors that implement PriorityOrdered, // Ordered, and the rest. List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>(); List<String> orderedPostProcessorNames = new ArrayList<>(); List<String> nonOrderedPostProcessorNames = 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); } else { nonOrderedPostProcessorNames.add(ppName); } } // First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered. sortPostProcessors(priorityOrderedPostProcessors, beanFactory); invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory); // Next, invoke the BeanFactoryPostProcessors that implement Ordered. 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... beanFactory.clearMetadataCache(); }

4. 總結

經過對invokeBeanFactoryPostProcessors()方法原始碼的分析,可以知道該方法主要就是用來對BeanDefinitionRegistryPostProcessorBeanFactoryPostProcessor介面的呼叫,一個用於註冊 BeanDefiition,一個用於對 BeanDefinition 的修改。

總的來說就是獲取 BeanDefinitionRegistry 物件,獲取到這個物件就可以獲取這個物件中註冊的所有 BeanDefinition 物件,擁有這個物件就可以完成裡面所有 BeanDefinition 物件修改操作,而具體需要做什麼,可以按照實際場景去定義。

另外一點需要注意的是 2 個後置處理器的入參 ConfigurableListableBeanFactoryBeanDefinitionRegistry本質上是一樣的,也就是為什麼下面這個判斷會為 true。

image-20211028114514541

他們之間的關係如下:

image-20211028114653245

5. 參考