Spring中Import注解源码解析

结论

@Import 注解可以导入三种类型的类

  1. 导入普通类,此时直接将普通类交给 Spring 管理
  2. 导入 ImportSelector 实现类中 String[] selectImports(AnnotationMetadata importingClassMetadata); 方法返回的类
  3. 导入 ImportBeanDefinitionRegistrar 实现类中 registerBeanDefinitions 方法注入的类

代码示例

以下代码每种方式使用的类都在同一个包下( @Configuration 默认会扫描当前包和子包 )。

Import的三种使用方式

引入普通类

java 复制代码
// 普通类
public class Student {
    public void sayHello() {
        System.out.println("test hello");
    }
}

//  配置类
@Configuration
@Import(Student.class)
public class ImportConfig {
}

//  主类
public class ImportMain {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ImportConfig.class);
        Student student = context.getBean(Student.class);
        student.sayHello();  // 会打印 "test hello"
    }
}

结合ImportSelector

java 复制代码
// 普通类
public class Student {
    public void sayHello() {
        System.out.println("test hello");
    }
}

//  配置类
@Configuration
@Import(CusImportSelector.class)  // 这里导入的是实现了 ImportSelector 接口的类
public class ImportConfig {
}

// 自定义实现 ImportSelector 接口的类
public class CusImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        // 返回的数组就是需要导入的类全限定名
        return new String[]{"org.example.imports.Student"};
    }
}

//  主类
public class ImportMain {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ImportConfig.class);
        Student student = context.getBean(Student.class);
        student.sayHello();  // 会打印 "test hello"
    }
}

结合 ImportBeanDefinitionRegistrar

java 复制代码
// 普通类
public class Student {
    public void sayHello() {
        System.out.println("test hello");
    }
}

//  配置类
@Configuration
@Import(CusImportBeanDefinitionRegistrar.class)  // 这里导入的是实现了 ImportSelector 接口的类
public class ImportConfig {
}

// 自定义实现 ImportBeanDefinitionRegistrar 接口的类
public class CusImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(Student.class);
        AbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
        String beanName = importBeanNameGenerator.generateBeanName(beanDefinition, registry);
        registry.registerBeanDefinition(beanName, beanDefinition);
    }
}

//  主类
public class ImportMain {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ImportConfig.class);
        Student student = context.getBean(Student.class);
        student.sayHello();  // 会打印 "test hello"
    }
}

源码流程解析

@Import 的解析实际是通过 BeanFactoryPostProcessor 实现的, 在 [[BeanFacotry的后置处理器详解]] 提到 @ComponentScan 是通过 ConfigurationClassPostProcessor 处理的,处理流程如下:

bash 复制代码
AbstractApplicationContext#refresh
    AbstractApplicationContext#invokeBeanFactoryPostProcessors
        PostProcessorRegistrationDelegate#invokeBeanDefinitionRegistryPostProcessors
            ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry
                ConfigurationClassParser#parse(Set<BeanDefinitionHolder>)
                    ConfigurationClassParser#doProcessConfigurationClass

最终是会调用到 ConfigurationClassParser#doProcessConfigurationClass 方法,接下来就看看这个方法

java 复制代码
protected final SourceClass doProcessConfigurationClass(
			ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
			throws IOException {

    // 省略

    // Process any @PropertySource annotations
    for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
            sourceClass.getMetadata(), PropertySources.class,
            org.springframework.context.annotation.PropertySource.class)) {
        if (this.propertySourceRegistry != null) {
            this.propertySourceRegistry.processPropertySource(propertySource);
        }
        else {
            logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
                    "]. Reason: Environment must implement ConfigurableEnvironment");
        }
    }

    //  这里是处理 @ComponentScan  Process any @ComponentScan annotations
    Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
            sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
    if (!componentScans.isEmpty() &&
            !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
        // 省略
    }

    // 处理 @Import Process any @Import annotations
    processImports(configClass, sourceClass, getImports(sourceClass), filter, true);

    // 省略  处理 @ImportResource 等  Process any @ImportResource annotations
    return null;
}

processImports 方法

java 复制代码
// ConfigurationClassParser 类, 该方法只列出了重要方法
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
			Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
			boolean checkForCircularImports) {

    for (SourceClass candidate : importCandidates) {
        // 导入的是 ImportSelector 类型的处理流程
        if (candidate.isAssignable(ImportSelector.class)) {
            // Candidate class is an ImportSelector -> delegate to it to determine imports
            Class<?> candidateClass = candidate.loadClass();
            ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
                    this.environment, this.resourceLoader, this.registry);
            Predicate<String> selectorFilter = selector.getExclusionFilter();
            if (selectorFilter != null) {
                exclusionFilter = exclusionFilter.or(selectorFilter);
            }
            if (selector instanceof DeferredImportSelector deferredImportSelector) {
                this.deferredImportSelectorHandler.handle(configClass, deferredImportSelector);
            }
            else {
                String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
                Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
                processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
            }
        }
        // 导入的是 ImportBeanDefinitionRegistrar 类型的处理流程
        else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
            // Candidate class is an ImportBeanDefinitionRegistrar ->
            // delegate to it to register additional bean definitions
            Class<?> candidateClass = candidate.loadClass();
            ImportBeanDefinitionRegistrar registrar =
                    ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
                            this.environment, this.resourceLoader, this.registry);
            configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
        }
        else {
            // 既不是 ImportSelector 也不是 ImportBeanDefinitionRegistrar 类型的处理流程,当作 普通的 @Configuration 类处理
            this.importStack.registerImport(
                    currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
            processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
        }
    }
}

