DeferredImportSelector的處理


一、Spring處理配置類大致過程

回憶一下Spring處理配置類的大致過程【ConfigurationClassPostProcessor

【BeanFactoryPostProcessor -> BeanDefinitionRegistryPostProcessor】
ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry
    -> ConfigurationClassPostProcessor#processConfigBeanDefinitions
        -> ConfigurationClassParser#parse(Set<BeanDefinitionHolder>) 開始解析配置傳入的配置類
        
        
ConfigurationClassParser#parse(Set<BeanDefinitionHolder>)
    - #parse(AnnotationMetadata, String) [最后調用 deferredImportSelectorHandler.process]
        - #processConfigurationClass [@Import導入的非三個特殊接口的類遞歸調用此方法處理], [調用此方法處理的配置類都放入 configurationClasses 字段]
            - #doProcessConfigurationClass 
                - #processImports
        
@Component掃描出來的遞歸調用ConfigurationClassParser#parse(String, String)處理
    - #processConfigurationClass 進而調用它, 回到上面的步驟

 

 

 二、@Import大致處理過程

直接看@Import的處理過程

    protected final SourceClass doProcessConfigurationClass(
            ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
            throws IOException {

        // 使用了@Component注解, ...
        // Process any @PropertySource annotations, ....
        // Process any @ComponentScan annotations, ...
        
        // Process any 【@Import】 annotations
        processImports(configClass, sourceClass, getImports(sourceClass), filter, true);

        // Process any @ImportResource annotations, ...

        // Process individual @Bean methods
        // 處理@Bean,獲取所有 @Bean 方法,還沒有到代理的邏輯, ....

        // 處理繼承的接口中的使用@Bean修飾的deafult方法
        processInterfaces(configClass, sourceClass);

        // Process superclass, if any, ...
        // 沒有父類
        return null;
    }
    
    private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
            Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
            boolean checkForCircularImports) {

        // 沒有Import任何東西,則僅表示此類是一個配置類
        if (importCandidates.isEmpty()) {
            return;
        }

        // 循環注入的檢查
        if (checkForCircularImports && isChainedImportOnStack(configClass)) {
            // 報錯
            this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
        }
        else {
            // importStack 好像是整個解析過程中全局的, importStack 干什么的呢
            this.importStack.push(configClass);
            try {
                // 遍歷所有被@Import引入的類
                for (SourceClass candidate : importCandidates) {
                    // 如果繼承了【ImportSelector】接口
                    if (candidate.isAssignable(ImportSelector.class)) {
                        // Candidate class is an ImportSelector -> delegate to it to determine imports
                        // 讓這個被Import引入的類決定實際引入那些類
                        Class<?> candidateClass = candidate.loadClass();
                        // 實例化
                        ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
                                this.environment, this.resourceLoader, this.registry);
                        Predicate<String> selectorFilter = selector.getExclusionFilter();
                        if (selectorFilter != null) {
                            exclusionFilter = exclusionFilter.or(selectorFilter);
                        }

                        // 如果是繼承了 DeferredImportSelector 接口
                        if (selector instanceof DeferredImportSelector) {
                            // 延遲引入,在所有的@Configuration處理完畢后處理,在作為@Conditional條件@Import時特別有用
                            // 這個應該不可以繼續@Import
                            this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
                        }
                        else {
                            // 返回的是字符串全類名
                            String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
                            Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
                            // 被當作直接@Import的數組再處理,也即可以繼續@Import
                            processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
                        }
                    }

                    // 如果繼承ImportBeanDefinitionRegistrar接口,主要是作為在處理@Configuration的過程中添加BD,需要在BD級別自定義一個Bean時有用
                    else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
                        // Candidate class is an ImportBeanDefinitionRegistrar ->
                        // delegate to it to register additional bean definitions
                        // 由@Import的實例自行定義BD,上面的兩個都是由Spring幫助定義BD
                        Class<?> candidateClass = candidate.loadClass();
                        ImportBeanDefinitionRegistrar registrar =
                                ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
                                        this.environment, this.resourceLoader, this.registry);
                        // 沒有注入BD,什么時候調用
                        // 放入了ConfigClass中
                        configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
                    }
                    else {
                        // Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
                        // process it as an @Configuration class
                        // 其他被@Import的類,被當作配置類進行處理
                        // 注意這里和掃描出來的有區別,掃描出來的是立即注入BD,然后如果是配置類再解析
                        // 而@Import暫時不注入,當作配置類解析,但是 processConfigurationClass 調用后被放在字段 configurationClasses 中
                        this.importStack.registerImport(
                                currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
                        processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
                    }
                }
            }
            catch (BeanDefinitionStoreException ex) {
                throw ex;
            }
            catch (Throwable ex) {
                throw new BeanDefinitionStoreException(
                        "Failed to process import candidates for configuration class [" +
                        configClass.getMetadata().getClassName() + "]", ex);
            }
            finally {
                this.importStack.pop();
            }
        }
    }

