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

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

相关推荐
卡尔特斯2 小时前
Android Kotlin 项目代理配置【详细步骤(可选)】
android·java·kotlin
白鲸开源2 小时前
Ubuntu 22 下 DolphinScheduler 3.x 伪集群部署实录
java·ubuntu·开源
ytadpole2 小时前
Java 25 新特性 更简洁、更高效、更现代
java·后端
纪莫3 小时前
A公司一面:类加载的过程是怎么样的? 双亲委派的优点和缺点? 产生fullGC的情况有哪些? spring的动态代理有哪些?区别是什么? 如何排查CPU使用率过高?
java·java面试⑧股
JavaGuide4 小时前
JDK 25(长期支持版) 发布,新特性解读!
java·后端
用户3721574261354 小时前
Java 轻松批量替换 Word 文档文字内容
java
白鲸开源4 小时前
教你数分钟内创建并运行一个 DolphinScheduler Workflow!
java
Java中文社群4 小时前
有点意思!Java8后最有用新特性排行榜!
java·后端·面试
代码匠心4 小时前
从零开始学Flink:数据源
java·大数据·后端·flink
间彧5 小时前
Spring Boot项目中如何自定义线程池
java