自学内容网 自学内容网

Spring - @Import注解


本文源码基于spring-context-5.3.36 版本

基本用法

  1. 直接填class数组方式
    @Import注解填入要导入的类的Class即可
@Import({ A.class , B.class... })
@Configuration
public class TestDemo {

}
  1. ImportSelector方式
    实现ImportSelector接口,selectImport返回需要导入的类的全限定名称
public class MyImportSelector implements ImportSelector {

    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        return new String[0];
    }
}

然后@Import中指定ImportSelector的实现类的Class

  1. ImportBeanDefinitionRegistrar方式
    实现ImportBeanDefinitionRegistrar接口
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
      // 通过 BeanDefinitionRegistry 构造 BeanDefinition 并进行注册
    }
}

然后@Import中指定ImportBeanDefinitionRegistrar的实现类的Class
总结

  1. 以上三种用法方式皆可混合在一个@Import中使用,注意第一种和第二种都是以全类名的方式注册,而第三中可自定义名称
  2. 第一种最简单,第二种较简单,第三种需要操作BeanDefinition,较为复杂
  3. @Import注解不一定非要和@Configuration搭配使用,也可以和@Component等注解使用,效果一样
@Import({ A.class , B.class... })
@Component
public class TestDemo {

}

源码分析

入口

BeanDefinitionRegistryPostProcessor执行阶段生效

例如 ConfigurationClassPostProcessor

ConfigurationClassPostProcessor

ConfigurationClass

代表定义的@Configuration修饰的类,包含一些bean方法。配置加载都有一个解析过程,对ConfigurationClass的解析就是由ConfigurationClassParser#parse方法完成的,它会解析每个配置类上的配置,包括@Import注解这个配置

public void parse(Set<BeanDefinitionHolder> configCandidates) {
// 拿到所有的BeanDefinition
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
if (bd instanceof AnnotatedBeanDefinition) {
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
}
else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
}
else {
parse(bd.getBeanClassName(), holder.getBeanName());
}
}
catch (BeanDefinitionStoreException ex) {
...
}
}

this.deferredImportSelectorHandler.process();
}

在这里插入图片描述

SourceClass

org.springframework.context.annotation.ConfigurationClassParser.SourceClass主要用于处理和解析配置类的源信息

private class SourceClass implements Ordered {
// Class或者MetadataReader
private final Object source;
// 注解元数据
private final AnnotationMetadata metadata;
}

主要作用如下:

  1. 表示源类:SourceClass 代表 Spring 配置类中的一个类,它封装了获取该类相关的源信息的方法,比如类的名称、注解、方法等。
  2. 提供元信息:SourceClass 提供了一些元数据的方法,帮助开发者获取类的详细信息,比如父类、实现的接口、注解信息等。

getImports

SourceClass可以简单的理解为java里的Class,同样,SourceClass可以代表普通类,也可以代表注解。同时SourceClass携带有一些注解元数据信息。其实getImports方法的过程就和根据反射获取一个类上的所有注解(包括修饰注解的注解)这个过程差不多

使用深度优先遍历

  1. 首先定义一个visited记录已经访问过的 SourceClass
  2. 对于每个访问的 SourceClass ,如果它被@Import注解修饰,则获取@Import注解的属性值
private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {
Set<SourceClass> imports = new LinkedHashSet<>();
Set<SourceClass> visited = new LinkedHashSet<>();
collectImports(sourceClass, imports, visited);
return imports;
}

调用的collectImports()方法是一个递归操作,从第一个SourceClass开始,获取其所有注解,然后调用collectImports递归进行收集。因为@Import可能放在注解上形成复合注解

private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited)
throws IOException {

if (visited.add(sourceClass)) {
for (SourceClass annotation : sourceClass.getAnnotations()) {
String annName = annotation.getMetadata().getClassName();
if (!annName.equals(Import.class.getName())) {
collectImports(annotation, imports, visited);
}
}
imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
}
}

最后返回的imports就是从根SourceClass开始遍历的所有@Import注解的值,这个值是一个java中Class的集合

processImports

getImports的返回值作为processImports方法的第三个参数

/**
 * 
 * @param configClass配置类,一般是@Configuration修饰的类
 * @param currentSourceClass  扫描@Import注解的根节点
 * @param importCandidates  currentSourceClass上携带的所有@Import注解的属性值
 * @param exclusionFilter    用于排除
 * @param checkForCircularImports  是否检查@Import形成环的情况
 */