@Import區分了三種類

  1. 繼承了ImportSelector接口的類,直接實例化這個類,調用selectImports將返回的類全限定名數組當做配置類處理,注意這個類不被注入容器
  2. 繼承了DeferredImportSelector接口(ImportSelector子接口)的類,暫時未處理,可以看到后面處理后也是講返回的類全部調用processImports當做被直接@Import的處理
  3. 繼承了ImportBeanDefinitionRegistrar接口的類,暫時未處理,【用於自定義BD注入】
  4. 普通類,當做配置類進行解析

 

 三、DeferredImportSelector接口處理

DeferredImportSelector接口的處理

注意前面processImports時已經將@Import進入的DeferredImportSelector接口的類this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector)添加了

// deferredImportSelectorHandler 為 ConfigurationClassParser 的全局字段
// 實際上就是存儲起來, 暫時不調用
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);

private class DeferredImportSelectorHandler {
    private List<DeferredImportSelectorHolder> deferredImportSelectors = new ArrayList<>();

    // @Import 遇到 DeferredImportSelectorHolder 先不處理, 調用它存儲起來
    public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) {
        // 包裝 configClass 和 importSelector 實例, 前者配置類, 后者被配置類 @Import 的 DeferredImportSelector
        DeferredImportSelectorHolder holder = new DeferredImportSelectorHolder(configClass, importSelector);
        // process后才為null,process后的其他直接進行處理
        // 什么時候會為 null ? 為 null 的時候應該是見一個立即處理一個, 否則存儲起來等待一起被調用處理
        if (this.deferredImportSelectors == null) {
            DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
            handler.register(holder);
            handler.processGroupImports();
        }
        else {
            // List#add
            this.deferredImportSelectors.add(holder);
        }
    }
}

DeferredImportSelectorHolder的定義較為簡單,僅封裝兩個字段,相當於Pair,就不列出來了

接着等待被處理,什么時候處理呢?看最開始列出的流程,在剛進入的第一個方法parse最后處理,此時parse的這些配置類已被處理完畢,【這些配置類僅是ConfigurationClassPostProcessor第一次找到的所有配置類,待續】

ConfigurationClassPostProcessor#processConfigBeanDefinitions
    --> 
    public void ConfigurationClassParser.parse(Set<BeanDefinitionHolder> configCandidates) {
        // 內部都是配置類,都需要進行解析
        for (BeanDefinitionHolder holder : configCandidates) {
            BeanDefinition bd = holder.getBeanDefinition();
            try {
                // 注解BD,只看這個即可
                if (bd instanceof AnnotatedBeanDefinition) {
                    parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
                }
                // ...
            }
        }

        // 這里處理 DeferredImportSelector
        this.deferredImportSelectorHandler.process();
    }
DeferredImportSelectorHandler的其余代碼,前面注冊的handle已列出
class ConfigurationClassParser{
    
    private class DeferredImportSelectorHandler {

        @Nullable
        private List<DeferredImportSelectorHolder> deferredImportSelectors = new ArrayList<>();

        public void process() {
            // 所有的 DeferredImportSelector
            List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
            // 清空, handler的那個null判斷處理難道是防止多線程問題???
            this.deferredImportSelectors = null;
            try {
                if (deferredImports != null) {
                    DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
                    // DeferredImportSelectorHolder 排序
                    deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
                    // 遍歷, 調用 DeferredImportSelectorGroupingHandler#register 處理
                    // 注冊, 對每一個進行注冊
                    deferredImports.forEach(handler::register);
                    // 實際處理, 批量處理
                    handler.processGroupImports();
                }
            }
            finally {
                this.deferredImportSelectors = new ArrayList<>();
            }
        }
    }
    
    private class DeferredImportSelectorGroupingHandler {

        /**
         * 鍵 DeferredImportSelector.Group(可能返回null, null則后面) 或 DeferredImportSelector
         * 值:DeferredImportSelectorGrouping
         */
        private final Map<Object, DeferredImportSelectorGrouping> groupings = new LinkedHashMap<>();

        private final Map<AnnotationMetadata, ConfigurationClass> configurationClasses = new HashMap<>();

