SpringBoot的自动配置原理
前言
众所周知,使用Spring框架进行项目开发时,起初要编写各种xml文件,用来定义bean信息,比较繁琐;后面可以通过注解的方式(javaBean方式)轻松了许多,但是如果与其他第三方组件进行整合时,还是需要进行一些配置,也是比较麻烦。发布代码时,还要将项目打包成war包,放到Tomcat等Servlet容器中,启动Servlet进行web访问。而SpringBoot的出现可以让这些繁琐的工作简化,通过引入具体组件的starter,在配置文件中进行简单的配置,就可以轻松使用这些组件;这些都得益于SpringBoot的自动配置,下面具体分析它时如何工作的!!!
一、SpringBoot启动入口
在main方法所在的主类上会标注@SpringBootApplication
这么一个注解,它等同于@SpringBootConfiguration
、@EnableAutoConfiguration
和@ComponentScan
这3个注解的组合
@SpringBootConfiguration
:其实就是@Configuration
注解,表示当前类是一个配置类@EnableAutoConfiguration
:开启自动配置,标注此注解Spring会扫描获取各个xxxAutoConfiguration配置类,完成对spring-boot-starter-xxx相关组件注入@ComponentScan
:扫描指定的包及其子包下的标注@Component
的类注入spring容器中,开发中常用的@Controller
、@Service
和@Repository
其实都是@Component
;如果没写具体包名,则扫描标注了@ComponentScan
的类所在包及其子包
二、@EnableAutoConfiguration
1. @EnableAutoConfiguration是啥?
@EnableAutoConfiguration
其实就是包含了@Import
,而@Import
这个注解的功能是将一个类导入到Spring容器中,使这个类成一个Spring的bean,而导入的这个类,可以是:
- 普通简单类: 直接导入Spring容器,成为一个bean
- 实现
DeferredImportSelector
接口: 延迟的ImportSelector,它是ImportSelector
接口的子接口,作用和ImportSelector
接口一样- 实现
ImportSelector
接口:它的作用是将一组类注入到Spring容器中,此接口有一个方法selectImports,返回类的全限定类名数组,将这些类注入到Spring容器中- 实现
ImportBeanDefinitionRegistrar
接口:它的主要功能是注册BeanDefinition,Spring再根据BeanDefinition实例化成bean,
这里导入的是AutoConfigurationImportSelector
类,它实现了DeferredImportSelector
接口
2. @EnableAutoConfiguration 执行时机
@EnableAutoConfiguration
的执行时机,即@Import
的执行时机,是在SpringBoot启动流程中,刷新上下文时调用的(了解SpringBoot的启动流程)
内部调用
AbstractApplicatoinContext#refresh
方法,再调用invokeBeanFactoryPostProcessors
方法,这属于Spring的范畴了
调用内部的
invokeBeanDefinitionRegistryPostProcessors
方法,主要是调用BeanDefinitionRegistryPostProcessor
接口的postProcessBeanDefinitionRegistry
方法,这里会获取到处理@Import
注解的类ConfigurationClassPostProcessor
,它正好实现了接口BeanDefinitionRegistryPostProcessor
,进而调用ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry
方法
postProcessBeanDefinitionRegistry
内部再调用processConfigBeanDefinitions
方法,其方法内部先是获取所有的BeanDefinition,循环找到所有配置类(标注了@Configuration
的类),刚好@SpringBootApplicaiton
内部含有@Configuration
,所以主类就是配置类
接下来就会创建
ConfigurationClassParser
,这个对象是用来解析配置类的,循环调用这个对象的parse
方法进行解析配置类,因为配置类可能有多个
进入parse方法,继续调用parse重载方法
进一步调用了
processConfigurationClass
方法,遍历调用doProcessConfigurationClass
方法,为啥遍历?因为当前配置类可能有父类,对父类再处理,这里主类没有父类所以只执行一次
注意:解析过的配置类都会放到configurationClasss
这个集合,后面会取出,对它们进行加载到Spring容器中
doProcessConfigurationClass
就是真正做处理的方法,里面就包含对@Import
的解析,解析方法为processImports
,注意传参里面的getImports(sourceClass)方法,它获取导入的类,这里即获取到了AutoConfigurationImportSelector
进入
processImports
方法,对AutoConfigurationImportSelector
进行处理,它是DeferredImportSelector
接口实现类,所以调用了deferredImportSelectorHandler#handle
方法,这里理解成将它保存起来后续处理,为啥后续处理?从这个接口的名字可以理解,Deferred(延迟的),因为它会在ImportSelector接口处理完之后再处理,而我们一般都是实现ImportSelector接口,完成对组件的配置,而SpringBoot的自动配置实现了DeferredImportSelector接口,也就是说SpringBoot对组件的配置是在我们配置之后的,这就是为啥,我们定义了组件之后,SpringBoot对该组件的配置不生效的原因
而如果只是实现了
ImportSelector
接口,会立即执行selectImports
方法获取到类名数组,再次递归调用processImports
,为啥要递归调用?因为import进来的类,有可能也刚好实现了上面3个接口,所以需要递归处理
3. AutoConfigurationImportSelector执行流程
上面讲到
AutoConfigurationImportSelector
被DeferredImportSelectorHandler
(ConfigurationClassParser的内部类)调用handle
方法保存起来,里面对其进行封装成了一个DeferredImportSelectorHolder
对象,并添加到了一个集合中
而它的执行时机是在
ConfiurationClassParser#parse
方法的最后,调用DeferredImportSelectorHandler#process
方法
在process方法中,主要对其进行分组处理,创建
DeferredImportSelectorGroupingHandler
对象,(理解成分组对象),返回调用register方法将其注册到分组中,然后在分组中处理调用了DeferredImportSelectorGroupingHandler#processGroupImports
方法
在
processGroupImports
调用分组的DeferredImportSelectorGrouping#getImports
方法,这里返回的是DeferredImportSelector.Group.Entry
(封装了配置类),这里当前处理的是AutoConfigurationImportSelector
,所以获取的AutoConfigurationImportSelector.AutoConfigurationGroup.Entry
,调用this.group.process方法就是调用AutoConfigurationImportSelector.AutoConfigurationGroup#process
方法
进入到
AutoConfigurationImportSelector.AutoConfigurationGroup#process
方法,又调用getAutoConfigurationEntry
方法,返回AutoConfigurationImportSelector.AutoConfigurationEntry
对象
getAutoConfigurationEntry
方法首先调用getCandidateConfigurations
方法,从项目或者所有依赖的jar的资源路径下META-INFO/spring.factories
文件中获取所有的自动配置类(即xxxAutoConfiguration),这里找到了130个
- spring-boot-devtools.jar 3个
- spring-boot-autoconfigure.jar 127个
上面相当于是把常用的配置类都获取到了,接下来去重,去除排除的配置类(根据
@SpringBootApplication
的exclude和excludeName属性值),再根据AutoConfigurationImportFilter
过滤器进行过滤,最后剩下27个满足条件的配置类,封装成AutoConfigurationImportSelector.AutoConfigurationEntry
对象
回到
AutoConfigurationImportSelector.AutoConfigurationGroup#process
方法,将AutoConfigurationImportSelector.AutoConfigurationEntry
添加到autoConfigurationEntries
集合属性中
回到DeferredImportSelectorGrouping#getImports方法,再调用selectImports方法,根据
autoConfigurationEntries
集合,遍历创建Entry对象,返回List集合,====
最后回到调用getImports方法的地方,循环遍历Entry, 调用
processImports
方法,对上面的27个配置类,进行解析。这里可以看出processImports方法才是核心
最后paser执行完,从
ConfigurationClassParser
的configurationClasses
集合中取出所有解析过的配置类,通过BeanDefinition读取器加载到Spring容器中
至此算是完成了Springboot的自动配置,将相关的组件加载到Spring容器中!!!
总结
以上只是对SpringBoot自动配置的大致说明,如果有错误之处,欢迎评论指正!!!