Spring中DeferredImportSelector源码解析

Spring版本: 6.0.6

DeferredImportSelectorImportSelector 的子接口,所以需要先了解 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;
        }
    }
}
  1. 表面上看来 DeferredImportSelectorImportSelector 的区别是 DeferredImportSelector 中多了一个内部接口

  2. 实际是是 DeferredImportSelectorImportSelector 的执行时机不一样

    1. ImportSelector 是在解析对应的实现类时就会直接处理需要导入的类
    2. DeferredImportSelector 是提供了一种延迟导入配置类的机制,SpringBoot 的自动装配就是用到了 DeferredImportSelector 延迟导入的功能,这个延迟的意思是,非自动装配的类都解析完成之后才会去导入自动装配的类,具体源码会在下面说明
  3. 除了延迟导入机制,如果 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 接口的类的执行顺序如下

flowchart TB A["Group实现类中的 process() 方法"] B["DeferredImportSelector实现类 中的 selectImports() 方法 (该方法不一定执行,当 Group 实现类中 有调用的时候才会执行,没有调用就没有执行 SpringBoot自动装配时就没有执行 selectImports() 方法)"] C["Group实现类中的 selectImports() 方法 这个方法的返回值才是真正要被 导入的类"] A-->B-->C

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. 上述流程中 (1) 位置就是处理 ImportSelector 的地方, 同时也是缓存 DeferredImportSelector 实现类的地方
  2. 上述流程中 (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();
}

至此就解析完了 SpringDeferredImportSelectory 的整体调用流程

相关推荐
黑胡子大叔的小屋34 分钟前
基于springboot的海洋知识服务平台的设计与实现
java·spring boot·毕业设计
ThisIsClark36 分钟前
【后端面试总结】深入解析进程和线程的区别
java·jvm·面试
雷神乐乐1 小时前
Spring学习(一)——Sping-XML
java·学习·spring
小林coding2 小时前
阿里云 Java 后端一面,什么难度?
java·后端·mysql·spring·阿里云
V+zmm101342 小时前
基于小程序宿舍报修系统的设计与实现ssm+论文源码调试讲解
java·小程序·毕业设计·mvc·ssm
文大。2 小时前
2024年广西职工职业技能大赛-Spring
java·spring·网络安全
一只小小翠3 小时前
EasyExcel 模板+公式填充
java·easyexcel
m0_748256343 小时前
QWebChannel实现与JS的交互
java·javascript·交互
Jelena技术达人4 小时前
Java爬虫获取1688关键字 item_search接口返回值详细解析
java·开发语言·爬虫
u0107735144 小时前
【字符串】-Lc5-最长回文子串(中心扩展法)
java·算法