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

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

相关推荐
有梦想的骇客5 小时前
书籍“之“字形打印矩阵(8)0609
java·算法·矩阵
yours_Gabriel6 小时前
【java面试】微服务篇
java·微服务·中间件·面试·kafka·rabbitmq
hashiqimiya7 小时前
android studio中修改java逻辑对应配置的xml文件
xml·java·android studio
liuzhenghua668 小时前
Python任务调度模型
java·运维·python
結城8 小时前
mybatisX的使用,简化springboot的开发,不用再写entity、mapper以及service了!
java·spring boot·后端
小前端大牛马8 小时前
java教程笔记(十一)-泛型
java·笔记·python
东阳马生架构8 小时前
商品中心—2.商品生命周期和状态的技术文档
java
星辰离彬9 小时前
Java 与 MySQL 性能优化:MySQL 慢 SQL 诊断与分析方法详解
java·spring boot·后端·sql·mysql·性能优化
q_19132846959 小时前
基于Springboot+Vue的办公管理系统
java·vue.js·spring boot·后端·intellij idea