Spring Boot 注解——@Import
引言
Import 见名知意:导入某某东西。在平时看源码或者很多配置类上面都会出现@Import注解,功能就是和Spring XML 里面的一样。@Import注解是用来导入配置类或者一些需要前置加载的类。
使用方法
- 被标注@Configuration的配置类,@Configuration详细用法及作用请参考 Spring Boot注解——@Configuration
@Configuration public class DatabaseConfig { @Bean public DataSource dataSource() { // instantiate, configure and return DataSource } } @Configuration @Import(DatabaseConfig.class) public class AppConfig { private final DatabaseConfig dataConfig; public AppConfig(DatabaseConfig dataConfig) { this.dataConfig = dataConfig; } @Bean public MyBean myBean() { // reference the dataSource() bean method return new MyBean(dataConfig.dataSource()); } } - 实现
ImportSelector接口 - 实现
ImportBeanDefinitionRegistrar接口 - 在标注
@Componment、@Service、@Controller等一系列能够注入容器的注解的类,即常规组件类 (从 4.2 开始;类似于AnnotationConfigApplicationContext.register())
源码解析
// 所在的包
package org.springframework.context.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 指示要导入的一个或多个组件类——通常是@Configuration类。提供与 Spring XML 中的<import/>元素等效的功能。允许
* 导入@Configuration类、 ImportSelector和ImportBeanDefinitionRegistrar实现,以及常规组件类(从 4.2 开始;
* 类似于AnnotationConfigApplicationContext.register)。在导入的@Configuration类中声明的@Bean定义可以使用
* @Autowired来访问。bean本身可以自动装配,或者声明 bean 的配置类实例可以自动装配。后一种方法允许在
* @Configuration类方法之间进行显式的、IDE 友好的导航。可以在类级别声明或作为元注释声明。
* 如果需要导入 XML 或其他非@Configuration bean 定义资源,请改用@ImportResource注解
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
/**
* 需要导入的资源,可以是:
* @Configuration标注的配置类
* ImportSelector接口具体实现
* ImportBeanDefinitionRegistrar接口具体实现
* 常规组件类
*/
Class<?>[] value();
}
实现逻辑
以下内容引自一直打铁 ---- @Import注解的作用
这里主要看 ConfigurationClassParser.java 里的
doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)这个方法。具体定位到源码的310行代码(为啥不从头梳理,这里Spring 启动过程比较复杂,要是从头梳理,涉及的东西比较多,好多人看了就会累,放弃了,我们就单个点熟悉 ,最后再进行汇总)

根据程序的逻辑,肯定是先执行getImports(sourceClass)
getImports() 方法
在分析这个方法之前,我们先看一下 getImports 方法,这个方法就是获取所有的@import里面的类
这里是获取@import里面的类,大致流程如下:
- 定义一个
visited的集合,用作是否已经判断过的标志 - 这里就是获取
sourceClass上面的所有的annotation,并挨个判断, 如果不是@import,那就 进一步递归调用对应的annotation,直到全部结束 - 加载
sourceClass里面的@Import annotation里面对应的类名,最后返回。private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException { Set<SourceClass> imports = new LinkedHashSet<>(); Set<SourceClass> visited = new LinkedHashSet<>(); collectImports(sourceClass, imports, visited); return imports; } 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")); } }
processImports() 方法

大致的流程如下:
- 判断
importCandidates是否为空,为空 退出 - 判断
isChainedImportOnStack,如果为true,加入problemReporter里面的error,并退出 - 把当前的
configClass加入到ImportStack里面,ImportStack是继承了ArrayDeque和实现了ImportRegistry - 对
getImports里面获取到的 需要import的类 进行遍历 处理- 如果是
ImportSelector类型,首先实例一个ImportSelector对象,然后 对其进行Aware扩展(如果 实现了Aware接口) - 进一步判断 是否 是
DeferredImportSelector类型,如果是 ,加入到deferredImportSelectors里面,最后处理 ,这里可以看一下 方法parse(Set configCandidates), 里面最后一行才调用,这也就是 有的时候,如果想最后注入,就可以定义为deferredImportSelectors类型 - 如果 不是
DeferredImportSelector类型 ,那就 调用selectImports方法,获取到所有的需要 注入的类,这时 再次调用processImports方法,这里调用processImports方法,其实是把这些需要注入的类当成普通的@Configuration处理
- 如果是
- 如果是
ImportBeanDefinitionRegistrar类型,这里也是 先实例一个对象,然后加入到importBeanDefinitionRegistrars里面,后续会在ConfigurationClassBeanDefinitionReader这个类里面的loadBeanDefinitionsFromRegistrars方法处理的 - 如果上面两种类型都不是,那就是当初普通的 带有
@Configuration的类进行处理了
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
boolean checkForCircularImports) {
if (importCandidates.isEmpty()) {
return;
}
if (checkForCircularImports && isChainedImportOnStack(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
this.importStack.push(configClass);
try {
for (SourceClass candidate : importCandidates) {
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) {
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
}
else {
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
}
}
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 {
// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
// process it as an @Configuration class
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
}
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configClass.getMetadata().getClassName() + "]", ex);
}
finally {
this.importStack.pop();
}
}
}

浙公网安备 33010602011771号