        // 處理 DeferredImportSelector
        // 這里僅注冊
        public void register(DeferredImportSelectorHolder deferredImport) {
            // DeferredImportSelectorHolder#getImportSelector 得到 DeferredImportSelectorHolder
            // 然后 DeferredImportSelectorHolder#getImportGroup, 注意是一個 Class
            Class<? extends Group> group = deferredImport.getImportSelector().getImportGroup();
            // 鍵: DeferredImportSelector.Group 或 為null時使用 DeferredImportSelector
            // 值: DeferredImportSelectorGrouping
            DeferredImportSelectorGrouping grouping = this.groupings.computeIfAbsent(
                    (group != null ? group : deferredImport),
                    // createGroup 創建Group實現類對象並對部分Aware接口進行了回調, 當為null使用 DefaultDeferredImportSelectorGroup
                    key -> new DeferredImportSelectorGrouping(createGroup(group)));
            // 所有 DeferredImportSelectorHolder
            // grouping 然后添加 DeferredImportSelectorHolder 實例, 可綁定多個
            // 相當於 Map<Group, List>
            grouping.add(deferredImport);
            // 所有configuration
            this.configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(),
                    deferredImport.getConfigurationClass());
        }

        // 實際處理
        public void processGroupImports() {
            // 相當於 grouping 為一個 List<DeferredImportSelectorGrouping>, 每個DeferredImportSelectorGrouping含多個Group實例
            for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
                Predicate<String> exclusionFilter = grouping.getCandidateFilter();
                // DeferredImportSelectorGrouping#getImports 會使用一個 Group 實例對多個 DeferredImportSelector 進行處理
                //     -- 先 forEach -> Group#process(DeferredImportSelector) 一般對每一個DeferredImportSelector能得到此Selector要注入的信息, Group會自己緩存起來
                //  -- 再 Group#selectImports 總的處理, 返回 Iterable<Entry>, Entry保存着要導入的類和導入此類的配置類的注解信息
                grouping.getImports().forEach(entry -> {
                    // 對每一個 Entry 進行處理
                    // 得到配置類實例
                    ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata());
                    try {
                        // !!! 再次processImports, 也就是把要注入的這個類當做 configurationClass 使用 @Import 注入時處理 @Import
                        // 因此還會對這個注入的類進行 @Import 接口處理、配置處理等等
                        // 不過此時的這個被導入的類使用 DeferredImportSelector、ImportBeanDefinitionRegistrar 接口還有作用嗎
                        // 倒是可以導入ImportSelector接口的和其他類型配置類(因為會被當做配置類解析一遍)
                        processImports(configurationClass, asSourceClass(configurationClass, exclusionFilter),
                                Collections.singleton(asSourceClass(entry.getImportClassName(), exclusionFilter)),
                                exclusionFilter, false);
                    }
                    catch (BeanDefinitionStoreException ex) {
                        throw ex;
                    }
                    catch (Throwable ex) {
                        throw new BeanDefinitionStoreException(
                                "Failed to process import candidates for configuration class [" +
                                        configurationClass.getMetadata().getClassName() + "]", ex);
                    }
                });
            }
        }

        /**
         * 創建對象,對部分Aware接口進行了回調,應該是還沒到Bean生命周期處理周期就需要使用到
         */
        private Group createGroup(@Nullable Class<? extends Group> type) {
            Class<? extends Group> effectiveType = (type != null ? type : DefaultDeferredImportSelectorGroup.class);
            return ParserStrategyUtils.instantiateClass(effectiveType, Group.class,
                    ConfigurationClassParser.this.environment,
                    ConfigurationClassParser.this.resourceLoader,
                    ConfigurationClassParser.this.registry);
        }
    }
    
}

 

  1. 一般來說在ConfigurationClassParser#parse的結尾調用deferredImportSelectorHandler.process統一處理此次解析得到的所有DeferredImportSelector
    1. 在處理@Import過程中區分導入的類是否繼承接口,繼承了則將其通過this.deferredImportSelectorHandler.handle添加進入字段deferredImportSelectorHandler中,可認為其是一個List
    2. 注意:上面的添加是添加的實例,在實例化后會回調部分Aware接口
    3. DeferredImportSelectorHandler#handle接受導入這個類的配置類Class和被導入這個類,封裝為DeferredImportSelectorHolder,添加進入List中
  2. DeferredImportSelectorHandler#process處理
    1. 新建一個DeferredImportSelectorGroupingHandler類,將所有DeferredImportSelectorHolder注冊進入其中
    2. 注冊大概干了什么呢?DeferredImportSelectorGroupingHandler可看做一個Map<Group, List<DeferredImportSelectorHolder>>,會調用DeferredImportSelector實例獲取處理它的Group類型,並實例化,Group對象作為k,DeferredImportSelectorHolder實例作為v
    3. 全部注冊后就是批量處理了,因為全部注入了,只需調用DeferredImportSelectorGroupingHandler#processGroupImports
    4. 處理又干了什么呢?遍歷Group並處理能處理的DeferredImportSelector實例列表
      1. Group#process會處理DeferredImportSelector實例得到它要注入的類,並緩存
      2. 當所有能處理的處理完畢,Group#selectImports得到所有當前Group處理的要注入的類列表
      3. 將這個列表processImport,也就是當做被配置類@Import解析一遍
  3. 注意
    1. 被注入的數據和直接被@Import差不多,不過延遲注入了,基本上大多數和啟動類相關Bean都被解析了才會注入BD,也和ImportSelector接口的差不多,不過延遲解析了

 

 

 四、SpringBoot自動配置原理

 

