spring-reading./spring-annotation-import
xuchengsheng 0425f6cef8 @Import新增目录 2023-10-07 09:57:09 +08:00
..
src/main/java/com/xcs/spring @Import注解源码分析 2023-09-28 17:12:13 +08:00
README.md @Import新增目录 2023-10-07 09:57:09 +08:00
pom.xml init 2023-09-15 14:44:51 +08:00

README.md

@Import

一、注解描述

@Import 是 Spring 框架的核心注解,用于导入配置类或组件到当前的 Spring 上下文中。它可以用于导入常规的 @Configuration 类、常规组件类,或实现了 ImportSelectorImportBeanDefinitionRegistrar 接口的类。ImportSelector 允许根据条件动态地选择要导入的组件,而 ImportBeanDefinitionRegistrar 提供了一种以编程方式注册bean的方法。使用 @Import 注解,我们可以更灵活、模块化地组织 Spring 的配置,确保上下文中有所需的所有组件和配置。

二、接口源码

@Import 是 Spring 框架自 3.0 版本开始引入的一个核心注解。允许我们导入一个或多个组件类,这些类通常是 @Configuration 类。它在功能上相当于 Spring XML 中的 <import/> 元素,导入类型@Configuration类、ImportSelectorImportBeanDefinitionRegistrar的实现以及其他常规组件类,在导入的 @Configuration 类中声明的 bean 定义应使用 @Autowired 进行注入。

/**
 * 表示要导入的一个或多个组件类 —— 通常是
 * Configuration @Configuration 类。
 *
 * 提供与Spring XML中的 <import/> 元素相同的功能。
 * 允许导入 @Configuration 类、ImportSelector 和
 * ImportBeanDefinitionRegistrar 的实现,以及常规组件
 * 类 (从 4.2 开始;与 AnnotationConfigApplicationContext#register 相似)。
 *
 * 在导入的 @Configuration 类中声明的 @Bean 定义应通过
 * org.springframework.beans.factory.annotation.Autowired @Autowired 注入。
 * 可以自动注入bean本身也可以自动注入声明bean的配置类实例。
 * 后者允许在 @Configuration 类方法之间进行明确的、IDE友好的导航。
 *
 * 可以在类级别声明或作为元注解。
 *
 * 如果需要导入XML或其他非-@Configuration 的bean定义资源
 * 请改用 ImportResource @ImportResource 注解。
 *
 * @author Chris Beams
 * @author Juergen Hoeller
 * @since 3.0
 * @see Configuration
 * @see ImportSelector
 * @see ImportBeanDefinitionRegistrar
 * @see ImportResource
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {

	/**
	 * 要导入的 Configuration @Configuration、ImportSelector、
	 * ImportBeanDefinitionRegistrar 或常规组件类。
	 */
	Class<?>[] value();

}

三、主要功能

导入配置类:允许一个 @Configuration 类引入另一个 @Configuration 类。

导入选择器:通过实现 ImportSelector 接口,可以动态地选择和导入配置类。

手动注册Bean:通过实现 ImportBeanDefinitionRegistrar 接口,可以在运行时手动注册 bean。

导入常规组件类:从 Spring 4.2 开始,还可以导入常规的组件类。

四、最佳实践

首先来看看启动类入口,上下文环境使用AnnotationConfigApplicationContext此类是使用Java注解来配置Spring容器的方式构造参数我们给定了一个MyConfiguration组件类然后遍历并打印所有的bean定义名。

public class ImportApplication {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class);
        for (String beanDefinitionName : context.getBeanDefinitionNames()) {
            System.out.println("beanName = " + beanDefinitionName);
        }
    }
}

使用@Import 注解允许导入其他组件或配置到当前的配置类。在 MyConfiguration 类中,它导入了四个不同的组件或选择器,第一个是MyBean.class一个常规Bean组件。第二个是MyImportSelector.class一个实现了 ImportSelector 的类,用于动态选择并导入配置。第三个是MyDeferredImportSelector.class一个实现了 DeferredImportSelector 的类,用于延迟地选择并导入配置。第四个是MyImportBeanDefinitionRegistrar.class一个实现了 ImportBeanDefinitionRegistrar 的类用于手动注册bean。

@Configuration
@Import({MyBean.class, MyImportSelector.class, MyDeferredImportSelector.class, MyImportBeanDefinitionRegistrar.class})
public class MyConfiguration {

}

