本文基于SpringBoot 2.5.7版本进行讲解
前文回顾:在上一篇文章,SpringBoot自动配置之AutoConfigurationImportSelector - SpringBoot自动配置(二),讲到AutoConfigurationImportSelector
的importSelector()
方法通过调用getAutoConfigurationEntry()
来获取需要自动配置的Bean信息。
但是,在第一篇文章,SpringBoot自动配置之@SpringBootApplication注解 - SpringBoot自动配置(一),我也讲到其实SpringBoot并没有通过AutoConfigurationImportSelector
类的importSelector()
方法来获取自动配置的Bean信息。
有些读者,可能也在AutoConfigurationImportSelector
类的selectImports()
方法断点了,结果发生debug的时候,程序根本就没有进入到这个方法。
看到这里,我们心中必定是十万个为什么?
AutoConfigurationImportSelector
不是实现了ImportSelector
接口吗?为什么selectImports()
方法没有被调用?- 既然Spring Boot不通过
AutoConfigurationImportSelector
的selectImports()
方法来获取需要自动配置的Bean信息,那么从哪里获取?
别急,这就是本文讲解的内容。让我们带着这个问题,一起看下去吧。
配置调试环境
既然,程序没有调用AutoConfigurationImportSelector
类的selectImports()
方法。那么,我们就自己创建一个简单的ImportSelector
接口的实现类,然后看看SpringBoot会不会调用这个自定义的ImportSelector
实现类。
需要被注入的Bean:Hello
1 | java复制代码public class Hello { |
自定义ImportSelector实现类:HelloImportSelector
1 | java复制代码public class HelloImportSelector implements ImportSelector { |
SpringBoot启动类:SpringTestApplication
1 | java复制代码@Import(HelloImportSelector.class) |
调试过程
到这里,就配置好了调试环境。接下来,我们在HelloImportSelector
类的selectImports()
方法打上断点,来看看程序会不会调用这个方法。
开始调试
看上图,我们知道SpringBoot程序是调用了selectImports()
方法的。
追踪调用栈
既然,我们知道SpringBoot程序是会调用HelloImportSelector
类的selectImports()
方法,那么就好办了。
现在我们就沿着调用栈来一步一步追踪是哪里调用了这个selectImports()
?
这里给出一部分调用栈的截图。我们看到,ConfigurationClassParser
的processImports()
方法调用了HelloImportSelector
的selectImports
方法。
那么接下来,我们就来看看这个processImports()
方法做了什么吧。
ConfigurationClassParser类的processImports()方法
下面这里给出processImports()
方法的定义和部分源码:
(我们不用去看这个方法的定义和部分源码,这里列出只是为了我后面解释的时候,读者能够翻来对照,读者可以直接跳过方法源码直接看文字部分。)
定义:
1 | java复制代码private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass, |
部分源码:
1 | kotlin复制代码for (SourceClass candidate : importCandidates) { |
这里就是文字部分了:
我们再精确点,看看是processImports()
方法的那一部分代码调用了selectImports()
方法。
1 | java复制代码if (selector instanceof DeferredImportSelector) { |
看到这个if-else语句,相信大家都懂了。
AutoConfigurationImportSelector
类的selectImports()
方法没有被调用,而我们自定义的HelloImportSelector
方法的selectImports()
方法被调用的原因就是:AutoConfigurationImportSelector
实现了DeferredImportSelector
接口。
简单讲讲ConfigurationClassParser
的processImports()
方法
现在我们翻回去看看刚刚上面贴出的processImports()
的部分源码。
看代码的if-else语句判断部分就可以了。
processImports()
方法会遍历importCandidates
变量,然后判断里面的元素是否是ImportSelector
或ImportBeanDefinitionRegistrar
的实现类,否则就是@Configuration类来处理。
我们也能猜到importCandidates
变量就是@Import(xxx)
里面的xxx类。
这就是为什么@Import
注解能够接收ImportSelector
和ImportBeanDefinitionRegistrar
实现类、@Configuration
配置类以及普通的Bean对象,支持四种不同类型的类并完成Bean注入的原因。
对@Import
注解不了解的读者,可以看Spring的@Import注解四种使用方式进行了解。
SpringBoot怎么获取需要自动配置的Bean信息?
看过SpringBoot自动配置之AutoConfigurationImportSelector - SpringBoot自动配置(二)的知道,AutoConfigurationImportSelector
实现了DeferredImportSelector
接口,而DeferredImportSelector
接口又实现了ImportSelector
接口,所以AutoConfigurationImportSelector
接口还是实现了selectImports()
方法。
在selectImports()
方法中,我们又讲到selectImports()
方法其实是通过调用getAutoConfigurationEntry()
方法来拿到需要自动配置的bean信息。
既然我们在selectImports()
方法断点发现SpringBoot并没有调用这个方法。那么我们不妨大胆猜想一下,SpringBoot最终会不会是直接通过调用getAutoConfigurationEntry()
方法来获取需要自动配置的bean信息。
那么接下来,我们来求证一下。
可以看到SpringBoot确实是调用了这个方法来获取需要自动配置的bean信息。
本文转载自: 掘金