Spring源码阅读 - @Import 一个继承 ImportSelector 接口的类
处理逻辑在 org.springframework.context.annotation.ConfigurationClassParser#processImports
。
暂时略其子接口 DeferredImportSelector
1. ImportSelector 接口
selectImports 方法返回的字符串数组,每个字符串被视为一个要被注入容器的类的全限定名。
public interface ImportSelector {
/**
* Select and return the names of which class(es) should be imported based on
* the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
* @return the class names, or an empty array if none
*/
String[] selectImports(AnnotationMetadata importingClassMetadata);
/**
* Return a predicate for excluding classes from the import candidates, to be
* transitively applied to all classes found through this selector's imports.
* <p>If this predicate returns {@code true} for a given fully-qualified
* class name, said class will not be considered as an imported configuration
* class, bypassing class file loading as well as metadata introspection.
* @return the filter predicate for fully-qualified candidate class names
* of transitively imported configuration classes, or {@code null} if none
* @since 5.2.4
*/
@Nullable
default Predicate<String> getExclusionFilter() {
return null;
}
}
2. 处理逻辑
先实例化这个被导入的类,然后执行 selectImports,根据全限定名数组加载 class 数组,并将这些 class 当做配置类解析一遍。
注意到这个被导入的类直接被实例化了,BD 没有注入容器,实例也没有注入容器。
问题: 导入的 class 数组被当做配置类解析,但是没有被注入容器啊,注入容器的时机在哪?
// Import 的类继承了 ImportSelector 接口
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);
}
// 继承了 DeferredImportSelector 接口(ImportSelector 的子接口)
if (selector instanceof DeferredImportSelector) {
// ...
}
else {
// 否则立即处理,将返回的字符串数组当做类全限定名数组,加载对应的 class 入内存
// 传入的信息是使用了 @Import 注解的类的信息
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
// 把这些加载的类当做直接 @Import 的类进行处理,注意到原 @Import 的类并未被注入容器
processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
}
}
3. BD 注入时机
注意到上面讲 selector.selectImports 要注入的类重新调用 processImports 处理了,那么这些类和被配置类直接 @Import 没有什么区别,也意味着若这个类是普通类, 那么走普通类@Import 的逻辑,继承了 ImportSelector 接口则走当前的处理逻辑,也意味着这个类不会被注入容器。
若是普通类,则和和 @Impoprt 导入普通类一样,需要等到 ConfigurationClassPostProcessor
显式调用 this.reader.loadBeanDefinitions(configClasses)
时会被解析并注入容器。