MyImportSelector 类提供了一种动态导入 MyBeanA 组件的机制。确保 MyBeanA 被加入到Spring的上下文中。

public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{MyBeanA.class.getName()};
    }
}

MyDeferredImportSelector 类提供了一种延迟导入 MyBeanB 组件的机制,确保 MyBeanB 被添加到Spring的上下文中。与普通的 ImportSelector 不同,DeferredImportSelector 允许在Spring处理完所有其他配置类之后再进行导入从而确保某些特定的处理顺序。

public class MyDeferredImportSelector implements DeferredImportSelector {

    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{MyBeanB.class.getName()};
    }
}

MyImportBeanDefinitionRegistrar 类提供手动注册 MyBeanC 组件到Spring容器的方法而不依赖于组件扫描或其他自动配置机制。确保 MyBeanC 被添加到Spring的上下文中。

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        RootBeanDefinition beanDefinition = new RootBeanDefinition(MyBeanC.class);
        registry.registerBeanDefinition(MyBeanC.class.getName(), beanDefinition);
    }
}

使用@Import注解和其相关的选择器或注册器来将这些bean类导入到Spring上下文中

public class MyBean {

}

public class MyBeanA {

}

public class MyBeanB {

}

public class MyBeanC {

}

五、时序图

sequenceDiagram
    participant ImportApplication
    participant AnnotationConfigApplicationContext
    participant AbstractApplicationContext
    participant PostProcessorRegistrationDelegate
    participant ConfigurationClassPostProcessor
    participant ConfigurationClassParser
    participant DeferredImportSelectorHandler
    participant DeferredImportSelectorGroupingHandler
    participant DeferredImportSelectorGrouping
    participant DefaultDeferredImportSelectorGroup
    participant ConfigurationClassBeanDefinitionReader
    participant MyImportSelector
    participant MyDeferredImportSelector
    participant MyImportBeanDefinitionRegistrar
    participant DefaultListableBeanFactory

    ImportApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext(componentClasses)<br>初始化上下文
    AnnotationConfigApplicationContext->>AbstractApplicationContext:refresh()<br>刷新上下文
    AbstractApplicationContext->>AbstractApplicationContext:invokeBeanFactoryPostProcessors(beanFactory)<br>调用BeanFactory的后处理器
    AbstractApplicationContext->>PostProcessorRegistrationDelegate:invokeBeanFactoryPostProcessors(beanFactory,beanFactoryPostProcessors)<br>委托调用BeanFactory的后处理器
    PostProcessorRegistrationDelegate->>PostProcessorRegistrationDelegate:invokeBeanDefinitionRegistryPostProcessors(postProcessors,registry,applicationStartup)<br>执行BeanDefinition的注册后处理器
    PostProcessorRegistrationDelegate->>ConfigurationClassPostProcessor:postProcessBeanDefinitionRegistry(registry)<br>处理配置类
    ConfigurationClassPostProcessor->>ConfigurationClassPostProcessor:processConfigBeanDefinitions(registry)<br>处理配置类bean的定义
    ConfigurationClassPostProcessor->>ConfigurationClassParser:new ConfigurationClassParser()<br>创建配置类解析器
    ConfigurationClassParser-->>ConfigurationClassPostProcessor:返回parser
    ConfigurationClassPostProcessor->>ConfigurationClassParser:parser.parse(candidates)<br>解析候选类
    ConfigurationClassParser->>ConfigurationClassParser:parse(metadata,beanName)<br>进一步解析类元数据
    ConfigurationClassParser->>ConfigurationClassParser:processConfigurationClass(configClass,filter)<br>处理@Configuration类
    ConfigurationClassParser->>+ConfigurationClassParser:doProcessConfigurationClass(configClass, sourceClass, filter)<br>实际处理配置类
    ConfigurationClassParser-->>-ConfigurationClassParser:返回SourceClass
    ConfigurationClassParser->>+ConfigurationClassParser:processImports(configClass, sourceClass, importCandidates, filter, true)<br>处理导入
    ConfigurationClassParser->>MyImportSelector:selectImports(importingClassMetadata)<br>调用自定义的导入选择器
    MyImportSelector-->>ConfigurationClassParser:返回Bean数组
    ConfigurationClassParser->>DeferredImportSelectorHandler:process()<br>处理延迟导入选择器
    DeferredImportSelectorHandler->>DeferredImportSelectorGroupingHandler:processGroupImports()<br>处理组导入
    DeferredImportSelectorGroupingHandler->>DeferredImportSelectorGrouping:getImports()<br>获取导入
    DeferredImportSelectorGrouping->>DefaultDeferredImportSelectorGroup:process(metadata,selector)<br>处理默认延迟导入选择器的组
    DefaultDeferredImportSelectorGroup->>MyDeferredImportSelector:selectImports(importingClassMetadata)<br> 调用自定义的导入选择器
    MyDeferredImportSelector-->>DefaultDeferredImportSelectorGroup:返回Bean数组,存储在imports字段中
    DeferredImportSelectorGrouping->>DefaultDeferredImportSelectorGroup:selectImports()<br>选择导入
    DeferredImportSelectorGrouping-->>DeferredImportSelectorGroupingHandler:返回Iterable<Group.Entry>
    DeferredImportSelectorGroupingHandler->>ConfigurationClassParser:processImports(configClass, sourceClass, importCandidates, filter, true)<br> 再次处理导入
    ConfigurationClassParser-->>-ConfigurationClassParser:递归处理@Configuration
    ConfigurationClassPostProcessor->>ConfigurationClassBeanDefinitionReader:loadBeanDefinitions(configClasses)<br>加载bean定义
    ConfigurationClassBeanDefinitionReader->>ConfigurationClassBeanDefinitionReader:loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator)<br>加载配置类的bean定义
    ConfigurationClassBeanDefinitionReader->>ConfigurationClassBeanDefinitionReader:registerBeanDefinitionForImportedConfigurationClass(configClass)<br>注册导入的配置类的bean定义
    ConfigurationClassBeanDefinitionReader->>DefaultListableBeanFactory:registerBeanDefinition(beanName,beanDefinition)<br>在bean工厂中注册bean定义
    ConfigurationClassBeanDefinitionReader->>ConfigurationClassBeanDefinitionReader:loadBeanDefinitionsFromRegistrars(registrars)<br>从注册器中加载bean定义
    ConfigurationClassBeanDefinitionReader->>MyImportBeanDefinitionRegistrar:registerBeanDefinitions(importingClassMetadata,registry)<br>调用自定义的bean定义注册器
    MyImportBeanDefinitionRegistrar->>DefaultListableBeanFactory:registerBeanDefinition(beanName,beanDefinition)<br>在bean工厂中注册bean定义

