ImportSelector 与 DeferredImportSelector 的区别(spring4)
欢迎访问我的 GitHub
这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos
在使用 @Import 注解来注册 bean 的时候,Import 注解的值可以是 ImportSelector 或者 DeferredImportSelector 的实现类,spring 容器会实例化这个实现类,并执行其 selectImports 方法,那么问题来了:ImportSelector 和 DeferredImportSelector 的区别在哪里,我们自定义 Imort 逻辑的时候该选择哪个呢? 本文通过分析相关的 spring 源码来查找答案;
全文概览
本文由以下几部分组成:
看官方文档;
分析 spring 源码中对这两个接口的处理;
实战验证;
看官方文档
先看官方文档看起,我选择了 4.3.9 版本在线文档(这是个 Release 版),地址:https://docs.spring.io/spring/docs/4.3.19.RELEASE/javadoc-api/
原文:A variation of ImportSelector that runs after all @Configuration beans have been processed. This type of selector can be particularly useful when the selected imports are @Conditional.Implementations can also extend the Ordered interface or use the Order annotation to indicate a precedence against other DeferredImportSelectors.
我的理解:
DeferredImportSelector 是 ImportSelector 的一个扩展;
ImportSelector 实例的 selectImports 方法的执行时机,是在 @Configguration 注解中的其他逻辑被处理之前,所谓的其他逻辑,包括对 @ImportResource、@Bean 这些注解的处理(注意,这里只是对 @Bean 修饰的方法的处理,并不是立即调用 @Bean 修饰的方法,这个区别很重要!);
DeferredImportSelector 实例的 selectImports 方法的执行时机,是在 @Configguration 注解中的其他逻辑被处理完毕之后,所谓的其他逻辑,包括对 @ImportResource、@Bean 这些注解的处理;
DeferredImportSelector 的实现类可以用 Order 注解,或者实现 Ordered 接口来对 selectImports 的执行顺序排序;
分析 spring 源码中对这两个接口的处理
接下来看看源码:
在 spring-framework-4.1.8.RELEASE 工程中找到类 ConfigurationClassParser.java,这里面有处理配置类的主要逻辑;
找到方法 parse(Set<BeanDefinitionHolder> configCandidates):
由以上代码可以大致看出 DeferredImportSelector 的实现类被最后放在 processDeferredImportSelectors 方法中处理,那么前面的 parse(AnnotationMetadata metadata, String beanName)做了些什么呢?继续看;
展开方法 parse(AnnotationMetadata metadata, String beanName)里面,是执行 processConfigurationClass 方法;
再展开 processConfigurationClass 方法,看到核心逻辑是调用 doProcessConfigurationClass 方法,展开看看:
根据上述代码分析,可以梳理出下图中的逻辑:
现在需要再看看 processImports 和 processDeferredImportSelectors 这两个方法的具体代码;
先看 processImports 方法:
以上代码有两个关键点:
第一、当前被处理的类,如果实现了 DeferredImportSelector 接口,就被加入到集合 deferredImportSelectors 中;
第二、当前被处理的类,如果没有实现 DeferredImportSelector 接口,但是实现了 ImportSelector 接口,就被执行 selectImports 方法;
接下来看看 processDeferredImportSelectors 方法的源码,提前推测应该是处理集合 deferredImportSelectors 中的所有类,这些类都实现了 DeferredImportSelector 接口:
至此,源码分析完毕了,从代码可以很清晰的看出 ImportSelector 与 DeferredImportSelector 的区别,就是 selectImports 方法执行时机有差别,这个差别期间,spring 容器对此 Configguration 类做了些其他的逻辑:包括对 @ImportResource、@Bean 这些注解的处理(注意,这里只是对 @Bean 修饰的方法的处理,并不是立即调用 @Bean 修饰的方法,这个区别很重要!);
实战验证
接下来到了实战验证的环节了,本次实战的内容是创建一个 springboot 工程,在里面自定义三个 ImportSelector 接口的实现类,如果您不想敲代码,也可以去 github 下载源码,地址和链接信息如下表所示:
这个 git 项目中有多个文件夹,本章源码在文件夹 customizeimportselector 下,如下图红框所示:
开始编码吧:
我们创建三个 ImportSelector 的实现类来检查其先后顺序,三个 Selector 类简介如下表,有两个是 DeferredImportSelector 的实现类,一个是 ImportSelector 的实现类,每个 Selector 负责向 spring 容器注册一种实例:
基于 maven 创建 springboot 框架的 web 工程,pom.xml 内容如下:
创建三个接口 CustomizeService1、CustomizeService2、CustomizeService3,第一个源码如下,另外两个除了类名,其余部分一样:
创建三个类,分别实现上面的三个接口,也是除了类名其余部分一样:
创建 CustomizeImportSelector1:
创建 CustomizeImportSelector2:
创建 CustomizeImportSelector3,实现的是 ImportSelector 接口:
创建配置类,将 CustomizeImportSelector1、CustomizeImportSelector2、CustomizeImportSelector3 全部用 Import 注解引入:
创建启动类 CustomizeimportselectorApplication.java:
启动应用,可见输入信息如下:
从上述信息可以看出:
首先、三个 selector 实现类的 selectImports 方法执行顺序符合预期:先执行 ImportSelector 实现类的,再执行 DeferredImportSelector 实现类的,并且 DeferredImportSelector 实现类的执行顺序会按照 Order 的设置从小到大执行;
其次、CustomizeServiceImpl1、CustomizeServiceImpl2、CustomizeServiceImpl3 的实例化顺序并未受到影响;
至此,ImportSelector 与 DeferredImportSelector 的区别已经分析和验证完毕,随着对 Configuration 初始化处理逻辑的深入了解,我们可以定制出更灵活强大的配置逻辑,以符合业务需求;
欢迎关注 InfoQ:程序员欣宸
版权声明: 本文为 InfoQ 作者【程序员欣宸】的原创文章。
原文链接:【http://xie.infoq.cn/article/35a661d937e218f973af6546e】。文章转载请联系作者。
评论