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 的注册

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

相关推荐
救救孩子把14 分钟前
深入理解 Java 对象的内存布局
java
落落落sss16 分钟前
MybatisPlus
android·java·开发语言·spring·tomcat·rabbitmq·mybatis
万物皆字节22 分钟前
maven指定模块快速打包idea插件Quick Maven Package
java
夜雨翦春韭29 分钟前
【代码随想录Day30】贪心算法Part04
java·数据结构·算法·leetcode·贪心算法
我行我素,向往自由35 分钟前
速成java记录(上)
java·速成
一直学习永不止步41 分钟前
LeetCode题练习与总结:H 指数--274
java·数据结构·算法·leetcode·数组·排序·计数排序
邵泽明42 分钟前
面试知识储备-多线程
java·面试·职场和发展
程序员是干活的1 小时前
私家车开车回家过节会发生什么事情
java·开发语言·软件构建·1024程序员节
煸橙干儿~~1 小时前
分析JS Crash(进程崩溃)
java·前端·javascript
2401_854391081 小时前
Spring Boot大学生就业招聘系统的开发与部署
java·spring boot·后端