詳解Spring的ImportSelector接口(2)

寫在最前

上篇文章 - 詳解Spring的ImportSelector接口(1) http://www.lxweimin.com/p/aa99a303bc37中,我們最后留下了兩個疑問。今天我們先說說第一個疑問 - ImportSelector是如何被Spring框架調用的呢?

ImportSelector是如何被Spring框架調用的呢?

ClassPathXmlApplicationContext

我們以ClassPathXmlApplicationContext為例子,看看Spring在啟動過程中是如何調用到ImportSelector接口的。

ClassPathXmlApplicationContext的構造器1

如上圖,我們初始化一個ClassPathXmlApplicationContext時需要指定一個xml文件作為應用上下文。該構造器中又調用了另一個構造器,我們跟進去看下:


ClassPathXmlApplicationContext的構造器2

從上面的代碼中,我們可以看到在設置完configLocations后調用了refresh()方法。那么,refresh()方法又是干什么的呢?

AbstractApplicationContext的refresh()方法

refresh()定義在ConfigurableApplicationContext接口中,具體的實現在AbstractApplicationContext中,如下:

AbstractApplicationContext.refresh()

從上面的實現中,我們可以看到該方法做了很多準備、初始化、事件通知等工作。具體的我們就不一一看了,今天我們只關心其中的invokeBeanFactoryPostProcessors方法。

AbstractApplicationContext.invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory)

從上面的代碼中我們可以看到,invokeBeanFactoryPostProcessors()方法把具體的調用方法委托給了PostProcessorRegistrationDelegate類。另外,從該方法的名字當中我們也可以看出,該方法主要的目的是:實例化,并調用BeanFactoryPostProcessor類的。BeanFactoryPostProcessor是一個接口,有很多的實現類,這里我們就暫且不管BeanFactoryPostProcessor的具體用處了。我們先進入PostProcessorRegistrationDelegate一窺究竟。

PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(xxx)方法

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

        // Invoke BeanDefinitionRegistryPostProcessors first, if any.
        // 如果有BeanDefinitionRegistryPostProcessors 的話,就先調用它們
        // 處理過的Beans
        Set<String> processedBeans = new HashSet<String>();
        // 是否是BeanDefinitionRegistry類型的BeanFactory. BeanDefinitionRegistry的作用是可以用來注冊,刪除,獲取BeanDefinitions
        if (beanFactory instanceof BeanDefinitionRegistry) {
            BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
        // 普通類型的BeanFactoryPostProcessor集合
            List<BeanFactoryPostProcessor> regularPostProcessors = new LinkedList<BeanFactoryPostProcessor>();
        // BeanDefinitionRegistry類型的BeanFactoryPostProcessor集合(BeanDefinitionRegistryPostProcessor繼承于BeanFactoryPostProcessor)
            List<BeanDefinitionRegistryPostProcessor> registryPostProcessors =
                    new LinkedList<BeanDefinitionRegistryPostProcessor>();
                        
        // 對集合beanFactoryPostProcessors進行分類,如果是BeanDefinitionRegistry類型的BeanFactoryPostProcessor;則調用方法 - postProcessBeanDefinitionRegistry()。、、
        //  postProcessBeanDefinitionRegistry(BeanDefinitionRegistry)該方法的作用是在標準的BeanDefinitions初始化完成后可以修改容器上下文內部的beanDefinition。
            for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
                if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
                    BeanDefinitionRegistryPostProcessor registryPostProcessor =
                            (BeanDefinitionRegistryPostProcessor) postProcessor;
                    registryPostProcessor.postProcessBeanDefinitionRegistry(registry);
                    registryPostProcessors.add(registryPostProcessor);
                }
                else {
                    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.
            // 這里不要初始化FactoryBeans,我們需要保留這些普通的beans 不在這里初始化,目的是為了讓bean factory post-processor去處理他們。
           // 根據BeanDefinitionRegistryPostProcessors 實現的不同接口,拆分開來去調用他們。
            String[] postProcessorNames =
                    beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);

            // First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
            // 首先,調用實現了優先級接口 - PriorityOrdered的BeanDefinitionRegistryPostProcessors 
            List<BeanDefinitionRegistryPostProcessor> priorityOrderedPostProcessors = new ArrayList<BeanDefinitionRegistryPostProcessor>();
            for (String ppName : postProcessorNames) {
                if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
                    priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                    processedBeans.add(ppName);
                }
            }
             // 排序
            sortPostProcessors(beanFactory, priorityOrderedPostProcessors);
            // 添加排序好的優先級PostProcessors到registryPostProcessors集合
            registryPostProcessors.addAll(priorityOrderedPostProcessors);
            // 調用排序好的優先級BeanDefinitionRegistryPostProcessors,postProcessBeanDefinitionRegistry方法會被調用,用來修改,獲取,刪除容器上下文中的bean定義。
            invokeBeanDefinitionRegistryPostProcessors(priorityOrderedPostProcessors, registry);

            // Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered.
            // 調用實現了Ordered接口的BeanDefinitionRegistryPostProcessors ,大致邏輯同上PriorityOrdered的處理
            postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
            List<BeanDefinitionRegistryPostProcessor> orderedPostProcessors = new ArrayList<BeanDefinitionRegistryPostProcessor>();
            for (String ppName : postProcessorNames) {
                if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
                    orderedPostProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                    processedBeans.add(ppName);
                }
            }
            sortPostProcessors(beanFactory, orderedPostProcessors);
            registryPostProcessors.addAll(orderedPostProcessors);
            invokeBeanDefinitionRegistryPostProcessors(orderedPostProcessors, registry);

            // Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear.
            // 調用所有其它類型的BeanDefinitionRegistryPostProcessors
            boolean reiterate = true;
            while (reiterate) {
                reiterate = false;
                postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
                for (String ppName : postProcessorNames) {
                    if (!processedBeans.contains(ppName)) {
                        BeanDefinitionRegistryPostProcessor pp = beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class);
                        registryPostProcessors.add(pp);
                        processedBeans.add(ppName);
                        pp.postProcessBeanDefinitionRegistry(registry);
                        reiterate = true;
                    }
                }
            }

            // Now, invoke the postProcessBeanFactory callback of all processors handled so far.
            // 調用所有的BeanDefinitionRegistryPostProcessor的postProcessBeanFactory 方法。
            invokeBeanFactoryPostProcessors(registryPostProcessors, beanFactory);
           // 調用容器中BeanFactoryPostProcessor類型的bean(不包含BeanDefinitionRegistryPostProcessor類型的beans)。
            invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
        }

        else {
            // Invoke factory processors registered with the context instance.
            // 若beanFactory不是BeanDefinitionRegistry類型的,則直接調用容器中所有的BeanFactoryPostProcessor的postProcessBeanFactory 方法。
            invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
        }

          // 下面是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<BeanFactoryPostProcessor>();
        List<String> orderedPostProcessorNames = new ArrayList<String>();
        List<String> nonOrderedPostProcessorNames = new ArrayList<String>();
        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(beanFactory, priorityOrderedPostProcessors);
        invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);

        // Next, invoke the BeanFactoryPostProcessors that implement Ordered.
        List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>();
        for (String postProcessorName : orderedPostProcessorNames) {
            orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
        }
        sortPostProcessors(beanFactory, orderedPostProcessors);
        invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);

        // Finally, invoke all other BeanFactoryPostProcessors.
        List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>();
        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();
    }