六、源码分析

首先来看看启动类入口,上下文环境使用AnnotationConfigApplicationContext此类是使用Java注解来配置Spring容器的方式构造参数我们给定了一个MyConfiguration组件类然后遍历并打印所有的bean定义名。

public class ImportApplication {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class);
        for (String beanDefinitionName : context.getBeanDefinitionNames()) {
            System.out.println("beanName = " + beanDefinitionName);
        }
    }
}

org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext构造函数中,执行了三个步骤,我们重点关注refresh()方法。

public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
    this();
    register(componentClasses);
    refresh();
}

org.springframework.context.support.AbstractApplicationContext#refresh方法中我们重点关注一下finishBeanFactoryInitialization(beanFactory)这方法会对实例化所有剩余非懒加载的单列Bean对象其他方法不是本次源码阅读的重点暂时忽略。

@Override
public void refresh() throws BeansException, IllegalStateException {
     // ... [代码部分省略以简化]
     // 调用在上下文中注册为bean的工厂处理器
     invokeBeanFactoryPostProcessors(beanFactory);
     // ... [代码部分省略以简化]
}

org.springframework.context.support.AbstractApplicationContext#invokeBeanFactoryPostProcessors方法中,又委托了PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors()进行调用。

protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
    PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
    // ... [代码部分省略以简化]
}

org.springframework.context.support.PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors方法中,首先调用了 BeanDefinitionRegistryPostProcessor(这是 BeanFactoryPostProcessor 的子接口)。它专门用来在所有其他 bean 定义加载之前修改默认的 bean 定义。

public static void invokeBeanFactoryPostProcessors(
        ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
    // ... [代码部分省略以简化]
    invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
	// ... [代码部分省略以简化]
}

