说到 SpringBoot 自动装配的原理,可能有的人就要开始 @EnableAutoConfiguration 的长篇大论了,感觉在背源码一样,那么 自动装配
的含义你是真的了解吗?它是如何与 Spring 相结合的,又是如何被 Spring 管理的?接下来我将告诉你 自动装配
原理的真正打开方式。
自动装配的本质其实就是将 starter
快速依赖包中的 Bean 信息,自动的整合到 Spring IOC 容器中被 Spring 所管理,掌握此本质,就可自由实现自定义的 starter
的各种工具包,学习本文以前,需要掌握 Spring 容器中的 Bean 生命周期流程。
其实现的方式就是通过 @SpringApplication
复合注解中的 @EnableAutoConfiguration
注解的 AutoConfigurationImportSelector
类中调用 SpringFactoriesLoader #loadFactoryNames
方法,加载 META-INF/spring.factories
文件实现的
大体了解流程以后,开始分析源码,流程掌握比较简单,但是如何从 SpringBoot 启动后基于 Spring 扩展进行的自动装配的实现才是最核心重点。
查看 @SpringBootApplication
源码,可以看到存在一个 @EnableAutoConfiguration
注解,此注解即为 SpringBoot 内部注解,为 Spring 上下文提供自动配置的
接着看 @EnableAutoConfiguration
注解的实现,内部使用了 @Import
注解(Spring 的核心注解,其用于导入一个或多个 Bean 信息),导入了 AutoConfigurationImportSelector
Bean 信息
此时 AutoConfigurationImportSelector
实例已被注册成 Bean,被 Spring 容器所管理,此实例的接口实现了 DeferredImportSelector
接口, 查看下源码的实现
齿轮开始转动,自动装配的实现也是从此刻开始,一直常说 SpringBoot 启动时会加载 META-INF/spring.factories
文件,并且将文件中的实现类加载到 Spring 容器中,那么到的是如何加载的呢?源码看到这,接下来要如何查找呢?
很简单粗暴的方法,一个查找当前源码的类,一个从源码中全局搜索,去查找到 spring.factories
文件在哪被定义过,看是否被当前类引用过,就找到了当前的接口调用
查看 SpringFactoriesLoader #loadFactoryNames
方法,可以看到其内部加载 META-INF/spring.factories
文件
源码看到这基本就差不多了,在看一眼 META-INF/spring.factories
文件内容
看完上面的流程,你以为就这样结束了么,感觉一切这么理所当然,转动脑筋思考下,SpringBoot 启动的时候,是如何来执行到上述的流程呢?接下来我们继续断点分析
通过对调用 SpringFactoriesLoader #loadFactoryNames
的方法进行断点,发现一切奥秘尽在堆栈之中,有没有看到那个特殊眼熟的 refresh
方法,这个是 Spring 容器刷新的入口,不了解的小伙伴可以去看下文章:Spring 源码分析之 Bean 的全生命周期
开始看上层堆栈方法的源码,首先将 META-INF/spring.factories
文件读取的的内容构建成 AutoConfigurationEntry
对象
将构建的 AutoConfigurationEntry
对象,存储到本地缓存的 this.autoConfigurationEntries
集合中
接着找上层的调用,又打开了新世界的大门,调用到了 ConfigurationClassParser
类,此类主要是对配置类的解析,以下是从掘金社区提供的图片
了解了 ConfigurationClassParser
类的作用,发现其内部调用了 processImports
方法,此方法就是解析的核心方法,递归加载,有兴趣可以自行阅读源码
可以看出此处进行的for循环,包含我们的核心类 AutoConfigurationImportSelector
,那么,其中 this.groupings.values()
数据从哪里来呢
找到上层方法,发现有一个 register
方法,内部果然是构建了 this.groupings.values()
数据
看到此处,发现解析的核心逻辑实现,有兴趣可以自行阅读源码,这里就不再赘述了
继续查找进入到了 ConfigurationClassPostProcessor
类中,见名知意,此类为配置类后置处理的作用,扩展实现了 BeanDefinitionRegistryPostProcessor
接口,被调用了 #postProcessBeanDefinitionRegistry
方法
接着往上层查找,走到了 invokeBeanDefinitionRegistryPostProcessors
方法,此处是调用 BeanDefinition 注册的后置处理的所有扩展接口,在联想到上述的 ConfigurationClassPostProcessor
类实现了其接口,可以理解是在此处将所有的配置类信息以及 starter
中的信息加载到了 Spring 容器中
找到这,终于看到非常眼熟的 refresh
方法,原来这一切都是在 Spring 容器刷新的时候,创建完 BeanFactory 以后,在调用 invokeBeanFactoryPostProcessors
方法,进行后置处理中操作的
通过上面的源码追溯,我们总结一下 自动装配
的扩展流程实现
对于 starter
的中加载到的Bean信息,是通过 Spring 提供的 DeferredImportSelector
接口进行扩展的,通过 BeanFactoryPostProcessor #postProcessBeanFactory
后置处理中调用 BeanDefinitionRegistryPostProcessor
后置处理进行实现的
思考一下
根据上面的源码思路,如何实现动态Bean加载呢? 如何基于Nacos配置中心来实现动态线程池的管理呢?
在 AutoConfigurationImportSelector #getCandidateConfigurations
方法,由调用的 SpringFactoriesLoader #loadFactoryNames
方法变成了 ImportCandidates #load
方法,查找的文件也从 META-INF/spring.factories
改为了 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
其中文件的内容也进行了调整
yaml# 变更前 META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
cn.liushigong.cache.redis.RedisCacheAutoConfig
# 变更后 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
cn.liushigong.cache.redis.RedisCacheAutoConfig
我们所了解的 SpringBoot 不是一个全新的框架,而是对 Spring 的一种扩展,回答 自动装配
这个问题,除了 @SpringApplication 这千篇一律的逻辑以外,我们还需要能结合 Spring 源码进行有条理的分析出来,才能更好的体现自己的深度。
本文作者:柳始恭
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!