springboot自动配置详解
最近在研究springboot,小有心得,在此记录一下,同大家分享,同时博文如有不妥之处,请各位大佬指出,小弟不胜感激!
springboot的启动类都需要使用@SpringBootApplication注解,这个注解用到了下面这些注解实现自动配置的三个关键注解:@SpringBootConfiguration,@EnableAutoConfiguration,@ComponentScan。其中@EnableAutoConfiguration最为关键,下面就让我们逐个解读这三个注解的作用。
一.@SpringBootConfiguration
@SpringBootConfiguration继承了@Configuration注解,所以主要作用是表示当前类为注解类该类的主要功能是代替spring的XML文件配置形式,之前spring的xml配置文件的配置bean,注入bean如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"
default-lazy-init="true">
<bean id="testService" class="..TestServiceImpl">
...
</bean>
</beans>
而用@Configuration的配置形式如下:
@Configuration
public class testConfiguration{
@Bean
public TestService testService(){
return new TestServiceImpl();
}
}
二.@ComponentScan
组件扫描,主要作用是自动发现和装配Bean,将bean装配到IOC容器中,也可以用value属性定义扫描范围
@ComponentScan(value = {"com.test"})
如果不定义@ComponentScan,则默认Spring框架实现会从声明@ComponentScan所在类的package进行扫描,这也是我们为什么会把启动类放到root package的原因。
三.@EnableAutoConfiguration(重点)
看名字也能知道这个注解才是自动配置的关键。我们看下这个注解下的内容:其中最为关键的两个注解:
@AutoConfigurationPackage 和 @Import({AutoConfigurationImportSelector.class})
1.@AutoConfigurationPackage
这个注解其实也继承了@Import注解,主要作用就是将主程序类所在包的路径作为扫描器的base-package,然后添加到容器中,之后可以被用作spring.factories配置文件的key,我们点进去看一下:其中Registrar.Class内容如下:
registerBeanDefinitions方法的主要作用是扫描主配置类同级目录以及子包,并将相应的组件导入到springboot创建管理的容器中。
2.@Import({AutoConfigurationImportSelector.class})
这个注解的作用是将AutoConfigurationImportSelector这个类的实例注入到IOC容器中,我们着重看下这个类主要做了什么:
看AutoConfigurationImportSelector源码,里面有个getAutoConfigurationEntry方法, 这个方法的主要作用就是去加载项目外部的配置(内部配置主要通过@ComponentScan扫描加载),我们着重看一下:
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata
autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
//获取自动配置的类名集合
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
//将自动配置的类名去重
configurations = this.removeDuplicates(configurations);
//获取需要去除的列名
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
//将自动配置类名集合里的无用类去除
configurations.removeAll(exclusions);
//过滤,autoConfigurationMetadata为过滤条件
configurations = this.filter(configurations, autoConfigurationMetadata);
//自动装配导入事件
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
}
}
上面的代码注释详细介绍了每一步的操作,我们着重看第一个方法,看看他是如何获取需要自动配置的类名集合的,我们点进去:主要方法就一个SpringFactoriesLoader.loadFactoryNames(),SpringFactoriesLoader类属于Spring框架私有的一种扩展方案,其主要功能就是从指定的配置文件
META-INF/spring.factories加载配置,来加载到需要自动配置的类的全限定名列表我,们再点进去看下:
我们再看下loadSpringFactories方法的源码:
大体流程如下:
1.在缓存中查找配置结合结果,如果找到直接返回
2.如果缓存没有找到,则通过classLoader.getResources(“META-INF/spring.factories”)直接获取文件,这里注意,路径是写死的,“META-INF/spring.factories”。其中spring.factories的文件格式如下:
3.解析Properties,并且用key,value形式存到LinkedMultiValueMap集合中
4.将结果存到缓存里
5.返回集合,结构为key:factoriesType(例:org.springframework.boot.autoconfigure.EnableAutoConfiguration),value为配置的全类名(例:org.springframework.boot.autoconfigure.aop.AopAutoConfiguration)
之后就是对自动配置的类名集合进行简单的处理,然后将这些配置都加载到当前SpringBoot创建并使用的IoC容器中。
springboot自动装配的大体流程就如下图所示:
***还有一点:springboot启动加载自动配置的方法,是直接调用的AutoConfigurationImportSelector类下的getAutoConfigurationEntry方法,并不是通过selectImports方法调用的getAutoConfigurationEntry,需要debug走源码的同学注意下,断点不要打在selectImports方法里。