private void processImports(ConfigurationClass configClass,
                            SourceClass currentSourceClass,
                            Collection<SourceClass> importCandidates,
                            Predicate<String> exclusionFilter,
                            boolean checkForCircularImports) {
if (importCandidates.isEmpty()) {
return;
}
// 检测可能存在的循环@Import情况
if (checkForCircularImports && isChainedImportOnStack(configClass)) {
this.problemReporter.error(
new CircularImportProblem(configClass, this.importStack));
} else {
// 下面代码保留了整体结构,省略了不重要的细节
this.importStack.push(configClass);
try {
for (SourceClass candidate : importCandidates) {
// 一些if/else判断
if (candidate.isAssignable(ImportSelector.class)) {
...
} else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
...
} else {
// 既不是ImportSelector也不是ImportBeanDefinitionRegistrar
// 当作@Configuration类处理
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
// 继续走处理配置类的流程,会继续进行processImports方法
processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
}
}
} catch (BeanDefinitionStoreException e) {
// 省略
}
finally {
this.importStack.pop();
}
}
}

处理 ImportSelector

如果是ImportSelector

处理细节如下所示

// 实例化ImportSelector对象
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);
}

// 区分是否是DeferredImportSelector
if (selector instanceof DeferredImportSelector) {
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
} else {
// 所有导入的类名称
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
// 将ImportSelector返回的类名转换为SourceClass
// 使用Class.forName转换成Class实例,构造SourceClass对象
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
// 递归调用processImports
processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
}
ImportSelector 接口
public interface ImportSelector {

/**
 * 根据配置类的元数据信息,返回类的全限定名称
 * importingClassMetadata:@Import注解修饰的那个类
 **/
String[] selectImports(AnnotationMetadata importingClassMetadata);

@Nullable
default Predicate<String> getExclusionFilter() {
return null;
}
}
DeferredImportSelector

Deferred意思是延迟,相比于ImportSelector会立即被处理,DeferredImportSelector的selectImport方法会在当前配置类所有的Bean信息解析完毕后才进行处理
在这里插入图片描述
DeferredImportSelector的作用是用于调整Import的Bean和当前配置类配置的Bean的先后关系

因为有条件注解的存在,所以需要Bean之间的注册有先后关系,条件注解才能发挥作用

处理 ImportBeanDefinitionRegistrar

如果SourceClass类型是ImportBeanDefinitionRegistrar的实现类简单,则调用org.springframework.context.annotation.ConfigurationClass#addImportBeanDefinitionRegistrar方法

// 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());
ImportBeanDefinitionRegistrar 接口

ImportBeanDefinitionRegistrar接口用于手动注册BeanDefinition

public interface ImportBeanDefinitionRegistrar {

/**
 * 注册BeanDefinition
 * BeanDefinitionRegistryPostProcessor此时还未注册
 * @param importingClassMetadata 导入的类的注解元数据
 * @param registry 当前BeanDefinitionRegistry,即BeanFactory实现
 * @param importBeanNameGenerator 导入的Bean的命名策略
 */
default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry,
BeanNameGenerator importBeanNameGenerator) {

registerBeanDefinitions(importingClassMetadata, registry);
}

default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
}
}
  1. BeanNameGenerator:默认实现是ConfigurationClassPostProcessor#IMPORT_BEAN_NAME_GENERATOR或者通过ConfigurationClassPostProcessor#setBeanNameGenerator进行设置
  2. BeanDefinitionRegistry registry:其实就是BeanFactory

@Configuration配置类解析完毕后,下一步就是将让配置生效。如下图所示,通过ConfigurationClassBeanDefinitionReader来加载BeanDefinition
在这里插入图片描述
ConfigurationClassBeanDefinitionReader加载BeanDefinition的过程中条件注解会生效

public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
for (ConfigurationClass configClass : configurationModel) {
loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
}
}

private void loadBeanDefinitionsForConfigurationClass(
ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {

if (trackedConditionEvaluator.shouldSkip(configClass)) {
String beanName = configClass.getBeanName();
if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
this.registry.removeBeanDefinition(beanName);
}
this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
return;
}
// 处理已经导入的配置类
if (configClass.isImported()) {
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
// 配置类的所有@Bean修饰的方法
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
loadBeanDefinitionsForBeanMethod(beanMethod);
}
// @ImportedResources用于导入Spring的配置文件
// 而Spring的配置文件中也可以定义Bean
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
// 加载ImportBeanDefinitionRegistrar中需要注册的Bean
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}

处理Configuration

如果@Import的内容既不是ImportSelector也不是ImportBeanDefinitionRegistrar,那么就把它当作@Configuration修饰的类进行处理,无论它是不是真的被@Configuration修饰

// 既不是ImportSelector也不是ImportBeanDefinitionRegistrar
// 当作@Configuration类处理
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
// 继续走处理配置类的流程,会继续进行processImports方法
processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);

假如@Import的是一个普通类,比如下面这样,什么都没有。由于@Import的存在,SimpleClass还是会被当作Bean注册进容器

public class SimpleClass {
}

如果给它加上@Configuration配置类可以有的配置,它也是会生效的

public class SimpleClass {

    @Bean
    public A a() {
        return new A();
    }
}

原文地址:https://blog.csdn.net/qq_40926260/article/details/142646615

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!