目录

 

​一、注解用法​

​二、实例分析​

​三、源码追踪​



一、注解用法

@Import注解也是用来给容器注册组件的,使用@Import注解快速给容器中导入一个组件有三种方法

  1. 导入​​@Configuration​​注解的配置类使用​​@Import​​(要导入到容器中的组件):容器中就会自动注册这个组件,ID默认为全类名
  2. 导入​​ImportSelector​​的实现类:通过实现​​ImportSelector​​类,实现​​selectImports​​方法,返回需要导入组件的全类名数组
  3. 导入​​ImportBeanDefinitionRegistrar​​的实现类:通过实现​​ImportBeanDefinitionRegistrar​​类,实现​​registerBeanDefinitions​​方法手动注册Bean到容器中

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
Class<?>[] value();
}

通过注解源码可以看到,@Import注解作用在类上,并且参数可以是class类型的数组,从这里可以看出可以使用@Import注解一次导入多个组件到容器中

二、实例分析

从上面的注解用法来看,使用​​@Import​​注解给容器导入组件有三种方法,并且该注解作用在方法上,一次可以导入多个组件,因此,这里我们直接将三种方法都放在一个​​@Import​​注解来进行导入。如下案例需求:使用方法一注入​​User​​类、使用方法二注入​​Person​​类、使用方法三注入​​Animal​​类。

【1】导入​​@Configuration​​注解的配置类使用​​@Import​

// 启动类,通过打印容器中的Bean来判断是否注入
@Test
public void TestMain(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
String[] beanNames = applicationContext.getBeanDefinitionNames();
for (String beanName : beanNames) {
System.out.println(beanName);
}
}

// 待注入的User
public class User {
}

// 配置类
@Configuration
@Import(User.class) //使用@Import导入组件,ID默认是组件的全类名
public class AppConfig {
}

通过在配置类上使用​​@Import​​注解,将​​User​​给注入进容器中,运行启动类,可以看到容器中有​​User​​对象:

@Import注解 -【Spring底层原理】_递归

image-20210226164625069

【2】导入​​ImportSelector​​的实现类

导入​​ImportSelector​​的实现类需要实现​​ImportSelector​​类,自定义逻辑返回需要导入的组件,返回的字符串数组即是要注入的组件,添加修改如下代码:

// ImportSelector实现类
public class MyImportSelector implements ImportSelector {
/**
* @description 获取要导入到容器的组件全类名
* @author ONESTAR
* @date 2021/2/25 15:49
* @param annotationMetadata:当前标注@Import注解类的所有注解信息
* @throws
* @return java.lang.String[]
*/
public String[] selectImports(AnnotationMetadata annotationMetadata) {
return new String[]{"bean.Person"};
}
}

// 待注入的Person
public class Person {
}

// 配置类
@Configuration
@Import({User.class, MyImportSelector.class}) //使用@Import导入组件,ID默认是组件的全类名
public class AppConfig {
}

在​​ImportSelector​​实现类中获取要导入到容器的组件全类名,这里将​​ImportSelector​​实现类在配置类中使用​​@Import​​注解进行配置,运行启动类,可以看到容器中有​​Person​​对象:

@Import注解 -【Spring底层原理】_类名_02

image-20210227151242060

【3】导入​​ImportBeanDefinitionRegistrar​​的实现类

导入​​ImportBeanDefinitionRegistrar​​的实现类需要实现​​ImportBeanDefinitionRegistrar​​类,通过实现​​registerBeanDefinitions​​方法手动注册Bean到容器中,添加修改如下代码:

// ImportBeanDefinitionRegistrar实现类
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {

// 指定Bean的名称
RootBeanDefinition beanDefinition = new RootBeanDefinition(Animal.class);
beanDefinitionRegistry.registerBeanDefinition("Animal", beanDefinition);
}
}

// 待注入的Animal
public class Animal {
}

// 配置类
@Configuration
@Import({User.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class}) //使用@Import导入组件,ID默认是组件的全类名
public class AppConfig {
}

通过​​ImportBeanDefinitionRegistrar​​的实现类进行手动注册添加Bean,并在配置类中使用​​@Import​​注解进行配置,运行启动类,可以看到容器中有​​Animal​​对象:

@Import注解 -【Spring底层原理】_类名_03

image-20210227153057676

三、源码追踪

 

通过​​@Configuration​​注解,会进入到​​doProcessConfigurationClass​​方法,此时解析的是​​appConfigure​​,在​​doProcessConfigurationClass​​方法里面,有个执行​​@Import​​注解的方法,即​​processImports​

this.processImports(configClass, sourceClass, this.getImports(sourceClass), filter, true);

@Import注解执行的时机,解析配置类的时候,由​ConfigurationClassParser​当中的​processImports​来处理,在分析​​processImports​​方法之前,咱们先来看看参数​​getImports​​方法:

【1】getImports方法

进入源码查看方法,这个方法就是获取所有的​​@import​​ 里面的类,流程如下:

  1. 定义一个 visited 的集合,用作 是否已经 判断过的标志
  2. 这里就是获取sourceClass 上面的 所有的 annotation,并挨个判断, 如果不是 @import ,那就 进一步递归 调用 对应的 annotation,直到全部结束
  3. 加载sourceClass 里面 的@Import annotation 里面对应的类名 ,最后返回
