SpringBoot自动配置原理
什么是自动配置?
在学习SSM开发项目时,我们需要自己引入spring、springMVC以及mybatis的依赖,并且需要使用配置文件或者java config来进行配置,比如配置视图解析器等等组件。但springboot 为我们提供了一种便利的开发方式:自动配置,我们只需要引入特定的starter即可,其它需要的组件springboot会自动帮我们加入到容器中,这就是自动配置。
SpringBoot是怎么实现自动配置的?
本文基于SpringBoot2.1.4书写。
查看主启动类,我们发现类上有一个@SpringBootApplication
注解,这个注解就是SpringBoot实现自动配置的关键。
@SpringBootApplication
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
}
继续深入下去,我们进入@SpringBootApplication
注解的源码,发现该注解上方有如下注解:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM,
classes = AutoConfigurationExcludeFilter.class) })
其中:
@SpringBootConfiguration
是SpringBoot项目的配置注解,也是一个组合注解,在SpringBoot项目中推荐使用@SpringBootConfiguration
替代@Configuration
;@ComponentScan
的作用就是根据定义的扫描路径,把符合扫描规则的类装配到spring容器中;@EnableAutoConfiguration
就是告诉SpringBoot开启自动配置功能,自动配置的重点就在这个注解中。
我们认清了自动配置的重点——@EnableAutoConfiguration
,接下来就去研究这个注解。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
同样,我们逐个逐个开始进行分析。
@AutoConfigurationPackage
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}
我们查看了@AutoConfigurationPackage
注解,发现他导入了AutoConfigurationPackages
下的Registrar
类
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
register(registry, new PackageImport(metadata).getPackageName());
}
@Override
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new PackageImport(metadata));
}
}
我们发现这个Registrar
类中有一个registerBeanDefinitions
方法,该方法的主要作用就是将主程序类所在包及其所有子包下的组件扫描到Spring容器中。
@Import(AutoConfigurationImportSelector.class)
**该注解是重点之中的重点。**该注解给当前配置类导入自动配置类AutoConfigurationImportSelector
。
该类中有个selectImports
方法,该方法的起到的作用是,根据配置文件(spring.factories
),将需要注入到容器的bean
注入到容器。(该方法在SpringBoot启动时不会被调用!!!)
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(
autoConfigurationMetadata, annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
该方法中有两个主要用到的方法:
- 一个是
AutoConfigurationMetadataLoader.loadMetadata
,该方法会默认加载类路径下META-INF/spring-autoconfigure-metadata.properties
内的配置; - 另外一个是
getAutoConfigurationEntry
。
getAutoConfigurationEntry
方法是SpringBoot启动时调用的方法,而不是selectImports
方法。
该方法主要功能是加载类路径下META-INF/spring.factories
,如果@EnableAutoConfiguration
注解传入了exclude
参数,就把exclude
对应的类从META-INF/spring.factories
中移除,并把不满足META-INF/spring-autoconfigure-metadata.properties
中所写条件的那些配置类过滤掉,返回经过筛选后的所有配置类。
protected AutoConfigurationEntry getAutoConfigurationEntry(
AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
// 检查自动装配开关
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
// 获取EnableAutoConfiguration中的参数,exclude()/excludeName()
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 获取需要自动装配的所有配置类,读取META-INF/spring.factories
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
// 去重,List转Set再转List
configurations = removeDuplicates(configurations);
// 从EnableAutoConfiguration的exclude/excludeName属性中获取排除项
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
// 检查需要排除的类是否在configurations中,不在报错
checkExcludedClasses(configurations, exclusions);
// 从configurations去除exclusions
configurations.removeAll(exclusions);
// 对configurations进行过滤,剔除掉不满足 spring-autoconfigure-metadata.properties 所写条件的配置类
configurations = filter(configurations, autoConfigurationMetadata);
// 监听器 import 事件回调
fireAutoConfigurationImportEvents(configurations, exclusions);
// 返回(configurations, exclusions)组
return new AutoConfigurationEntry(configurations, exclusions);
}
是不是有点晕了?其实就是把不合规范和不被允许的剔除,其他保留下来返回出去而已。
spring-factories
不知道在哪找spring-factories
?没见过具体是什么内容?
如果是使用Maven做包管理工具的话,在External Libraries
下可以找到对应版本的spring-boot-autoconfigure
包,包里面有我们的spring.factories
和spring-autoconfigure-metadata.properties
。
spring.factories
这个文件存储了spring-boot所有默认支持的待自动装配候选类。
spring-autoconfigure-metadata.properties
就是自动装配元数据过滤相关的配置文件。
小结
@Import(AutoConfigurationPackages.Registrar.class)
,实现是将主配置类(@SpringBootApplication
)标注的所有包及子包里面的所有组件扫描到Spring容器当中。@Import(AutoConfigurationImportSelector.class)
,实现的是SpringBoot 在启动的时候从类路径下的META-INF/spring.factores
中获取@EnableAutoConfiguration
指定的值,将这些作为自动配置类导入到容器当中,自动配置类就生效,帮我们进行自动配置的工作。(getAutoConfigurationEntry
方法是SpringBoot启动时调用的方法,而不是selectImports
方法。)