org.springframework.context.support.PostProcessorRegistrationDelegate#invokeBeanDefinitionRegistryPostProcessors方法中,循环调用了实现BeanDefinitionRegistryPostProcessor接口中的postProcessBeanDefinitionRegistry(registry)方法

private static void invokeBeanDefinitionRegistryPostProcessors(
			Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry, ApplicationStartup applicationStartup) {

    for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
        StartupStep postProcessBeanDefRegistry = applicationStartup.start("spring.context.beandef-registry.post-process")
            .tag("postProcessor", postProcessor::toString);
        postProcessor.postProcessBeanDefinitionRegistry(registry);
        postProcessBeanDefRegistry.end();
    }
}

org.springframework.context.annotation.ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry方法中,调用了processConfigBeanDefinitions方法该方法的主要目的是处理和注册配置类中定义的beans。

@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    // ... [代码部分省略以简化]
    processConfigBeanDefinitions(registry);
}

org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions方法中这个方法主要处理了配置类的解析和验证并确保了所有在配置类中定义的beans都被正确地注册到Spring的bean定义注册表中。

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
    // ... [代码部分省略以简化]

    // 步骤1创建一个用于解析配置类的解析器
    ConfigurationClassParser parser = new ConfigurationClassParser(
            this.metadataReaderFactory, this.problemReporter, this.environment,
            this.resourceLoader, this.componentScanBeanNameGenerator, registry);

    // 步骤2初始化候选配置类集合以及已解析配置类集合
    Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
    Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());

    // 步骤3循环处理所有候选配置类直至没有候选类为止
    do {
        // 步骤3.1 解析配置类
        parser.parse(candidates);
        // 步骤3.2 验证配置类
        parser.validate();

        // 获取解析后的配置类,并从中移除已经处理过的
        Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
        configClasses.removeAll(alreadyParsed);

        // 步骤4如果reader为空则创建一个新的Bean定义读取器
        if (this.reader == null) {
            this.reader = new ConfigurationClassBeanDefinitionReader(
                    registry, this.sourceExtractor, this.resourceLoader, this.environment,
                    this.importBeanNameGenerator, parser.getImportRegistry());
        }

        // 步骤5使用读取器为解析的配置类加载Bean定义
        this.reader.loadBeanDefinitions(configClasses);

        // ... [代码部分省略以简化]
        
    } while (!candidates.isEmpty());

    // ... [代码部分省略以简化]
}

我们来到org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions方法中的步骤3.1。在org.springframework.context.annotation.ConfigurationClassParser#parse方法中主要是遍历所有的配置类候选者并对每一个带有注解的Bean定义进行解析。这通常涉及到查找该配置类中的@Bean方法、组件扫描指令等并将这些信息注册到Spring容器中。

public void parse(Set<BeanDefinitionHolder> configCandidates) {
    // 遍历每个配置类的持有者
    for (BeanDefinitionHolder holder : configCandidates) {
        BeanDefinition bd = holder.getBeanDefinition();
        try {
            // 步骤1如果Bean定义是AnnotatedBeanDefinition的实例则获取其注解元数据并解析
            if (bd instanceof AnnotatedBeanDefinition) {
                parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
            }
            // 步骤2如果Bean定义是AbstractBeanDefinition并且已经有关联的Bean类则直接解析该Bean类
            else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
                parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
            }
            // 步骤3如果上述情况都不符合则直接使用Bean的类名进行解析
            else {
                parse(bd.getBeanClassName(), holder.getBeanName());
            }
        }
        // 如果在解析过程中捕获到BeanDefinitionStoreException则直接抛出
        catch (BeanDefinitionStoreException ex) {
            throw ex;
        }
        // 对于其他类型的异常封装并抛出一个新的BeanDefinitionStoreException
        catch (Throwable ex) {
            // ... [代码部分省略以简化]
        }
    }
    // 步骤4在所有配置类都被解析后处理所有延迟的导入选择器
    this.deferredImportSelectorHandler.process();
}

我们来到org.springframework.context.annotation.ConfigurationClassParser#parse方法的第一步,在org.springframework.context.annotation.ConfigurationClassParser#parse(metadata, beanName)方法中将注解元数据和Bean名称转化为一个配置类然后对其进行处理。处理配置类是Spring配置驱动的核心它涉及到许多关键操作如解析@Bean方法、处理@ComponentScan注解、处理@Import注解等。

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

