Spring原始碼解讀之BeanFactoryPostProcessor的處理

張曾經發表於2019-06-29

前言

    前段時間旁聽了某課堂兩節Spring原始碼解析課,剛好最近自己又在重新學習中,便在這裡記錄一下學習所得。我之前寫過一篇博文,是介紹BeanFactoryPostProcessor跟BeanPostProcessor是如何發揮作用的,當時覺得講的還行,但是現在看來,太粗劣了,很多地方沒涉及到,而且重點都被我忽略了,簡直就是蠢得不行。現在就用這篇文章彌補一下前文中對BeanFactoryPostProcessor的講解,爭取把重點講到,至於BeanPostProcessor,由於涉及到的東西太多,限於本人目前的水平只能作罷,待後面感悟成熟了再來補充。

我們以AnnotationConfigApplicationContext為例來構建測試類,先附上此次打斷點除錯的三個簡約到極致的測試類:

1 public class SpringTest {
2 
3     public static void main(String[] args) {
4         // 從這兩行程式碼,實地跟蹤考察Spring中的流程
5         AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ScanConfig.class);
6         applicationContext.getBean(Teacher.class).teach();
7     }
8 }
 1 package myPackage;
 2 import org.springframework.stereotype.Service;
 3 
 4 @Service
 5 public class Teacher {
 6     public Teacher () {
 7         System.out.println("Teacher init");
 8     }
 9     public void teach () {
10         System.out.println("teach");
11     }
12 }
1 package myPackage;
2 import org.springframework.context.annotation.ComponentScan;
3 
4 @ComponentScan("myPackage")
5 public class ScanConfig {
6 }

1、洞悉啟動容器時的準備工作

熟悉一些Spring的道友應該都知道,refresh方法中的invokeBeanFactoryPostProcessors方法實現了對BeanFactoryPostProcessor實現類的處理。大家如果只看invokeBeanFactoryPostProcessors方法的話,不會發現有何異常之處,此方法雖然較長,但是處理邏輯很清晰,先對重寫了BeanFactoryPostProcessor的子介面BeanDefinitionRegistryPostProcessor方法的實現類進行處理,後對重寫了BeanFactoryPostProcessor的方法的實現類做了處理。但是如果心細的話,你會發現問題,Spring是如何將@ComponentScan("myPackage")註解發揮作用的?這時帶著這樣的問題,我們再回過頭來看容器的構造方法,就會在這平實的表面下發現意想不到的 "殺機"。

1 public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
2         this();
3         register(annotatedClasses);
4         refresh();
5     }

通過這個構造方法可以知道,在第二行將我們的測試類ScanConfig 註冊進了容器中,但這只是註冊,註冊之後是如何呼叫如何實現了@ComponentScan("myPackage")這個註解的包掃描的呢?這時我們將目光鎖定this()方法。點進去後發現是這樣的:

1 public AnnotationConfigApplicationContext() {
2         this.reader = new AnnotatedBeanDefinitionReader(this);
3         this.scanner = new ClassPathBeanDefinitionScanner(this);
4     }

在第二行新建reader物件時,呼叫了這個構造方法:

1 public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
2         Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
3         Assert.notNull(environment, "Environment must not be null");
4         this.registry = registry;
5         this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
6         AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
7     }

其中的第六行,最終呼叫了AnnotationConfigUtils#registerAnnotationConfigProcessors方法,而就是在這個方法中完成了對多個重要Bean的註冊,跟我們關係比較大的有以下幾個:

 1         // BeanDefinitionHolder只是存放BD的,裡面有三個屬性:BD物件、beanName以及別名組成的String[]
 2         Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<BeanDefinitionHolder>(4);
 3         // 註冊最關鍵的類,對應的類為ConfigurationClassPostProcessor,父類的父類是BeanFactoryPostProcessor
 4         if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
 5             RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
 6             def.setSource(source);
 7             // 將BD注入進容器中,沒經過什麼處理,只是放入了DefaultListableBeanFactory中的beanDefinitionMap跟存放beanName的list中
 8             beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
 9         }
