ImportSelector接口是Spring中導入外部配置的核心接口,SpringBoot的自動化配置和@EnableXXX(功能性註解)核心原理都是它。
該接口只有一個方法,返回需要Import的類的完整類名數組。
package org.springframework.context.annotation;
import org.springframework.core.type.AnnotationMetadata;
public interface ImportSelector {
String[] selectImports(AnnotationMetadata importingClassMetadata);
}
根據接口註釋可以看出:其主要作用是收集需要導入的配置類。
如果該接口的實現類同時實現Aware
接口(如:EnvironmentAware,BeanFactoryAware ,BeanClassLoaderAware,ResourceLoaderAware),那麼在調用其selectImports
方法之前先調用上述接口中的方法。
如果需要在所有的@Configuration
處理完在導入時可以實現DeferredImportSelector
接口。
調用鏈
源碼分析
調用selectImports
方法的地方在processImports
,該方法處理@Import
註解的解析,如果@Import
裏導入的是ImportSelector
接口,則會調用selectImports
方法獲取Import的類。
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass, Collection<SourceClass> importCandidates, boolean checkForCircularImports) throws IOException {
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)) { // Import註解中配置的是ImportSelector類型
Class<?> candidateClass = candidate.loadClass();
ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class); // 實例化
ParserStrategyUtils.invokeAwareMethods(selector, this.environment, this.resourceLoader, this.registry); // 如果該類有實現對應的Aware接口,則注入對應的屬性
if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) { // DeferredImportSelector類型的放到集合中待後續處理
this.deferredImportSelectors.add(new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
}
else { // 直接調用selectImports方法取得對應的類
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
processImports(configClass, currentSourceClass, importSourceClasses, false);
}
}
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// 如果是ImportBeanDefinitionRegistrar類型
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相關的類
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();
}
}
}
上述processImports
方法在doProcessConfigurationClass
方法中調用:
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
//....
processImports(configClass, sourceClass, getImports(sourceClass), true);
//....
return null;
}
依次往上找,發現的ConfigurationClassParser
類入口方法是:parse
,如註釋中描述的,前面一大塊代碼都是解析@Import
,後面單獨對接口DeferredImportSelector
解析:
public void parse(Set<BeanDefinitionHolder> configCandidates) {
this.deferredImportSelectors = new LinkedList<DeferredImportSelectorHolder>();
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) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException("Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
}
}
// 處理DeferredImportSelector接口的解析
processDeferredImportSelectors();
}