org.springframework.context.annotation.ConfigurationClassParser#processConfigurationClass方法中,处理一个给定的配置类。它首先递归地处理配置类及其父类,以确保所有相关的配置都被正确地读取并解析。在递归处理完所有相关配置后,它将配置类添加到已解析的配置类的映射中。

protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
    // ... [代码部分省略以简化]

    // 步骤1递归地处理配置类及其超类层次结构
    SourceClass sourceClass = asSourceClass(configClass, filter);
    do {
        sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
    } while (sourceClass != null);

    // 步骤2将处理后的配置类放入映射中
    this.configurationClasses.put(configClass, configClass);
}

org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass方法中,其中主要目的是处理给定ConfigurationClass中的@Import注解。在处理完所有的导入之后它完成了任务并返回null。

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

    // ... [代码部分省略以简化]
    
    // 处理 sourceClass 上的所有 @Import 注解
    processImports(configClass, sourceClass, getImports(sourceClass), filter, true);

    // ... [代码部分省略以简化]
    
    // 当前实现中直接返回null意味着没有超类需要进一步处理
    return null;
}

org.springframework.context.annotation.ConfigurationClassParser#processImports方法中,主要分为三个步骤。第一步是对于ImportSelectorprocessImports方法主要完成了动态选择和导入类的功能使得Spring配置更加灵活和模块化。第二步是ImportBeanDefinitionRegistrar提供了一个在bean定义注册过程中的插入点允许动态、条件性配置。processImports方法确保这些逻辑在处理@Import时正确执行。第三步是当遇到一个不是ImportSelectorImportBeanDefinitionRegistrar的类时直接视其为一个普通的Spring组件或配置类并按照常规的处理流程进行。这确保了所有通过@Import导入的类,不论它们的类型如何,都会被正确地注册和处理。

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
            Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
            boolean checkForCircularImports) {
    // 如果没有要导入的候选类,直接返回
    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) {
                // 步骤1判断候选类是否实现了 ImportSelector 接口
                if (candidate.isAssignable(ImportSelector.class)) {
                    // 加载当前的候选类
                    Class<?> candidateClass = candidate.loadClass();
                    // 通过工具方法实例化 ImportSelector 接口的实现类
                    ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
                                        this.environment, this.resourceLoader, this.registry);
                    // 获取从 ImportSelector 指定的排除过滤器
                    Predicate<String> selectorFilter = selector.getExclusionFilter();
                    // 如果选择器提供了额外的排除过滤器,则合并当前的排除过滤器
                    if (selectorFilter != null) {
                        exclusionFilter = exclusionFilter.or(selectorFilter);
                    }
                    // 步骤1.1:检查该选择器是否是 DeferredImportSelector 的实例
                    if (selector instanceof DeferredImportSelector) {
                        // 如果是 DeferredImportSelector使用特定的处理器来处理它
                        this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
                    }
                    else {
                        // 步骤1.2 如果不是 DeferredImportSelector则使用选择器来选择要导入的类
                        String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
                        // 将这些类名转化为 SourceClass 集合
                        Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
                        // 递归地处理这些要导入的类
                        processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
                    }
                }
                // 步骤2检查当前候选类是否实现了 ImportBeanDefinitionRegistrar 接口
                else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
                    // 加载当前的候选类
                    Class<?> candidateClass = candidate.loadClass();
                    // 通过工具方法实例化 ImportBeanDefinitionRegistrar 接口的实现类
                    ImportBeanDefinitionRegistrar registrar =
                        ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
                            this.environment, this.resourceLoader, this.registry);
                    // 将实例化的 registrar 添加到当前的配置类中
                    configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
                }
                // 步骤3如果候选类既不是 ImportSelector 也不是 ImportBeanDefinitionRegistrar
                else {
                    // 在导入堆栈中为当前的源类注册导入的类
                    this.importStack.registerImport(
                        currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
                    // 将候选类处理为一个配置类,并递归地处理它
                    processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
                }
            }
        }
        catch (BeanDefinitionStoreException ex) {
            throw ex;
        }
        catch (Throwable ex) {
            // ... [代码部分省略以简化]
        }
        finally {
            // 在处理完成后,从导入堆栈中弹出当前配置类
            this.importStack.pop();
        }
    }
}

