spring源碼解析------@Import注解解析與ImportSelector镇防,ImportBeanDefinitionRegistrar以及DeferredImportSelector區(qū)別

1.@Import注解在springBoot中間接的廣泛應(yīng)用

?在springboot中并沒有直接顯式的使用@Import標(biāo)簽啦鸣,而是通過@Import標(biāo)簽來間接的提供了很多自動配置的注解。比如@EnableAutoConfiguration来氧,@EnableConfigurationProperties等诫给。這些標(biāo)簽的實現(xiàn)都是通過使用@Import標(biāo)簽來完成的。

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

?可以發(fā)現(xiàn)都是通過@Import來完成的啦扬。

2.spring中的@Import注解

2.1@Import注解的作用

?@Import標(biāo)簽可以導(dǎo)入一個或者多個組件類中狂,通常是@Configuration 注入的bean。提供了與xml中<import/>標(biāo)簽類型的功能扑毡,能夠?qū)?code>@Configuration類胃榕。

public @interface Import {

    /**
     * {@link Configuration @Configuration}, {@link ImportSelector},
     * {@link ImportBeanDefinitionRegistrar}, or regular component classes to import.
     */
    Class<?>[] value();

}

?導(dǎo)入的類可以是@Configuration類型的配置類,實現(xiàn)了ImportSelector接口的類瞄摊,實現(xiàn)了ImportBeanDefinitionRegistrar的類或者常規(guī)的組件類勋又。

2.2@Import的解析前的處理

?@Import處理的位置其實跟@Conditional注解都在同一個類中苦掘,處理的時機也是一樣的。這里可以去看看@Conditional注解解析的邏輯楔壤。

2.2.1 容器的刷新時候的準(zhǔn)備

?在容器刷新方法就定義在AbstractApplicationContext類的refresh方法中鹤啡。在這個里面會有解析注冊以及實例話bean和其他的步驟,我們要看的就是解析跟注冊步驟挺邀。

    public void refresh() throws BeansException, IllegalStateException {
    ......
    invokeBeanFactoryPostProcessors(beanFactory);
    }

?在invokeBeanFactoryPostProcessors方法中會實例化所有的BeanFactoryPostProcessor類型的類并調(diào)用實現(xiàn)的postProcessBeanFactory方法揉忘。

protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
        PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
    ......
}
2.2.2 PostProcessorRegistrationDelegate處理BeanDefinitionRegistry以及BeanFactoryPostProcessor

?這里直接進入到invokeBeanFactoryPostProcessors方法。會有兩種處理方式:

  1. 當(dāng)前的beanFactoryBeanDefinitionRegistry類型的
    (1) 先處理beanFactory端铛,然后按照是否實現(xiàn)了PriorityOrdered,Ordered以及兩個都沒有實現(xiàn)的順序來處理BeanDefinitionRegistryPostProcessor接口的實現(xiàn)類泣矛。
    (2)處理實現(xiàn)了BeanFactoryPostProcessor接口的子類依次調(diào)用實現(xiàn)的postProcessBeanFactory方法
  2. 當(dāng)前的beanFactory不是BeanDefinitionRegistry類型的,則直接處理實現(xiàn)了BeanFactoryPostProcessor接口的子類依次調(diào)用實現(xiàn)的postProcessBeanFactory方法

