Spring @Import 机制
@Import
注解是 Spring 3.0 引入的一个新注解,用于 import Configuration
,使用了这个注解,相当于 Java 中的 import
关键字,可以导入 Spring 的 bean 到 IOC 容器中。
Spring Boot 的自动装配就是基于 @Import
机制。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
Class>[] value();
}
@Import
注解的解析
在简单的 demo 项目中,查看 @Import
注解的引用,除了作为注解使用,就只有在 ConfigurationClassParser
这个类中使用:
/**
* @param sourceClass 需要搜索的 class
* @param imports 到目前为止已经找到的 imports
* @param visited 去重,可能有些已经解析过了
*/
private void collectImports(SourceClass sourceClass, Set imports, Set visited) throws IOException {
if (visited.add(sourceClass)) {
// 递归查找当前 class 的注解以及注解的注解中的 @Import
for (SourceClass annotation : sourceClass.getAnnotations()) {
String annName = annotation.getMetadata().getClassName();
if (!annName.equals(Import.class.getName())) {
collectImports(annotation, imports, visited);
}
}
// 添加当前 class 的 @Import 注解 import 进来的类
imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
}
}
只有这个方法中对 @Import
注解进行了解析,内部逻辑就是递归查找当前类的注解,包括当前类的注解的注解(类似于 Java 中的继承),找到所有的 @Import
标注的 class 并且加到 imports
中。
@Import
的解析流程
查看调用 collectImports
的方法,发现就是 collectImports
上方的 getImports
方法,而 getImports
方法又在 doProcessConfigurationClass
中被调用,doProcessConfigurationClass
又被 processConfigurationClass
调用,processConfigurationClass
又在 parse
方法中调用,parse
在 ConfigurationClassPostProcessor
类的 processConfigBeanDefinitions
中被调用,继续查看是在 postProcessBeanDefinitionRegistry
和 postProcessBeanFactory
中被调用,通过 debug 发现是在 postProcessBeanDefinitionRegistry
中调用,再往上是 PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors
,而这个方法是在 AbstractApplicationContext.invokeBeanFactoryPostProcessors
中被调用,整理整个流程如下图所示:
sequenceDiagram
AbstractApplicationContext ->> AbstractApplicationContext:refresh
AbstractApplicationContext ->> AbstractApplicationContext:invokeBeanFactoryPostProcessors
AbstractApplicationContext ->> PostProcessorRegistrationDelegate:invokeBeanFactoryPostProcessors
PostProcessorRegistrationDelegate ->> PostProcessorRegistrationDelegate:invokeBeanDefinitionRegistryPostProcessors
PostProcessorRegistrationDelegate ->> ConfigurationClassPostProcessor :postProcessBeanDefinitionRegistry
ConfigurationClassPostProcessor ->> ConfigurationClassPostProcessor:processConfigBeanDefinitions
ConfigurationClassPostProcessor ->> ConfigurationClassParser:parse
ConfigurationClassParser ->> ConfigurationClassParser:processConfigurationClass
ConfigurationClassParser ->> +ConfigurationClassParser:doProcessConfigurationClass
ConfigurationClassParser ->> ConfigurationClassParser:getImports
ConfigurationClassParser ->> -ConfigurationClassParser:processImports
获取到所有的 @Import
中的 value 之后,再执行 processImports
方法:
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection importCandidates, boolean checkForCircularImports) {
if (importCandidates.isEmpty()) {
return;
}
if (checkForCircularImports && isChainedImportOnStack(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
// importStack 用来进行递归循环处理,可能当前处理的 import 中 import 进来了新的 import
this.importStack.push(configClass);
try {
// 遍历所有被 import 的类,并根据所属类型执行对应的操作
// 如果是 ImportSelector,执行 select 操作并递归处理
// 如果是 ImportBeanDefinitionRegistrar,添加到当前 configClass 的 importBeanDefinitionRegistrars 中
// 否则其他所有的都当做 Configuration 来处理,调用 processConfigurationClass
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 = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
ParserStrategyUtils.invokeAwareMethods(
selector, this.environment, this.resourceLoader, this.registry);
//
if (selector instanceof DeferredImportSelector) {
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
}
else {
// ImportSelector 返回的是类名数组,可能又返回了一个 ImportSelector,所以递归处理
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection importSourceClasses = asSourceClasses(importClassNames);
processImports(configClass, currentSourceClass, importSourceClasses, 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 =
BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
ParserStrategyUtils.invokeAwareMethods(
registrar, this.environment, this.resourceLoader, this.registry);
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
else {
// 当做 @Configuration class 处理
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
processConfigurationClass(candidate.asConfigClass(configClass));
}
}
}
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();
}
}
}
常见的 @Import
用的 value
都是 ImportSelector
类型的,比如 @EnableAutoConfiguration
就用了 AutoConfigurationImportSelector
,而在 AutoConfigurationImportSelector
中的 selectImports
方法仅仅是从 spring.factories
文件中读取 org.springframework.boot.autoconfigure.EnableAutoConfiguration
对应的 value 并做简单处理后返回,由 Spring 自己去处理这些 value,而开发者想要有自己的 EnableXxx
注解,在 /resources/META-INF/spring.factories
文件中添加如下信息,Spring Boot 就会加载这个 Xxx
类并交给 Spring 处理:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=Xxx
@Import
注解并不是 Spring Boot 独有的,但是 Spring Boot 借 @Import
机制来自动加载处理自动装配的类,进而实现自动装配。(ps: EnableXxx
模式也不是 Spring Boot 独有的,Spring Framework 中就已经有这样模式的使用了,Spring Boot 基于此进行扩展)
了解了 @Import
机制后,接下来继续深入 Spring Boot 的自动装配机制。