Spring版本: 6.0.6
DeferredImportSelector
是 ImportSelector
的子接口,所以需要先了解 ImportSelector
的作用,可以参考Spring中Import注解源码解析 - 掘金 (juejin.cn)
简述 ImportSelector 的作用
java
public interface ImportSelector {
String[] selectImports(AnnotationMetadata importingClassMetadata);
@Nullable
default Predicate<String> getExclusionFilter() {
return null;
}
}
这里直接总结一下 ImportSelector
的作用,selectImports()
方法返回值是需要导入的类的全限定类名组成的数组,也就是可以批量注册普通类给 Spring
进行管理, getExclusionFilter()
则是可以过滤掉不想要注册的类
DeferredImportSelector 和 ImportSelector 的区别是什么
先看一下 DeferredImportSelector
接口
java
public interface DeferredImportSelector extends ImportSelector {
@Nullable
default Class<? extends Group> getImportGroup() {
return null;
}
interface Group {
// 第二个参数 selector 就是对应 DeferredImportSelector 实现类
void process(AnnotationMetadata metadata, DeferredImportSelector selector);
// 返回需要被导入的类全限定类名,就和 ImportSelector 接口中的 selectImports 方法类似
Iterable<Entry> selectImports();
// Entry 就是用来保存需要导入的类的全限定类名和 元数据信息
class Entry {
private final AnnotationMetadata metadata;
private final String importClassName;
}
}
}
-
表面上看来
DeferredImportSelector
与ImportSelector
的区别是DeferredImportSelector
中多了一个内部接口 -
实际是是
DeferredImportSelector
和ImportSelector
的执行时机不一样ImportSelector
是在解析对应的实现类时就会直接处理需要导入的类DeferredImportSelector
是提供了一种延迟导入配置类的机制,SpringBoot
的自动装配就是用到了DeferredImportSelector
延迟导入的功能,这个延迟的意思是,非自动装配的类都解析完成之后才会去导入自动装配的类,具体源码会在下面说明
-
除了延迟导入机制,如果
DeferredImportSelector
实现类没有实现Group
接口的话,其他方法和ImportSelector
一样,因为当没有实现Group
接口时会使用Spring
提供的默认的实现类DefaultDeferredImportSelectorGroup
, 可以从下面代码看到默认的实现中process()
方法直接调用的就是selector.selectImports()
方法,其实就是直接调用的就是ImportSelector
接口的selectImports()
方法,因此此时和ImportSelector
接口在功能上是一致的【当然,执行时机还是不同】
java
private static class DefaultDeferredImportSelectorGroup implements Group {
private final List<Entry> imports = new ArrayList<>();
@Override
public void process(AnnotationMetadata metadata, DeferredImportSelector selector) {
for (String importClassName : selector.selectImports(metadata)) {
this.imports.add(new Entry(metadata, importClassName));
}
}
@Override
public Iterable<Entry> selectImports() {
return this.imports;
}
}
DeferredImportSelector 使用示例
java
// 普通的类,这个类没有任何注解,就是为了让 DeferredImportSelector 来导入的
public class DefImportBean {
public void sayHello() {
System.out.println("DefImportBean sayHello");
}
}
// 实现了 DeferredImportSelector 接口的类,这个类的作用是为了导入 DefImportBean 类
public class CusDeferredImportSelector implements DeferredImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
System.out.println("实际导入 --- 2");
return new String[]{DefImportBean.class.getName()};
}
// 自定义导入分组,这代表不使用默认的DefaultDeferredImportSelectorGroup, 如果没有导入就算写了 Group 实现类也不会使用的
@Override
public Class<? extends Group> getImportGroup() {
return CusImportSelectorBeanGroup.class;
}
private static class CusImportSelectorBeanGroup implements DeferredImportSelector.Group {
// 用来保存需要导入的类
private final List<Entry> instanceImports = new ArrayList<>();
@Override
public void process(AnnotationMetadata metadata, DeferredImportSelector selector) {
System.out.println("使用了自定义的分组 ---- 1");
for (String importClassName : selector.selectImports(metadata)) {
this.instanceImports.add(new Entry(metadata, importClassName));
}
}
@Override
public Iterable<Entry> selectImports() {
System.out.println("真正返回 -- 3");
return instanceImports;
}
}
}
// 主类,使用了 Import 导入了 实现了 CusDeferredImportSelector 接口的类
@ComponentScan
@Configuration
@Import(CusDeferredImportSelector.class)
public class DefImportMain {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(DefImportMain.class);
DefImportBean defImportBean = context.getBean(DefImportBean.class);
defImportBean.sayHello();
}
}
执行结果:
lua
使用了自定义的分组 ---- 1
实际导入 --- 2
真正返回 -- 3
DefImportBean sayHello
从打印结果中也可以看到实现了 DeferredImportSelector
接口的类的执行顺序如下
DeferredImportSelector 执行流程源码解析
在源码分析之前先熟悉一下会涉及到的几个类
java
private static class DeferredImportSelectorHolder {
// 配置主类,在上面的示例中就是 DefImportMain 对应的 ConfigurationClass
private final ConfigurationClass configurationClass;
// 保存的就是 DeferredImportSelector 实现类
private final DeferredImportSelector importSelector;
}
// 前面有说过 DeferredImportSelector 是会延迟注入的,既然要延迟,那就需要先将其存在一个地方,DeferredImportSelectorHandler 类就是保存所有的 DeferredImportSelector 实现类
private class DeferredImportSelectorHandler {
private List<DeferredImportSelectorHolder> deferredImportSelectors = new ArrayList<>();
}
DeferredImportSelector
的解析其实是通过 BeanFactory
的后置处理器实现的,对应的 BeanFactory
后置处理器类是 ConfigurationClassPostProcessor
,而解析 Import
注解的类则是 ConfigurationClassParser
,这里再简单写一下解析顺序流程
csharp
AbstractApplicationContext#refresh
AbstractApplicationContext#invokeBeanFactoryPostProcessors
public PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors
private PostProcessorRegistrationDelegate#invokeBeanDefinitionRegistryPostProcessors
ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry
ConfigurationClassPostProcessor#processConfigBeanDefinitions
ConfigurationClassParser#parse
for 循环 start[解析单个类]
ConfigurationClassParser#parse(AnnotationMetadata, java.lang.String)
ConfigurationClassParser#processImports (1)
for 循环结束
DeferredImportSelectorHandler#process (2)
java
// ConfigurationClassParser 类
public void parse(Set<BeanDefinitionHolder> configCandidates) {
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
if (bd instanceof AnnotatedBeanDefinition annotatedBeanDef) {
// 这里会对 DeferredImportSelector 进行缓存,缓存在 DeferredImportSelectorHandler 类中
parse(annotatedBeanDef.getMetadata(), holder.getBeanName());
}
else if (bd instanceof AbstractBeanDefinition abstractBeanDef && abstractBeanDef.hasBeanClass()) {
parse(abstractBeanDef.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 的实际处理
this.deferredImportSelectorHandler.process();
}
先简单总结一下:
- 上述流程中
(1)
位置就是处理ImportSelector
的地方, 同时也是缓存DeferredImportSelector
实现类的地方 - 上述流程中
(2)
位置就是实际处理DeferredImportSelector
的地方, 可以看到(2)
位置是在循环外面,也就是等所有的BeanDefinition
都解析完成之后再处理的,所以才说DeferredImportSelector
实现接口有延迟导入的功能
接下来就直接看 processImports
方法【下面所有列出代码都省略了和本次分析无关的代码】
java
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
boolean checkForCircularImports) {
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);
// 这里就是处理 DeferredImportSelector 的逻辑
if (selector instanceof DeferredImportSelector deferredImportSelector) {
// 这里并没有真正的处理,只是将其存在 DeferredImportSelectorHandler 对象中
this.deferredImportSelectorHandler.handle(configClass, deferredImportSelector);
}
}
}
}
上面代码就是缓存 DeferredImportSelector
实现类的处理逻辑,接下来就是处理 DeferredImportSelector
实现类的逻辑了,从前面代码已经知道了,是会在 DeferredImportSelectorHandler
中进行处理, 处理过程中会涉及到 Group
相关的类,所以这里先看一下
java
private static class DeferredImportSelectorGrouping {
// 这里就保存了 Group 实现类
private final DeferredImportSelector.Group group;
private final List<DeferredImportSelectorHolder> deferredImports = new ArrayList<>();
}
private class DeferredImportSelectorGroupingHandler {
private final Map<Object, DeferredImportSelectorGrouping> groupings = new LinkedHashMap<>();
}
接下来看 this.deferredImportSelectorHandler.process();
这个处理
java
// DeferredImportSelectorHandler 类
public void process() {
List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
this.deferredImportSelectors = null;
try {
if (deferredImports != null) {
DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
// 这里涉及到 Group 的注册 (1)
deferredImports.forEach(handler::register);
// 真正的处理 导入逻辑 (2)
handler.processGroupImports();
}
}
finally {
this.deferredImportSelectors = new ArrayList<>();
}
}
先看一下 Group
注册的相关逻辑,也就是 (1)
中的相关逻辑
java
private class DeferredImportSelectorGroupingHandler {
private final Map<Object, DeferredImportSelectorGrouping> groupings = new LinkedHashMap<>();
private final Map<AnnotationMetadata, ConfigurationClass> configurationClasses = new HashMap<>();
public void register(DeferredImportSelectorHolder deferredImport) {
/**
* deferredImport.getImportSelector() 获取的就是 DeferredImportSelector 实现类,再调用 getImportGroup() 就是想要获取我们自己实现了 Group 的类
* 这也是为什么前面说如果单纯实现了 Group 类是没有用的,还需要使用 getImportGroup() 方法返回才行,就是因为这里会调用 getImportGroup() 方法获取我们自己实现了 Group 的类
*/
Class<? extends Group> group = deferredImport.getImportSelector().getImportGroup();
DeferredImportSelectorGrouping grouping = this.groupings.computeIfAbsent(
(group != null ? group : deferredImport),
// 这里是创建 Group, 如果没有创建创建就是使用默认的,具体看下面 createGroup 方法
key -> new DeferredImportSelectorGrouping(createGroup(group)));
grouping.add(deferredImport);
this.configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getConfigurationClass());
}
private Group createGroup(@Nullable Class<? extends Group> type) {
// 这里是创建 Group, 如果我们自己没有创建,那么 type 就会是 null, 也就会使用默认的 DefaultDeferredImportSelectorGroup 类
Class<? extends Group> effectiveType = (type != null ? type : DefaultDeferredImportSelectorGroup.class);
return ParserStrategyUtils.instantiateClass(effectiveType, Group.class,
ConfigurationClassParser.this.environment,
ConfigurationClassParser.this.resourceLoader,
ConfigurationClassParser.this.registry);
}
}
接下来就是看 processGroupImports()
方法
java
// DeferredImportSelectorGroupingHandler 类
public void processGroupImports() {
for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
Predicate<String> exclusionFilter = grouping.getCandidateFilter();
// grouping.getImports() 中就会实际调用 Group 类的 process() 方法
// 如果使用的是默认的 Group 实现类,实际就会调用 ImportSelector 中的 selectImports 方法,
//getImports() 方法的返回值就是我们自己实现了 Group 类的 selectImports 方法的返回值
grouping.getImports().forEach(entry -> {
ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata());
try {
// 拿到 扫描到的类的全限定类名,然后就按照 Import 普通类来处理,也就是当作 @Configuration 注解的类来处理
processImports(configurationClass, asSourceClass(configurationClass, exclusionFilter),
Collections.singleton(asSourceClass(entry.getImportClassName(), exclusionFilter)),
exclusionFilter, false);
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configurationClass.getMetadata().getClassName() + "]", ex);
}
});
}
}
public Iterable<Group.Entry> getImports() {
for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
// 在这里开始调用 Group 的 process() 方法
this.group.process(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getImportSelector());
}
return this.group.selectImports();
}
至此就解析完了 Spring
中 DeferredImportSelectory
的整体调用流程