?其中會處理@Import標(biāo)簽的ConfigurationClassPostProcessor實現(xiàn)類BeanDefinitionRegistryPostProcessor接口跟PriorityOrdered接口間接實現(xiàn)了BeanFactoryPostProcessor接口因此無論怎么樣都會被調(diào)用的禾蚕。

    public static void invokeBeanFactoryPostProcessors(
            ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
        //如果當(dāng)前的beanFactory是BeanDefinitionRegistry的您朽,則需要將已經(jīng)存在的beanDefinition進行注冊
        if (beanFactory instanceof BeanDefinitionRegistry) {
            BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
            List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
            //保存注冊bean的BeanDefinitionRegistryPostProcessor
            List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();
            //迭代beanFactoryPostProcessors如果是BeanDefinitionRegistryPostProcessor子類則加入到registryProcessors集合中
            for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
                if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
                    BeanDefinitionRegistryPostProcessor registryProcessor =
                            (BeanDefinitionRegistryPostProcessor) postProcessor;
                    //處理beanFactory中的beanDefinitionNames
                    registryProcessor.postProcessBeanDefinitionRegistry(registry);
                    registryProcessors.add(registryProcessor);
                }
                else {
                    regularPostProcessors.add(postProcessor);
                }
            }
            List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();
            //先處理同時實現(xiàn)了PriorityOrdered接口的BeanDefinitionRegistryPostProcessor的實現(xiàn)類
            String[] postProcessorNames =
                    beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
            for (String ppName : postProcessorNames) {
                //如果當(dāng)前的BeanDefinitionRegistryPostProcessor類的實現(xiàn)類也實現(xiàn)了PriorityOrdered類,則加入當(dāng)當(dāng)前需要注冊的BeanDefinitionRegistryPostProcessor集合
                if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
                    currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                    processedBeans.add(ppName);
                }
            }
            //進行排序
            sortPostProcessors(currentRegistryProcessors, beanFactory);
            //加入到registryProcessors集合
            registryProcessors.addAll(currentRegistryProcessors);
            //調(diào)用實現(xiàn)了BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法
            invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
            currentRegistryProcessors.clear();
            ......
            invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
            invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
    }else {
            // Invoke factory processors registered with the context instance.
            invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
    }
}
2.2.3 ConfigurationClassPostProcessorprocessConfigBeanDefinitionsprocessConfigBeanDefinitions解析bean

?上面提到了ConfigurationClassPostProcessor無論怎么樣都會被調(diào)用换淆。這里先看看實現(xiàn)的postProcessBeanDefinitionRegistry方法跟postProcessBeanFactory的共同點哗总。

    //在refresh方法中最先被調(diào)用
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
        //給當(dāng)前的BeanDefinitionRegistry類型的beanFactory設(shè)置一個全局hash值,避免重復(fù)處理
        int registryId = System.identityHashCode(registry);
        if (this.registriesPostProcessed.contains(registryId)) {
            throw new IllegalStateException(
                    "postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
        }
        if (this.factoriesPostProcessed.contains(registryId)) {
            throw new IllegalStateException(
                    "postProcessBeanFactory already called on this post-processor against " + registry);
        }
        //加入到已經(jīng)處理的PostProcessed集合中
        this.registriesPostProcessed.add(registryId);
        //進行處理
        processConfigBeanDefinitions(registry);
    }

    /**
     * Prepare the Configuration classes for servicing bean requests at runtime by replacing them with CGLIB-enhanced subclasses.
     */
    //在refresh方法中第二被調(diào)用
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        //給當(dāng)前的ConfigurableListableBeanFactory類型的beanFactory設(shè)置一個全局hash值倍试,避免重復(fù)處理
        int factoryId = System.identityHashCode(beanFactory);
        if (this.factoriesPostProcessed.contains(factoryId)) {
            throw new IllegalStateException(
                    "postProcessBeanFactory already called on this post-processor against " + beanFactory);
        }
        //加入到已經(jīng)處理的集合中
        this.factoriesPostProcessed.add(factoryId);
        //如果當(dāng)前的beanFactory不在registriesPostProcessed中則進行處理
        if (!this.registriesPostProcessed.contains(factoryId)) {
            // BeanDefinitionRegistryPostProcessor hook apparently not supported...
            // Simply call processConfigurationClasses lazily at this point then.
            processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
        }
        //將beanFactory中用Configuration注解配置的bean進行動態(tài)加強
        enhanceConfigurationClasses(beanFactory);
        //增加一個ImportAwareBeanPostProcessor
        beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
    }

