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

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

相关推荐
大数据编程之光17 分钟前
Flink Standalone集群模式安装部署全攻略
java·大数据·开发语言·面试·flink
爪哇学长31 分钟前
双指针算法详解:原理、应用场景及代码示例
java·数据结构·算法
ExiFengs35 分钟前
实际项目Java1.8流处理, Optional常见用法
java·开发语言·spring
paj12345678936 分钟前
JDK1.8新增特性
java·开发语言
繁依Fanyi1 小时前
简易安卓句分器实现
java·服务器·开发语言·算法·eclipse
慧都小妮子1 小时前
Spire.PDF for .NET【页面设置】演示:打开 PDF 时自动显示书签或缩略图
java·pdf·.net
m51271 小时前
LinuxC语言
java·服务器·前端
IU宝1 小时前
C/C++内存管理
java·c语言·c++
瓜牛_gn1 小时前
依赖注入注解
java·后端·spring
hakesashou1 小时前
Python中常用的函数介绍
java·网络·python