@Import注解源码分析

2023年 10月 13日 32.8k 0

@Import

  • @Import
    • 一、基本信息
    • 二、注解描述
    • 三、注解源码
    • 四、主要功能
    • 五、最佳实践
    • 六、时序图
    • 七、源码分析
    • 八、注意事项
    • 九、总结
      • 最佳实践总结
      • 源码分析总结

一、基本信息

✒️ 作者 - Lex 📝 博客 - 我的CSDN 📚 文章目录 - 所有文章 🔗 源码地址 - @Import源码

二、注解描述

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

三、注解源码

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

/**
 * 表示要导入的一个或多个组件类 —— 通常是
 * Configuration @Configuration 类。
 *
 * 提供与Spring XML中的  元素相同的功能。
 * 允许导入 @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)初始化上下文
        AnnotationConfigApplicationContext->>AbstractApplicationContext:refresh()刷新上下文
        AbstractApplicationContext->>AbstractApplicationContext:invokeBeanFactoryPostProcessors(beanFactory)调用BeanFactory的后处理器
        AbstractApplicationContext->>PostProcessorRegistrationDelegate:invokeBeanFactoryPostProcessors(beanFactory,beanFactoryPostProcessors)委托调用BeanFactory的后处理器
        PostProcessorRegistrationDelegate->>PostProcessorRegistrationDelegate:invokeBeanDefinitionRegistryPostProcessors(postProcessors,registry,applicationStartup)执行BeanDefinition的注册后处理器
        PostProcessorRegistrationDelegate->>ConfigurationClassPostProcessor:postProcessBeanDefinitionRegistry(registry)处理配置类
        ConfigurationClassPostProcessor->>ConfigurationClassPostProcessor:processConfigBeanDefinitions(registry)处理配置类bean的定义
        ConfigurationClassPostProcessor->>ConfigurationClassParser:new ConfigurationClassParser()创建配置类解析器
        ConfigurationClassParser-->>ConfigurationClassPostProcessor:返回parser
        ConfigurationClassPostProcessor->>ConfigurationClassParser:parser.parse(candidates)解析候选类
        ConfigurationClassParser->>ConfigurationClassParser:parse(metadata,beanName)进一步解析类元数据
        ConfigurationClassParser->>ConfigurationClassParser:processConfigurationClass(configClass,filter)处理@Configuration类
        ConfigurationClassParser->>+ConfigurationClassParser:doProcessConfigurationClass(configClass, sourceClass, filter)实际处理配置类
        ConfigurationClassParser-->>-ConfigurationClassParser:返回SourceClass
        ConfigurationClassParser->>+ConfigurationClassParser:processImports(configClass, sourceClass, importCandidates, filter, true)处理导入
        ConfigurationClassParser->>MyImportSelector:selectImports(importingClassMetadata)调用自定义的导入选择器
        MyImportSelector-->>ConfigurationClassParser:返回Bean数组
        ConfigurationClassParser->>DeferredImportSelectorHandler:process()处理延迟导入选择器
        DeferredImportSelectorHandler->>DeferredImportSelectorGroupingHandler:processGroupImports()处理组导入
        DeferredImportSelectorGroupingHandler->>DeferredImportSelectorGrouping:getImports()获取导入
        DeferredImportSelectorGrouping->>DefaultDeferredImportSelectorGroup:process(metadata,selector)处理默认延迟导入选择器的组
        DefaultDeferredImportSelectorGroup->>MyDeferredImportSelector:selectImports(importingClassMetadata) 调用自定义的导入选择器
        MyDeferredImportSelector-->>DefaultDeferredImportSelectorGroup:返回Bean数组,存储在imports字段中
        DeferredImportSelectorGrouping->>DefaultDeferredImportSelectorGroup:selectImports()选择导入
        DeferredImportSelectorGrouping-->>DeferredImportSelectorGroupingHandler:返回Iterable
        DeferredImportSelectorGroupingHandler->>ConfigurationClassParser:processImports(configClass, sourceClass, importCandidates, filter, true) 再次处理导入
        ConfigurationClassParser-->>-ConfigurationClassParser:递归处理@Configuration
        ConfigurationClassPostProcessor->>ConfigurationClassBeanDefinitionReader:loadBeanDefinitions(configClasses)加载bean定义
        ConfigurationClassBeanDefinitionReader->>ConfigurationClassBeanDefinitionReader:loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator)加载配置类的bean定义
        ConfigurationClassBeanDefinitionReader->>ConfigurationClassBeanDefinitionReader:registerBeanDefinitionForImportedConfigurationClass(configClass)注册导入的配置类的bean定义
        ConfigurationClassBeanDefinitionReader->>DefaultListableBeanFactory:registerBeanDefinition(beanName,beanDefinition)在bean工厂中注册bean定义
        ConfigurationClassBeanDefinitionReader->>ConfigurationClassBeanDefinitionReader:loadBeanDefinitionsFromRegistrars(registrars)从注册器中加载bean定义
        ConfigurationClassBeanDefinitionReader->>MyImportBeanDefinitionRegistrar:registerBeanDefinitions(importingClassMetadata,registry)调用自定义的bean定义注册器
        MyImportBeanDefinitionRegistrar->>DefaultListableBeanFactory:registerBeanDefinition(beanName,beanDefinition)在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 beanFactoryPostProcessors) {
        // ... [代码部分省略以简化]
        invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
    	// ... [代码部分省略以简化]
    }
    

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

    private static void invokeBeanDefinitionRegistryPostProcessors(
    Collection

    相关文章

    如何删除WordPress中的所有评论
    检查WordPress服务器磁盘使用情况的7种简便方法(查找大文件和数据)
    如何更改WordPress常量FS_METHOD
    如何为区块编辑器、Elementor等构建WordPress文章模板
    如何彻底地删除WordPress主题及相关内容
    如何使用WordPress搭建一个内网

    发布评论