上面的調用關系中,我們可以看出。在這個方法中有兩種類型的PostPorcessor被調用處理了;而這兩種PostProcessor的關系如下圖:


BeanDefinitionRegistryPostProcessor

BeanDefinitionRegistryPostProcessor繼承于BeanFactoryPostProcessor。我們大致看看接口定義的方法:

BeanFactoryPostProcessor

postProcessBeanFactory用于在標準的初始化完成后修改容器上下文中的beanFactory。所有bean定義將被加載,但是它們將暫時不被實例化,這允許覆蓋,甚至添加一些屬性到延遲初始化的bean上。

BeanDefinitionRegistryPostProcessor

postProcessBeanDefinitionRegistry方法在標準化初始化完成后可以修改容器內部的bean definition registry。所有的常規的bean定義都將被加載,但是沒有bean被實例化。這允許我們在下一個處理階段增加一些新的bean定義。

綜合上面的邏輯,postProcessBeanDefinitionRegistry是在postProcessBeanFactory方法之前被調用的。

ConfigurationClassPostProcessor

上面說了這么多,目的是為了說明我們的主角 - ConfigurationClassPostProcessor。ConfigurationClassPostProcessor類實現了BeanDefinitionRegistryPostProcessor接口,同時還實現了一些其他的接口,如下圖:

ConfigurationClassPostProcessor

ConfigurationClassPostProcessor類實現了父類的postProcessBeanDefinitionRegistry和postProcessBeanFactory方法,如下圖:


ConfigurationClassPostProcessor實現父類方法

由上面的分析可知,postProcessBeanDefinitionRegistry方法的調用早于postProcessBeanFactory方法。所有我們先來看看postProcessBeanDefinitionRegistry方法的具體內容:

postProcessBeanDefinitionRegistry

從注釋中我們可以看到,這個方法的主要作用是進一步從配置中獲取bean的定義,也就是被@Configuration注解修飾的配置類。該方法中又調用了processConfigBeanDefinitions()方法,該方法主要用于處理@Configuration注解。

在processConfigBeanDefinitions方法中又調用了ConfigurationClassParser類的parse方法用于解析@Configuration,如下圖:


image.png

我們在進入到ConfigurationClassParser類的parse方法看看,如下:


ConfigurationClassParser.parse()

看到這里,我們終于看見曙光了,終于看見DeferredImportSelector接口了。我們忽略方法中其它部分代碼,直接進入到processDeferredImportSelectors()方法一窺究竟。


processDeferredImportSelectors

processDeferredImportSelectors方法中調用了processImports方法執行導入,跟進去看看,在processImports 方法中有很重要的一段代碼,如下:

processImports

如上圖中代碼所示:

  1. 檢查類型是否是ImportSelector,如果是則執行第2步。
  2. 如果同時實現了Aware接口和ImportSelector接口,則先調用Aware接口的實現方法;然后進入第三步。
  3. 調用selector的selectImpors方法,把要導入的類的元數據信息作為參數,根據具體的實現邏輯返回要導入的classes。
  4. 執行后續的代碼...

說到這里,我們可以看到ImportSelector接口的實現方法selectImports終于被調用成功了。

總結

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

推薦閱讀更多精彩內容

  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,786評論 18 139
  • Spring Boot 參考指南 介紹 轉載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,899評論 6 342
  • 文章作者:Tyan博客:noahsnail.com 3.4 Dependencies A typical ente...
    SnailTyan閱讀 4,187評論 2 7
  • 殘雪消融,院中春意漸濃。 早間折下的花枝在修長十指的擺弄下被修剪成長長短短的形狀插進白瓷的花瓶中,盛放的,半開的,...
    九重桜閱讀 319評論 0 0
  • 今天讀《奇特的一生》最后四章 思考:我們對時間的感知? 幼小的孩子對時間的流逝感覺很遲鈍;我們對時間的感覺隨著年...
    梁木純閱讀 182評論 2 1