org.springframework.context.annotation.ConfigurationClassParser#processImports方法中的步骤1.2中,最后调用到我们自定义的实现逻辑中来,selectImports方法始终返回MyBeanA类的完全限定名,表示当这个ImportSelector被处理时,MyBeanA类将被导入到Spring应用上下文中。

public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{MyBeanA.class.getName()};
    }
}

我们回到org.springframework.context.annotation.ConfigurationClassParser#parse方法的第四步,在org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorHandler#process方法中,首先获取当前待处理的延迟导入选择器,然后使用DeferredImportSelectorGroupingHandler来按组处理它们,最后确保再次初始化延迟导入选择器列表,为下一次处理做准备。

/**
 * 处理所有注册的延迟导入选择器。
 */
public void process() {
    // 获取当前的延迟导入选择器列表
    List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
    // 将当前的延迟导入选择器列表设为null表示它们即将被处理
    this.deferredImportSelectors = null;
    
    try {
        // 如果存在待处理的延迟导入选择器
        if (deferredImports != null) {
            // 创建一个新的分组处理器,用于处理延迟导入选择器的分组逻辑
            DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
            
            // 对延迟导入选择器进行排序,确保按预期的顺序进行处理
            deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
            
            // 遍历每个延迟导入选择器,使用分组处理器进行注册
            deferredImports.forEach(handler::register);
            
            // 使用分组处理器处理分组的导入
            handler.processGroupImports();
        }
    }
    // 确保在方法结束时,无论是否发生异常,都将延迟导入选择器列表重新初始化为一个新的空列表
    finally {
        this.deferredImportSelectors = new ArrayList<>();
    }
}

org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorGroupingHandler#processGroupImports方法中,遍历每个已注册的延迟导入选择器分组,并对每个分组中的每个导入条目进行处理。处理的内容包括递归地处理所有相关的@Import@Bean定义。

/**
 * 处理每个分组中的延迟导入选择器。
 */
public void processGroupImports() {
    // 遍历每个已注册的延迟导入选择器分组
    for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
        // 获取分组的候选类排除过滤器
        Predicate<String> exclusionFilter = grouping.getCandidateFilter();
        
        // 遍历分组中的每个导入选择器
        grouping.getImports().forEach(entry -> {
            // 获取与当前导入条目关联的配置类
            ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata());
            
            try {
                // 对每个导入条目进行处理,包括递归地处理所有相关的@Import和@Bean定义
                processImports(configurationClass, asSourceClass(configurationClass, exclusionFilter),
                               Collections.singleton(asSourceClass(entry.getImportClassName(), exclusionFilter)),
                               exclusionFilter, false);
            }
            // 如果在处理过程中捕获到BeanDefinitionStoreException则直接抛出
            catch (BeanDefinitionStoreException ex) {
                throw ex;
            }
            // 对于其他类型的异常,处理相关逻辑(此处简化,不展开详细的异常处理逻辑)
            catch (Throwable ex) {
                // ... [代码部分省略以简化]
            }
        });
    }
}

org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorGrouping#getImports方法中,遍历每个已注册的延迟导入选择器,将它们的相关信息传递给分组进行处理,然后最终使用分组实例来选择并返回要导入的类。

public Iterable<Group.Entry> getImports() {
    // 遍历每个已注册的延迟导入选择器
    for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
        // 对于每个延迟导入选择器,将其关联的配置类的元数据和其选择器传递给分组进行处理
        this.group.process(deferredImport.getConfigurationClass().getMetadata(),
                           deferredImport.getImportSelector());
    }
    // 使用分组实例来选择并返回要导入的类
    return this.group.selectImports();
}

org.springframework.context.annotation.ConfigurationClassParser.DefaultDeferredImportSelectorGroup#process方法中,主要给传入的DeferredImportSelector,找出它选择导入的所有类,并将这些类与提供的注解元数据一起存储在一个列表中,以供以后使用。

@Override
public void process(AnnotationMetadata metadata, DeferredImportSelector selector) {
    // 使用选择器和提供的元数据选择要导入的类
    for (String importClassName : selector.selectImports(metadata)) {
        // 对于每个选定的导入类创建一个新的Entry对象并将其添加到当前分组的导入列表中
        this.imports.add(new Entry(metadata, importClassName));
    }
}

