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接口。

调用链

Spring源码分析之ImportSelector工作原理_第1张图片

源码分析

调用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

你可能感兴趣的:(Java开发)