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 到目前為止已經(jīng)找到的 imports
* @param visited 去重,可能有些已經(jīng)解析過了
*/
private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> 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
的方法,發(fā)現(xiàn)就是 collectImports
上方的 getImports
方法单芜,而 getImports
方法又在 doProcessConfigurationClass
中被調用犁柜,doProcessConfigurationClass
又被 processConfigurationClass
調用赁温,processConfigurationClass
又在 parse
方法中調用,parse
在 ConfigurationClassPostProcessor
類的 processConfigBeanDefinitions
中被調用袜匿,繼續(xù)查看是在 postProcessBeanDefinitionRegistry
和 postProcessBeanFactory
中被調用稚疹,通過 debug 發(fā)現(xiàn)是在 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 之后噪径,再執(zhí)行 processImports
方法:
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
if (importCandidates.isEmpty()) {
return;
}
if (checkForCircularImports && isChainedImportOnStack(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
// importStack 用來進行遞歸循環(huán)處理,可能當前處理的 import 中 import 進來了新的 import
this.importStack.push(configClass);
try {
// 遍歷所有被 import 的類梗顺,并根據(jù)所屬類型執(zhí)行對應的操作
// 如果是 ImportSelector车摄,執(zhí)行 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 返回的是類名數(shù)組敞贡,可能又返回了一個 ImportSelector摄职,所以遞歸處理
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> 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,而開發(fā)者想要有自己的 EnableXxx
注解鞠抑,在 /resources/META-INF/spring.factories
文件中添加如下信息忌警,Spring Boot 就會加載這個 Xxx
類并交給 Spring 處理:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=Xxx
@Import
注解并不是 Spring Boot 獨有的,但是 Spring Boot 借 @Import
機制來自動加載處理自動裝配的類箕速,進而實現(xiàn)自動裝配朋譬。(ps: EnableXxx
模式也不是 Spring Boot 獨有的,Spring Framework 中就已經(jīng)有這樣模式的使用了字柠,Spring Boot 基于此進行擴展)
了解了 @Import
機制后,接下來繼續(xù)深入 Spring Boot 的自動裝配機制扶关。