?發(fā)現(xiàn)一個共同點就是讯屈,如果沒有解析過的beanFactory進來都會調(diào)用processConfigBeanDefinitions方法來處理Configuration類。在這個方法中有兩個解析的位置县习。

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
//循環(huán)處理知道所有的bean都處理完涮母,包含bean內(nèi)部定義的bean
        do {
            //第一個解析的位置,這里是解析能夠直接獲取的候選配置bean躁愿∨驯荆可能是Component,ComponentScan彤钟,Import来候,ImportResource或者有Bean注解的bean
            parser.parse(candidates);
            parser.validate();
            //獲取上面封裝已經(jīng)解析過的配置bean的ConfigurationClass集合
            Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
            //移除前面已經(jīng)處理過的
            configClasses.removeAll(alreadyParsed);

            // Read the model and create bean definitions based on its content
            if (this.reader == null) {
                this.reader = new ConfigurationClassBeanDefinitionReader(
                        registry, this.sourceExtractor, this.resourceLoader, this.environment,
                        this.importBeanNameGenerator, parser.getImportRegistry());
            }
            //第二個解析的位置,這里是加載configurationClasse中內(nèi)部可能存在配置bean逸雹,比如方法上加了@Bean或者@Configuration標(biāo)簽的bean
            this.reader.loadBeanDefinitions(configClasses);
            ......
            }
            ......
}

?兩個解析的位置分別是:

  1. 第一個位置是ConfigurationClassParser類的parse方法营搅,解析能夠直接從beanFactory中獲取的候選bean。
  2. 第二個是ConfigurationClassBeanDefinitionReaderloadBeanDefinitions方法梆砸,解析從直接獲取的候選bean中內(nèi)部定義的bean剧防。
2.2.4 ConfigurationClassParser的解析過程

?ConfigurationClassParserparse有很多重載的方法,但是內(nèi)部邏輯都是調(diào)用的processConfigurationClass方法辫樱。

    public void parse(Set<BeanDefinitionHolder> configCandidates) {
        for (BeanDefinitionHolder holder : configCandidates) {
            BeanDefinition bd = holder.getBeanDefinition();
            try {
            //解析bean
                if (bd instanceof AnnotatedBeanDefinition) {
                    parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
                }
                else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
                    parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
                }
                else {
                    parse(bd.getBeanClassName(), holder.getBeanName());
                }
            }
            catch (BeanDefinitionStoreException ex) {
                throw ex;
            }
            catch (Throwable ex) {
                throw new BeanDefinitionStoreException(
                        "Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
            }
        }
        //處理DeferredImportSelector類型的實現(xiàn)類
        this.deferredImportSelectorHandler.process();
    }

    protected final void parse(@Nullable String className, String beanName) throws IOException {
        ......
        processConfigurationClass(new ConfigurationClass(reader, beanName));
    }

    protected final void parse(Class<?> clazz, String beanName) throws IOException {
        processConfigurationClass(new ConfigurationClass(clazz, beanName));
    }

    protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
        processConfigurationClass(new ConfigurationClass(metadata, beanName));
    }