10         // 此類實現了BeanPostProcessor,用於處理@Autowired、@Value註解
11         if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
12             RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
13             def.setSource(source);
14             beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
15         }
16         // 此類也實現了BeanPostProcessor,用於處理有@Required註解的方法
17         if (!registry.containsBeanDefinition(REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
18             RootBeanDefinition def = new RootBeanDefinition(RequiredAnnotationBeanPostProcessor.class);
19             def.setSource(source);
20             beanDefs.add(registerPostProcessor(registry, def, REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
21         }

其中第一個對應的類就是我們重點關注的物件 ConfigurationClassPostProcessor類,檢視此類的組成,發現它實現了BeanDefinitionRegistryPostProcessor介面,而此介面正是BeanFactoryPostProcessor的子介面。此時,縈繞在我們心頭的迷霧開始漸漸散開,我們彷彿能抓到一閃而過的邏輯走向,現在讓我們帶著之前的發現,進入正主invokeBeanFactoryPostProcessors方法中一探究竟。

2、invokeBeanFactoryPostProcessors

該方法位於AbstractApplicationContext的refresh方法中,如下所示:

 1 public void refresh() throws BeansException, IllegalStateException {
 2         synchronized (this.startupShutdownMonitor) {
 3             // Prepare this context for refreshing.
 4             prepareRefresh();
 5 
 6             // Tell the subclass to refresh the internal bean factory.
 7             ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
 8 
 9             // Prepare the bean factory for use in this context.
10             prepareBeanFactory(beanFactory);
11 
12             try {
13                 // Allows post-processing of the bean factory in context subclasses.
14                 postProcessBeanFactory(beanFactory);
15 
16                 // Invoke factory processors registered as beans in the context. 
17                 invokeBeanFactoryPostProcessors(beanFactory);

即第17行呼叫的方法。初學者看到這個方法的內部實現時,會發現此方法無外乎是找到所有實現了BeanDefinitionRegistryPostProcessor跟BeanFactoryPostProcessor介面的類,按照優先順序(實現了PriorityOrdered介面的先於實現了Ordered介面,前兩者均先於未實現的,且同一類中按照getOrder方法返回值排優先順序)順序執行它們的重寫方法,先執行BeanDefinitionRegistryPostProcessor的重寫方法,再執行BeanFactoryPostProcessor的重寫方法,很容易理解的邏輯。但是現在我們是帶著前面準備工作中得到的線索來的,此時再看,就能透過這個方法樸實的外表發現它潛藏的凶險,它如平地一聲雷般讓人猛地驚出一身冷汗。

我們打斷點進入PostProcessorRegistrationDelegate類中的下面方法中

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

先略過開始對手動新增進去的beanFactoryPostProcessors處理邏輯,看後面的部分程式碼(由於此方法程式碼較多,此處就不全部貼上出來了,因為邏輯很好理解,所以只貼上重點):

 1             String[] postProcessorNames =
 2                     beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
 3 
 4             // First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
 5             List<BeanDefinitionRegistryPostProcessor> priorityOrderedPostProcessors = new ArrayList<BeanDefinitionRegistryPostProcessor>();
 6             // 1.2 先處理實現了PriorityOrdered的類
 7             for (String ppName : postProcessorNames) {
 8                 if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
 9                     // 此處通過getBean來生成ConfigurationClassPostProcessor例項物件
10                     priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
11                     processedBeans.add(ppName);
12                 }
13             }
14             // 就一個物件,有啥好排序的
15             sortPostProcessors(beanFactory, priorityOrderedPostProcessors);
16             registryPostProcessors.addAll(priorityOrderedPostProcessors);
17             // 執行ConfigurationClassPostProcessor中的重寫方法postProcessBeanDefinitionRegistry,會將所有加了註解的類註冊到容器中
18             // 此處才是整個invokeBeanFactoryPostProcessors方法的核心所在,需要詳述 下面進入ConfigurationClassPostProcessor中的postProcessBeanDefinitionRegistry中一探究竟
19             invokeBeanDefinitionRegistryPostProcessors(priorityOrderedPostProcessors, registry);

debug到第一行的時候,會發現此處獲取到的postProcessorNames 中只有一個值,就是前面準備工作中通過硬編碼往容器裡註冊的ConfigurationClassPostProcessor類。下面的邏輯就是進行各種判斷,最後在第19行完成了對ConfigurationClassPostProcessor中postProcessBeanDefinitionRegistry方法的呼叫。

就是在這個後置處理方法中,完成了@ComponentScan("myPackage")中對包的掃描,完成了所有Bean的註冊。執行完這個方法後,你會發現beanDefinitionMap中所有應該容器管理的類全都齊活了,包括其他的後置處理器。這樣,後面繼續呼叫beanFactory.getBeanNamesForType方法時,獲取到的是所有滿足條件的類,後面的工作就會有條不紊的開展下去了。

總結

    本文著重追溯了BeanFactoryPostProcessor及其子介面是如何在Spring中發揮作用的。先通過Spring初始化容器時註冊進去的ConfigurationClassPostProcessor類觸發對其他類的掃描,待全部註冊進容器後,再從容器中取對應的BeanFactoryPostProcessor及其子介面的實現類,逐一對重寫方法進行呼叫。

雖然魔鬼在細節,但這也正是解讀原始碼的快樂之處,不是嗎?

相關文章