导入的是普通类是的处理流程

从上面源码可以看到,如果导入的是普通的类(不是 ImportSelector 也不是 ImportBeanDefinitionRegistrar 类型)则将这个普通类按照含有 @Configuration 注解的类一样进行处理, 如果是按照 @Configuration 注解的类进行处理,会将普通类变成 ConfigurationClass 类型

导入的是 ImportSelector 类型

对于导入的是 ImportSelector 类型处理代码如下

java 复制代码
if (candidate.isAssignable(ImportSelector.class)) {
    // Candidate class is an ImportSelector -> delegate to it to determine imports
    Class<?> candidateClass = candidate.loadClass();
    ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
            this.environment, this.resourceLoader, this.registry);
    Predicate<String> selectorFilter = selector.getExclusionFilter();
    if (selectorFilter != null) {
        exclusionFilter = exclusionFilter.or(selectorFilter);
    }
    if (selector instanceof DeferredImportSelector deferredImportSelector) {
        this.deferredImportSelectorHandler.handle(configClass, deferredImportSelector);
    }
    else {
        String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
        Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
        processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
    }
}
  1. 先调用 selectImports 方法获取导入的类名数组
  2. 调用 asSourceClasses 方法将类名数组转换为 SourceClass 集合
  3. 调用 processImports 方法处理 SourceClass 数组, 这里就是递归调用了,递归的第一层是 ImportSelector 类型的类,递归的第二层就是 selectImports 方法中返回的普通的类了,这个就和 Import 导入的是普通类一样的处理流程,也就是最终是按照 @Configuration 注解的类处理

导入的是 ImportBeanDefinitionRegistrar 类型

对于导入的是 ImportBeanDefinitionRegistrar 类型处理代码如下

java 复制代码
if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
    // Candidate class is an ImportBeanDefinitionRegistrar ->
    // delegate to it to register additional bean definitions
    Class<?> candidateClass = candidate.loadClass();
    ImportBeanDefinitionRegistrar registrar =
            ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
                    this.environment, this.resourceLoader, this.registry);
    // configClass 就是 ConfigurationClass
    configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata()); 
}
java 复制代码
/**
 *   ConfigurationClass 类
 * 
 *  private final Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> importBeanDefinitionRegistrars = new LinkedHashMap<>();
 * /
void addImportBeanDefinitionRegistrar(ImportBeanDefinitionRegistrar registrar, AnnotationMetadata importingClassMetadata) {
    this.importBeanDefinitionRegistrars.put(registrar, importingClassMetadata);
}

最终是将 ImportBeanDefinitionRegistrar 实现类放入了 ConfigurationClass 类中的 importBeanDefinitionRegistrars 属性中,那么最终是什么时候注册成 BeanDefinition 的呢?

这个就得回到 org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions 方法中了, 这里通过流程图梳理一下解析和注册两个步骤的流程

sequenceDiagram autonumber participant A as ConfigurationClassPostProcessor participant B as ConfigurationClassParser participant C as ConfigurationClassBeanDefinitionReader activate A A->>A: processConfigBeanDefinitions() A->>B: parse() B->>B: doProcessConfigurationClass() A->>C: loadBeanDefinitions() C->>C: loadBeanDefinitionsFromRegistrars() deactivate A
  1. 整体逻辑是在 ConfigurationClassPostProcessorprocessConfigBeanDefinitions() 方法中执行的
  2. 对于 @ComponentScan, @Import 等解析是在 ConfiguraionClassParserdoProcessConfigurationClass() 方法中解析的
  3. 对于 ImportBeanDefinitionRegistrar 类型的类在第二步解析完成之后放入了 ConfigurationClass 类型(就是将 ImportBeanDefinitionRegistrar 类型的实现类转换成 ConfigurationClass 类型而已)中的 importBeanDefinitionRegistrars 属性中
  4. 最后在 ConfigurationClassBeanDefinitionReader 类的 loadBeanDefinitionsFromRegistrars() 方法中会进行 BeanDefinition 的注册

至此,解析和注册两个步骤的流程就解析完了

相关推荐
佳腾_3 分钟前
【Web应用服务器_Tomcat】二、Tomcat 核心配置与集群搭建
java·前端·中间件·tomcat·web应用服务器
聂 可 以3 分钟前
IntelliJ IDEA修改实体类成员变量的名称(引入了该实体类的全部文件也会自动更新变量的名称)
java·ide·intellij-idea
冰茶_5 分钟前
C#中常见的设计模式
java·开发语言·microsoft·设计模式·微软·c#·命令模式
.似水22 分钟前
2025.4.22_C_可变参数列表
java·c语言·算法
禅与Bug的修复艺术24 分钟前
JAVA后端开发常用的LINUX命令总结
java·linux·面试·java面试·后端开发·java后端·面试经验
佩奇的技术笔记30 分钟前
Java学习手册:Java开发常用的内置工具类包
java
triticale42 分钟前
【蓝桥杯】P12165 [蓝桥杯 2025 省 C/Java A] 最短距离
java·蓝桥杯
Felven42 分钟前
A. Ideal Generator
java·数据结构·算法
秋野酱1 小时前
基于 Spring Boot 的银行柜台管理系统设计与实现(源码+文档+部署讲解)
java·spring boot·后端
JAVA学习通1 小时前
JAVA多线程(8.0)
java·开发语言