最后调用到我们自定义的实现逻辑中来,selectImports方法始终返回MyBeanB类的完全限定名,表示当这个DeferredImportSelector被处理时,MyBeanB类将被导入到Spring应用上下文中。

public class MyDeferredImportSelector implements DeferredImportSelector {

    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{MyBeanB.class.getName()};
    }
}

我们来到org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions方法中的步骤5。在org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitions方法中,遍历给定的一组ConfigurationClass对象(这些对象代表的是@Configuration注解的类并为每个类加载其相关的Bean定义。

public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
    TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
    for (ConfigurationClass configClass : configurationModel) {
        loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
    }
}

org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForConfigurationClass方法中,如果配置类是通过@Import注解或其他机制导入的则该方法会为其注册一个Bean定义另外该方法会处理与ConfigurationClass关联的所有ImportBeanDefinitionRegistrar。这些注册器允许在解析配置类时以编程方式动态地添加额外的Bean定义。

private void loadBeanDefinitionsForConfigurationClass(
        ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {

    // ... [可能的初始化或其他预处理]

    // 步骤1如果这个配置类是由于@Import或其他机制而被导入的那么为它注册一个Bean定义。
    // 这确保了导入的@Configuration类也被视为一个Bean并可以在ApplicationContext中被检索。
    if (configClass.isImported()) {
        registerBeanDefinitionForImportedConfigurationClass(configClass);
    }
    
    // ... [可能的其他处理或Bean定义加载]

    // 步骤2从当前配置类关联的ImportBeanDefinitionRegistrars加载Bean定义。
    // ImportBeanDefinitionRegistrar允许我们在解析@Configuration类时进行编程式地注册额外的Bean定义。
    loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}

我们来到org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForConfigurationClass方法中步骤1org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#registerBeanDefinitionForImportedConfigurationClass方法中首先它根据配置类的注解元数据创建一个Bean定义。然后它确定并设置Bean的作用域例如单例或原型。为Bean定义生成一个唯一的名称后该方法处理了其上的常见注解例如@Lazy@Primary。如果Bean的作用域如请求或会话需要代理会应用相应的代理。最后它将Bean定义注册到Spring容器并将生成的名称与原始配置类关联。这确保了导入的配置类在Spring中也可以作为一个常规Bean进行访问或注入。

/**
 * 为导入的ConfigurationClass注册Bean定义。
 *
 * @param configClass 要处理的ConfigurationClass
 */
private void registerBeanDefinitionForImportedConfigurationClass(ConfigurationClass configClass) {
    // 获取configClass的元数据
    AnnotationMetadata metadata = configClass.getMetadata();

    // 创建基于元数据的Bean定义
    AnnotatedGenericBeanDefinition configBeanDef = new AnnotatedGenericBeanDefinition(metadata);

    // 解析Bean的作用域例如singleton, prototype
    ScopeMetadata scopeMetadata = scopeMetadataResolver.resolveScopeMetadata(configBeanDef);
    configBeanDef.setScope(scopeMetadata.getScopeName());

    // 为configBeanDef生成Bean名称
    String configBeanName = this.importBeanNameGenerator.generateBeanName(configBeanDef, this.registry);

    // 处理常见的注解定义(例如:@Lazy, @Primary
    AnnotationConfigUtils.processCommonDefinitionAnnotations(configBeanDef, metadata);

    // 将Bean定义封装在BeanDefinitionHolder中以携带其他配置元数据
    BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(configBeanDef, configBeanName);

    // 根据需要应用作用域代理模式(例如,对于"request"和"session"作用域的Beans
    definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);

    // 在Bean定义注册表中注册Bean定义
    this.registry.registerBeanDefinition(definitionHolder.getBeanName(), definitionHolder.getBeanDefinition());

    // 将生成的Bean名称设置到configClass中
    configClass.setBeanName(configBeanName);

    // 如果日志级别为TRACE记录一条关于注册的消息
    if (logger.isTraceEnabled()) {
        logger.trace("Registered bean definition for imported class '" + configBeanName + "'");
    }
}

我们来到org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForConfigurationClass方法中步骤2每个ImportBeanDefinitionRegistrar都有机会基于其自定义逻辑在Spring容器中注册额外的Bean定义。

private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
    registrars.forEach((registrar, metadata) ->
                       registrar.registerBeanDefinitions(metadata, this.registry, this.importBeanNameGenerator));
}