// 获取所有的`@import` 里面的类
private Set<ConfigurationClassParser.SourceClass> getImports(ConfigurationClassParser.SourceClass sourceClass) throws IOException {
Set<ConfigurationClassParser.SourceClass> imports = new LinkedHashSet();
Set<ConfigurationClassParser.SourceClass> visited = new LinkedHashSet();
this.collectImports(sourceClass, imports, visited);
return imports;
}

// 这里就是获取sourceClass 上面的 所有的 annotation, 如果不是 @import ,那就 进一步递归 调用 对应的 annotation,直到全部结束
private void collectImports(ConfigurationClassParser.SourceClass sourceClass, Set<ConfigurationClassParser.SourceClass> imports, Set<ConfigurationClassParser.SourceClass> visited) throws IOException {
if (visited.add(sourceClass)) {
Iterator var4 = sourceClass.getAnnotations().iterator();

while(var4.hasNext()) {
ConfigurationClassParser.SourceClass annotation = (ConfigurationClassParser.SourceClass)var4.next();
String annName = annotation.getMetadata().getClassName();
if (!annName.equals(Import.class.getName())) {
this.collectImports(annotation, imports, visited);
}
}

imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
}

}

【2】processImports 方法

获取到​​@Import​​里面的类后,再来执行​​processImports​​方法,进入源码进行查看:

private void processImports(ConfigurationClass configClass, ConfigurationClassParser.SourceClass currentSourceClass, Collection<ConfigurationClassParser.SourceClass> importCandidates, Predicate<String> exclusionFilter, boolean checkForCircularImports) {
// 准备注入的候选类集合为空 直接返回
if (!importCandidates.isEmpty()) {
// 循环注入的检查
if (checkForCircularImports && this.isChainedImportOnStack(configClass)) {
this.problemReporter.error(new ConfigurationClassParser.CircularImportProblem(configClass, this.importStack));
} else {
// 当前configClass加入到ImportStack里面
this.importStack.push(configClass);

try {
Iterator var6 = importCandidates.iterator();

// 遍历注入的候选集合
while(var6.hasNext()) {
ConfigurationClassParser.SourceClass candidate = (ConfigurationClassParser.SourceClass)var6.next();
Class candidateClass;
// 如果是实现了ImportSelector接口的类
if (candidate.isAssignable(ImportSelector.class)) {
candidateClass = candidate.loadClass();
ImportSelector selector = (ImportSelector)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) {
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector)selector);
} else {
// 调用selector当中的selectImports方法,得到要注入的类的全限定名
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<ConfigurationClassParser.SourceClass> importSourceClasses = this.asSourceClasses(importClassNames, exclusionFilter);
this.processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
}
// 如果是ImportBeanDefinitionRegistrar 则configClass.addImportBeanDefinitionRegistrar 提前放到一个map当中
} else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar = (ImportBeanDefinitionRegistrar)ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class, this.environment, this.resourceLoader, this.registry);
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
} else {
// 如果是普通类
this.importStack.registerImport(currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
this.processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
}
}
} catch (BeanDefinitionStoreException var17) {
throw var17;
} catch (Throwable var18) {
throw new BeanDefinitionStoreException("Failed to process import candidates for configuration class [" + configClass.getMetadata().getClassName() + "]", var18);
} finally {
this.importStack.pop();
}
}

}
}

以上方法便是​​@Import​​注解的核心作用,可以总结为以下几点:

  1. 判断注入的候选集合 ​​importCandidates​​ 是否为空,为空则直接退出
  2. 循环注入检查:判断isChainedImportOnStack ,如果为true ,加入 problemReporter 里面的error ,并退出
  3. 把当前的 ​​configClass​​ 加入到 ​​ImportStack​​里面,​​ImportStack​​ 是继承了 ​​ArrayDeque​​ 和实现了 ​​ImportRegistry​
  4. 遍历注入的候选集合:对 ​​getImports​​ 里面获取到的 需要​​import​​ 的类 进行遍历 处理(三种类型的类执行不同的逻辑)
  5. 实现了ImportSelector接口的类,调用getExclusionFilter()方法,如果不为空,那么就进行过滤,过滤后调用​​selectImports​​方法,得到要注入的类的全限定名。根据类全限定名,得到类元信息。然后递归的调用processImports方法
  6. 实现了​​ImportBeanDefinitionRegistrar​​接口的类,会实例化这个类,放入集合​​importBeanDefinitionRegistrars​​当中
  7. 普通类型的类(上面两个都不满足),那么就把它当作是配置类来处理,调用​​processConfigurationClass​​方法,最终会放入到​​configurationClasses​​这个集合当中。
  8. 如果是 ​​ImportBeanDefinitionRegistrar​​ 类型,这里也是 先实例一个对象,然后加入到 ​​importBeanDefinitionRegistrars​​ 里面,后续 会在 ​​ConfigurationClassBeanDefinitionReader​​ 这个类里面 的 ​​loadBeanDefinitionsFromRegistrars​​ 方法处理的
  9. 如果上面两种类型都不是,那就是当初普通的 带有​​@Configuration​​ 的类进行处理了