?從上面可以看出來峭拘,主要要看的邏輯還是在processConfigurationClass方法中。這里還需要注意一點就是在最上面的parse方法最后有一個邏輯this.deferredImportSelectorHandler.process()這個邏輯是處理@Import注解中指定的引入類是DeferredImportSelectorHandler子類的情況的。
?進入到processConfigurationClass方法鸡挠,關(guān)于這個方法在@Conditional注解解析的邏輯這個里面說到過辉饱。這里只需要關(guān)注內(nèi)部的doProcessConfigurationClass方法的邏輯。

    protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
        //檢查當(dāng)前解析的配置bean是否包含Conditional注解拣展,如果不包含則不需要跳過
        // 如果包含了則進行match方法得到匹配結(jié)果彭沼,如果是符合的并且設(shè)置的配置解析策略是解析階段不需要調(diào)過
        if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
            return;
        }
        //從緩存中嘗試獲取當(dāng)前配置bean解析之后的ConfigurationClass對象
        ConfigurationClass existingClass = this.configurationClasses.get(configClass);
        if (existingClass != null) {
            //檢查當(dāng)前這個配置bean是通過@Import標(biāo)簽引入的還是自動注入到另外一個配置類bean里面的
            if (configClass.isImported()) {
                //如果是通過@Import標(biāo)簽引入則將當(dāng)前解析的配置bean加入到已經(jīng)存在的解析過的bean的用來保存通過@Import標(biāo)簽引入的bean的集合中
                if (existingClass.isImported()) {
                    existingClass.mergeImportedBy(configClass);
                }
                // Otherwise ignore new imported config class; existing non-imported class overrides it.
                return;
            }
            else {
                // Explicit bean definition found, probably replacing an import.
                // Let's remove the old one and go with the new one.
                //將當(dāng)前解析的配置bean代替之前的
                this.configurationClasses.remove(configClass);
                this.knownSuperclasses.values().removeIf(configClass::equals);
            }
        }

        // Recursively process the configuration class and its superclass hierarchy.
        //遞歸獲取原始的配置類信息然后封裝為SourceClass
        SourceClass sourceClass = asSourceClass(configClass);
        do {
            //處理configClass
            sourceClass = doProcessConfigurationClass(configClass, sourceClass);
        }
        while (sourceClass != null);
        //保存到configurationClasses中
        this.configurationClasses.put(configClass, configClass);
    }

?在doProcessConfigurationClass方法中有很多標(biāo)簽的處理邏輯,比如@Component备埃,@PropertySources姓惑,@ComponentScans等。對于@Import注解的處理方式按脚,封裝在了另外的一個方法processImports里面于毙。

    protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
            throws IOException {
                .......
                        processImports(configClass, sourceClass, getImports(sourceClass), true);
                ......
            }
2.3 @Import注解的解析
2.3.1 processImports方法

?已經(jīng)知道@Import注解在那個方法里面進行解析了。先看看對應(yīng)的方法參數(shù)的含義辅搬。

參數(shù) 含義
ConfigurationClass configClass 當(dāng)前需要解析的Configuration類
SourceClass currentSourceClass 類的源數(shù)據(jù)封裝對象
Collection<SourceClass> importCandidates Configuration類引入的其他類唯沮,以及其他類中引入的別的類,比如A引入B堪遂,B引入C介蛉,C引入D。那么這個就包含了B溶褪,C币旧,D
boolean checkForCircularImports 是否檢查循環(huán)引入

?現(xiàn)在對方法內(nèi)部進行分析

    private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
            Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
        //檢查包含有Import注解的集合是不是空的,空的則表示沒有
        if (importCandidates.isEmpty()) {
            return;
        }
        //檢查是否存在循環(huán)引入猿妈,通過deque的方式來檢查
        if (checkForCircularImports && isChainedImportOnStack(configClass)) {
            this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
        }
        else {
            //將當(dāng)前bean放到importStack中吹菱,用于檢查循環(huán)引入
            this.importStack.push(configClass);
            try {
                for (SourceClass candidate : importCandidates) {
                    //import指定的Bean是ImportSelector類型
                    if (candidate.isAssignable(ImportSelector.class)) {
                        //實例化指定的ImportSelector子類
                        Class<?> candidateClass = candidate.loadClass();
                        ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
                                this.environment, this.resourceLoader, this.registry);
                        //如果是ImportSelector的子類DeferredImportSelector的子類則按照DeferredImportSelectorHandler邏輯進行處理
                        if (selector instanceof DeferredImportSelector) {
                            this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
                        }
                        else {//如果不是則獲取指定的需要引入的class的ClassNames
                            String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
                            //根據(jù)ClassNames獲取并封裝成一個SourceClass
                            Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
                            //繼續(xù)調(diào)用processImports處理
                            processImports(configClass, currentSourceClass, importSourceClasses, false);
                        }
                    }//如果是ImportBeanDefinitionRegistrar的子類
                    else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
                        // Candidate class is an ImportBeanDefinitionRegistrar -> delegate to it to register additional bean definitions
                        //如果對應(yīng)的ImportBeanDefinitionRegistrar子類對象,并放到configClass于游,這個是用來注冊額外的bean的
                        Class<?> candidateClass = candidate.loadClass();
                        ImportBeanDefinitionRegistrar registrar =
                                ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
                                        this.environment, this.resourceLoader, this.registry);
                        configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
                    }
                    else {//不是上面任何類的子類就可以進行處理了,將指定的需要引入的bean轉(zhuǎn)化為ConfigurationClass垫言,然后到processConfigurationClass方法中處理
                        this.importStack.registerImport(
                                currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
                        processConfigurationClass(candidate.asConfigClass(configClass));
                    }
                }
            }
            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();
            }
        }
    }

?主要的步驟如下:

  1. 檢查解析出來的Import注解集合是不是空的贰剥,空的則不處理,直接返回
  2. 如果設(shè)置了需要檢查循環(huán)依賴筷频,則進行循環(huán)依賴的檢查蚌成,不需要則跳過,進入3步驟
  3. 進行處理
    (1) 將當(dāng)前bean放到importStack中凛捏,用于檢查循環(huán)引入
    (2)循環(huán)處理以下情況
    ??1)如果是ImportSelector類型的子類担忧。先實例化這個類,然后檢查這個類是不是DeferredImportSelector子類坯癣,是的則調(diào)用ConfigurationClassParser的內(nèi)部類DeferredImportSelectorHandlerhandle方法處理瓶盛。如果不是DeferredImportSelector子類則繼續(xù)調(diào)用processImports方法處理。按照步驟1從頭開始。
    ??2)如果是ImportBeanDefinitionRegistrar類型的子類惩猫,則實例化這個類芝硬,然后放到當(dāng)前解析的bean的importBeanDefinitionRegistrars屬性中,后面注冊bean時候調(diào)用轧房。
    ??3)沒有實現(xiàn)以上任何接口拌阴,則將當(dāng)前的被引入的bean加入到這個bean的importedBy屬性中,然后調(diào)用processConfigurationClass方法奶镶。

?對上面的信息進行總結(jié)迟赃。這個方法作用就是解析時候?qū)⒈挥?code>@Import注解引入的bean加入到使用@Import注解標(biāo)簽的bean的importedBy中后面進行解析時候用,還有就是后面注冊bean的時候可能也會調(diào)用實現(xiàn)了ImportBeanDefinitionRegistrar類型的子類厂镇。

2.3.2 processConfigurationClass方法處理configClass

?上面processImports方法調(diào)用之后最后都會返回到processConfigurationClass纤壁。這里介紹以下這個方法的作用。這個方法會對ConfigurationClass對象(這個對象封裝了貼了@Configuration或者@Bean注解的對象的信息)進行解析剪撬,解析上面的注解摄乒。就我們上面提到的@Component@PropertySources残黑,@ComponentScans等馍佑。最后保存起來,后面實例化的時候會用到(后面實例化的時候使用的是CGLIB的方式實例化的)梨水,跟其他的bean不一樣拭荤。

    protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
        ......
        do {
            //處理configClass
            sourceClass = doProcessConfigurationClass(configClass, sourceClass);
        }
        while (sourceClass != null);
        //保存到configurationClasses中
        this.configurationClasses.put(configClass, configClass);
    }

?到這里@Import注解的解析基本就完了。剩下的就是介紹ImportSelector疫诽,ImportBeanDefinitionRegistrar以及DeferredImportSelector之間的區(qū)別了舅世。下篇文章解析。

3.ImportSelector奇徒,ImportBeanDefinitionRegistrar以及DeferredImportSelector

?在上面的processImports方法中已經(jīng)講解了對所有的@Import注解中value值為不同指的情況進行解析俗批。有以下的情況:

  1. ImportSelector接口的實現(xiàn)類
  2. DeferredImportSelector接口的實現(xiàn)類
  3. ImportBeanDefinitionRegistrar接口的實現(xiàn)類
  4. 非以上3中接口的實現(xiàn)類铸题,也就是普通的類

?這里主要講解1到3這三個接口類的區(qū)別。

3.1 ImportSelector
public interface ImportSelector {

    String[] selectImports(AnnotationMetadata importingClassMetadata);

}

?ImportSelector接口作用將方法返回的字符串?dāng)?shù)組作為bean注入到容器中,注意這里的字符串需要是對象的全路徑名稱比如A.class.getName()這種速客。后面會講spring的擴展的時候會使用的并村。

3.2 ImportBeanDefinitionRegistrar

?ImportBeanDefinitionRegistrar這個接口作用是苍柏,用戶可以實現(xiàn)了之后來自定義來注冊需要注冊的bean画切。可以設(shè)置自定義的BeanNameGeneratorbean名稱生成規(guī)則长踊。

public interface ImportBeanDefinitionRegistrar {

    default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry,
            BeanNameGenerator importBeanNameGenerator) {

        registerBeanDefinitions(importingClassMetadata, registry);
    }

    default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    }

}
3.3 DeferredImportSelector

?ImportBeanDefinitionRegistrarImportSelector的子接口功舀,在4.0版本加入的。在這個類里面添加分組的功能身弊,能夠?qū)⒍鄠€DeferredImportSelector進行分組辟汰。同一個組內(nèi)的ImportBeanDefinitionRegistrar能夠通過實現(xiàn)@Order注解去實現(xiàn)排序列敲。在調(diào)用的時候處理的時候會先按照序號進行排序,然后依次調(diào)用對應(yīng)實現(xiàn)的 ImportSelector接口的selectImports方法莉擒。
?還有一點就是DeferredImportSelector的調(diào)用邏輯在酿炸,所有的@Configuration已經(jīng)解析了之后在調(diào)用的。這點可以在2.2.4 ConfigurationClassParser的解析過程代碼中看出來涨冀。

public interface DeferredImportSelector extends ImportSelector {

    default Class<? extends Group> getImportGroup() {
        return null;
    }

    interface Group {

    ......

    }

?這里將區(qū)別列舉出來

作用
ImportSelector 將方法返回的字符串?dāng)?shù)組作為bean注入到容器中
ImportBeanDefinitionRegistrar 自定義來注冊bean
DeferredImportSelector ImportSelector差不多只不過多了分組的功能填硕,處理在@Configuration類型bean解析之后
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市鹿鳖,隨后出現(xiàn)的幾起案子扁眯,更是在濱河造成了極大的恐慌,老刑警劉巖翅帜,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件姻檀,死亡現(xiàn)場離奇詭異,居然都是意外死亡涝滴,警方通過查閱死者的電腦和手機绣版,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來歼疮,“玉大人杂抽,你說我怎么就攤上這事『啵” “怎么了缩麸?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長赡矢。 經(jīng)常有香客問我杭朱,道長,這世上最難降的妖魔是什么吹散? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任弧械,我火速辦了婚禮,結(jié)果婚禮上空民,老公的妹妹穿的比我還像新娘刃唐。我一直安慰自己,他們只是感情好袭景,可當(dāng)我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布唁桩。 她就那樣靜靜地躺著闭树,像睡著了一般耸棒。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上报辱,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天与殃,我揣著相機與錄音单山,去河邊找鬼。 笑死幅疼,一個胖子當(dāng)著我的面吹牛米奸,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播爽篷,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼悴晰,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了逐工?” 一聲冷哼從身側(cè)響起铡溪,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎泪喊,沒想到半個月后棕硫,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡袒啼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年哈扮,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蚓再。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡滑肉,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出对途,到底是詐尸還是另有隱情赦邻,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布实檀,位于F島的核電站惶洲,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏膳犹。R本人自食惡果不足惜恬吕,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望须床。 院中可真熱鬧铐料,春花似錦、人聲如沸豺旬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽族阅。三九已至篓跛,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間坦刀,已是汗流浹背愧沟。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工蔬咬, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人沐寺。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓林艘,卻偏偏與公主長得像,于是被迫代替她去往敵國和親混坞。 傳聞我的和親對象是個殘疾皇子狐援,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內(nèi)容