Spring源碼分析之ImportSelector工作原理

ImportSelector接口是Spring中導入外部配置的核心接口,SpringBoot的自動化配置和@EnableXXX(功能性註解)核心原理都是它。

該接口只有一個方法,返回需要Import的類的完整類名數組。

package org.springframework.context.annotation;

import org.springframework.core.type.AnnotationMetadata;

public interface ImportSelector {

    String[] selectImports(AnnotationMetadata importingClassMetadata);
    
}

根據接口註釋可以看出:其主要作用是收集需要導入的配置類。

如果該接口的實現類同時實現Aware接口(如:EnvironmentAware,BeanFactoryAware ,BeanClassLoaderAware,ResourceLoaderAware),那麼在調用其selectImports方法之前先調用上述接口中的方法。

如果需要在所有的@Configuration處理完在導入時可以實現DeferredImportSelector接口。

調用鏈

源碼分析

調用selectImports方法的地方在processImports,該方法處理@Import註解的解析,如果@Import裏導入的是ImportSelector接口,則會調用selectImports方法獲取Import的類。

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass, Collection<SourceClass> importCandidates, boolean checkForCircularImports) throws IOException {

    if (importCandidates.isEmpty()) {
        return;
    }

    if (checkForCircularImports && isChainedImportOnStack(configClass)) {
        this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
    }
    else {
        this.importStack.push(configClass);
        try {
            for (SourceClass candidate : importCandidates) {
                if (candidate.isAssignable(ImportSelector.class)) { // Import註解中配置的是ImportSelector類型
                    Class<?> candidateClass = candidate.loadClass();
                    ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class); // 實例化
                    ParserStrategyUtils.invokeAwareMethods(selector, this.environment, this.resourceLoader, this.registry); // 如果該類有實現對應的Aware接口,則注入對應的屬性
                    if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) { // DeferredImportSelector類型的放到集合中待後續處理
                        this.deferredImportSelectors.add(new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
                    }
                    else { // 直接調用selectImports方法取得對應的類
                        String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
                        Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
                        processImports(configClass, currentSourceClass, importSourceClasses, false);
                    }
                }
                else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
                    // 如果是ImportBeanDefinitionRegistrar類型
                    Class<?> candidateClass = candidate.loadClass();
                    ImportBeanDefinitionRegistrar registrar = BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
                    ParserStrategyUtils.invokeAwareMethods(registrar, this.environment, this.resourceLoader, this.registry);
                    configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
                }
                else {
                    // 如果非上面兩種類型,那麼代表這是一個與@Configuration相關的類
                    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();
        }
    }
}

上述processImports方法在doProcessConfigurationClass方法中調用:

protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
    //....
    processImports(configClass, sourceClass, getImports(sourceClass), true);
    //....
    return null;
}

依次往上找,發現的ConfigurationClassParser類入口方法是:parse,如註釋中描述的,前面一大塊代碼都是解析@Import,後面單獨對接口DeferredImportSelector解析:

public void parse(Set<BeanDefinitionHolder> configCandidates) {
    this.deferredImportSelectors = new LinkedList<DeferredImportSelectorHolder>();

    for (BeanDefinitionHolder holder : configCandidates) {
        BeanDefinition bd = holder.getBeanDefinition();
        try {
            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接口的解析
    processDeferredImportSelectors();
}

參考文章

https://blog.csdn.net/u013160932/article/details/87018724

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章