最后调用到我们自定义的实现逻辑中来,MyImportBeanDefinitionRegistrar 类提供手动注册 MyBeanC 组件到Spring容器的方法而不依赖于组件扫描或其他自动配置机制。确保 MyBeanC 被添加到Spring的上下文中。

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        RootBeanDefinition beanDefinition = new RootBeanDefinition(MyBeanC.class);
        registry.registerBeanDefinition(MyBeanC.class.getName(), beanDefinition);
    }
}

七、注意事项

避免循环引用:确保您没有创建循环引用,即一个配置类导入另一个配置类,而后者又反过来导入前者。

@ComponentScan的关系@Import@ComponentScan都可以用于注册bean但是它们的用途稍有不同。@ComponentScan用于自动扫描和注册bean@Import用于明确地导入其他配置类。

属性覆盖如果从多个配置类中导入相同的bean定义并设置了不同的属性或值那么后导入的bean定义将覆盖先导入的bean定义。

@Profile的关系:可以结合使用@Import@Profile,这样只有在特定的激活配置文件中才会导入某个配置类。

八、总结

8.1、最佳实践总结

启动类:通过ImportApplication我们初始化了Spring的上下文环境选择了AnnotationConfigApplicationContext这是Spring提供的使用Java注解配置方式。在该上下文中我们指定了MyConfiguration作为主要的配置类并遍历了上下文中所有已注册的bean名称。

主配置类MyConfiguration类通过使用@Import注解导入了四种不同类型的组件或选择器:

  • MyBean一个普通的Java类。
  • MyImportSelector:一个实现了ImportSelector接口的类。
  • MyDeferredImportSelector:一个实现了DeferredImportSelector接口的类。
  • MyImportBeanDefinitionRegistrar:一个实现了ImportBeanDefinitionRegistrar接口的类。

选择器与注册器

  • MyImportSelector通过实现ImportSelector接口,动态地选择并导入了MyBeanA类。
  • MyDeferredImportSelector作为DeferredImportSelector的实现,延迟地选择并导入了MyBeanB类。与ImportSelector不同,它允许在所有其他配置类被处理后再进行导入。
  • MyImportBeanDefinitionRegistrar提供了手动注册bean的功能。在这个示例中它将MyBeanC手动注册到了Spring上下文中。

组件定义示例中包含四个Java类分别为MyBeanMyBeanAMyBeanBMyBeanC它们都被上述的选择器或注册器选择并导入到Spring上下文中。

8.2、源码分析总结

启动阶段在Spring启动时会使用AnnotationConfigApplicationContext来加载和解析配置类,这个配置类可能包含有@Import注解。

解析配置类:在AnnotationConfigApplicationContext的构造函数中,通过refresh()方法开始刷新容器并解析Bean定义。进一步AbstractApplicationContext#refresh方法中,会调用invokeBeanFactoryPostProcessors方法来处理在上下文中注册为bean的工厂处理器。

处理@Import注解:处理过程中会特别关注BeanDefinitionRegistryPostProcessor,这是BeanFactoryPostProcessor的子接口专门用于在所有其他bean定义加载之前修改默认的bean定义。然后系统解析配置类并验证其内容这主要涉及查找该类中的@Bean方法、组件扫描指令等并将这些信息注册到Spring容器中。

处理ImportSelectorDeferredImportSelector:在上述解析过程中,如果配置类包含一个实现了ImportSelectorDeferredImportSelector接口的类,那么它们将被用来选择并导入其他的类。在示例中,MyImportSelectorMyDeferredImportSelector是这样的选择器,分别导入MyBeanAMyBeanB

处理ImportBeanDefinitionRegistrar在加载Bean定义的过程中也会处理与ConfigurationClass关联的所有ImportBeanDefinitionRegistrar。这些注册器允许在解析时动态地、以编程方式地添加额外的Bean定义MyImportBeanDefinitionRegistrar示例中为MyBeanC进行的手动注册。

注册导入的配置类:如果一个@Configuration类是由于@Import或其他机制导入的Spring不仅会处理它的@Bean方法和其他注解还会为这个类本身注册一个Bean定义以便它也可以被注入到其他Bean或由应用程序检索。

额外Bean的注册:通过ImportBeanDefinitionRegistrar和其自定义逻辑能够根据需要将任意数量的额外Bean定义注册到容器中。