@Import源码分析

@Import

一、基本信息

✒️ 作者 - Lex 📝 博客 - 我的CSDN 📚 文章目录 - 所有文章 🔗 源码地址 - @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();

}

四、主要功能

  1. 导入配置类

    • 允许一个 @Configuration 类引入另一个 @Configuration 类。
  2. 导入选择器

    • 通过实现 ImportSelector 接口,可以动态地选择和导入配置类。
  3. 手动注册Bean

    • 通过实现 ImportBeanDefinitionRegistrar 接口,可以在运行时手动注册 bean。
  4. 导入常规组件类

    • 从 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 {

}

六、时序图

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

七、源码分析

首先来看看启动类入口,上下文环境使用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方法中步骤1,在org.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);
    }
}

八、注意事项

  1. 避免循环引用

    • 确保我们没有创建循环引用,即一个配置类导入另一个配置类,而后者又反过来导入前者。
  2. @ComponentScan的关系

    • @Import@ComponentScan都可以用于注册bean,但是它们的用途稍有不同。@ComponentScan用于自动扫描和注册bean,而@Import用于明确地导入其他配置类。
  3. 属性覆盖

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

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

九、总结

最佳实践总结
  1. 启动类

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

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

      • MyBean:一个普通的Java类。

      • MyImportSelector:一个实现了ImportSelector接口的类。

      • MyDeferredImportSelector:一个实现了DeferredImportSelector接口的类。

      • MyImportBeanDefinitionRegistrar:一个实现了ImportBeanDefinitionRegistrar接口的类。

  3. 选择器与注册器

    • MyImportSelector通过实现ImportSelector接口,动态地选择并导入了MyBeanA类。

    • MyDeferredImportSelector作为DeferredImportSelector的实现,延迟地选择并导入了MyBeanB类。与ImportSelector不同,它允许在所有其他配置类被处理后再进行导入。

    • MyImportBeanDefinitionRegistrar提供了手动注册bean的功能。在这个示例中,它将MyBeanC手动注册到了Spring上下文中。

  4. 组件定义

    • 在我们最佳实践中包含四个Java类,分别为MyBeanMyBeanAMyBeanBMyBeanC,它们都被上述的选择器或注册器选择并导入到Spring上下文中。
源码分析总结
  1. 启动阶段

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

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

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

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

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

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

    • 通过ImportBeanDefinitionRegistrar和其自定义逻辑,能够根据需要将任意数量的额外Bean定义注册到容器中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值