自動配置 @EnableAutoConfiguration 

@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration { }

@Import導入了AutoConfigurationImportSelector這個類,而這個類是一個DeferredImportSelector實現類

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
        ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {}

可以看到他實現了各種接口,在@Import處理實例化這個類時,會調用部分Aware注入一些已有的實例

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
        ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {}
        
        
    // 返回處理當前 DeferredImportSelector 的 Group 類型
    @Override
    public Class<? extends Group> getImportGroup() {
        return AutoConfigurationGroup.class;
    }
    
    // AutoConfigurationImportSelector 的內部類
    // 也繼承了許多接口, 創建當前 Group 實例時也會回調這些 Aware 接口
    private static class AutoConfigurationGroup
            implements DeferredImportSelector.Group, BeanClassLoaderAware, BeanFactoryAware, ResourceLoaderAware {

        private final Map<String, AnnotationMetadata> entries = new LinkedHashMap<>();

        private final List<AutoConfigurationEntry> autoConfigurationEntries = new ArrayList<>();

        private ClassLoader beanClassLoader;

        private BeanFactory beanFactory;

        private ResourceLoader resourceLoader;

        private AutoConfigurationMetadata autoConfigurationMetadata;

        // 接口回調 setter


        // 1. 會先調用 process
        //   --> 會使用一個 Group 實例處理它能處理的一系列 DeferredImportSelector 實例
        //     --> 每一個 DeferredImportSelector 實例都可以注入數據, 現在還未注入, 僅緩存起來
        @Override
        public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
            // 只處理 AutoConfigurationImportSelector
            // 說明這個 Group 僅處理這個類型的實例, 其他類型的 DeferredImportSelector 不處理
            Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
                    () -> String.format("Only %s implementations are supported, got %s",
                            AutoConfigurationImportSelector.class.getSimpleName(),
                            deferredImportSelector.getClass().getName()));
            // AutoConfigurationEntry 包裝了要注入的自動配置類和排除了的配置類
            AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
                    .getAutoConfigurationEntry(annotationMetadata);
            this.autoConfigurationEntries.add(autoConfigurationEntry);
            // 遍歷需要注入的配置類
            for (String importClassName : autoConfigurationEntry.getConfigurations()) {
                // 配置類全類名 -> 導入此配置類的類的注解信息
                this.entries.putIfAbsent(importClassName, annotationMetadata);
            }
            
            // 注意上面只是將要注入的數據緩存在了當前 Group 實例中
        }

        // 2. 調用它, 返回處理了的所有 DeferredImportSelector 實例要注入的數據
        @Override
        public Iterable<Entry> selectImports() {
            if (this.autoConfigurationEntries.isEmpty()) {
                return Collections.emptyList();
            }
            // 移除的配置類 Set
            Set<String> allExclusions = this.autoConfigurationEntries.stream()
                    .map(AutoConfigurationEntry::getExclusions).flatMap(Collection::stream).collect(Collectors.toSet());
            // 要注入的配置類 Set
            Set<String> processedConfigurations = this.autoConfigurationEntries.stream()
                    .map(AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream)
                    .collect(Collectors.toCollection(LinkedHashSet::new));
            // emm
            processedConfigurations.removeAll(allExclusions);

            // 排序
            return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream()
                    .map((importClassName) ->
                            // 導入此配置類的類的注解信息為 + 當前被自動注入的類
                            new Entry(this.entries.get(importClassName), importClassName))
                    .collect(Collectors.toList());
        }

        // ...

    }
    
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM