我们从SpringBoot的主程序类来看
- 首先主程序类上有@SpringBootApplication注解,点进去一看,知道它又等价于以下以下三个注解
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication{}
- 接下来就开始从这三个注解一个个出发,首先第一个是@SpringBOotConfiguration
@SpringBootConfiguration注解点进去可看见 @Configuration,所以代表当前是一个配置类 - 然后看@ComponentScan,这个注解很明显就是包扫描,扫描注解并实例化且放入容器中
- 最后就把重点放到@EnableAutoConfiguration上了,一起来吧!
它主要为:
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {}
(1)@AutoConfigurationPackage
自动配置包?指定了默认的包规则
@Import(AutoConfigurationPackages.Registrar.class) //给容器中导入一个组件
public @interface AutoConfigurationPackage {}
//利用Registrar给容器中导入一系列组件
//将指定的一个包下的所有组件导入进来?MainApplication 所在包下。
以上Registrar类
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
Registrar() {
}
//该方法时利用利用Registrar批量给容器中导入一系列组件
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
AutoConfigurationPackages.register(registry, (String[])(new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new String[0]));Enable
}
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new AutoConfigurationPackages.PackageImports(metadata));
}
}
在registerBeanDefinitions这个方法中,可以得到:
- 参数metadata是一注解源信息,其实是在主程序类上面的,因为我们是从@EnableAutoConfiguration出发的,并且,该注解在主程序类上,信息如下:
- 接下来看该方法内容
计算(new AutoConfigurationPackages.PackageImports(metadata).getPackageNames())可以得到包名:
这也就是为什么没有在主程序类配置包扫描路径,默认路径为主程序类所在包之下了
总的来说,Register类的作用就是 将指定的一个包下的所有组件导入进来,也就是@AutoConfigurationPackage注解的作用
**(2)**接下来看@Import({AutoConfigurationImportSelector.class})
点进去看到往下翻找到该方法
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
这个方法里面我们需要主要研究getAutoConfigurationEntry这个方法:
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(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);
configurations = this.getConfigurationClassFilter().filter(configurations);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
}
}
其中
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
该方法的作用: 获取到所有需要导入到容器中的配置类
深入该方法:
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
return configurations;
}
其中
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
从kiadFactoryNames方法出发,深入方法之后得到:
Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader)
作用为:利用工厂加载,得到所有的组件
那么得到所有的组件,从哪来呢?
在 SpringFactoriesLoader类中可以看到此行代码
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
和此方法
Map<String, List> loadSpringFactories(@Nullable ClassLoader classLoader)
该方法在加载资源时候,就是用到上方属性FACTORIES_RESOURCE_LOCATION,所以路径就被指定到"META-INF/spring.factories"。举例如下:
点开spring.factories:
# Application Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.devtools.restart.RestartScopeInitializer
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.devtools.restart.RestartApplicationListener,\
org.springframework.boot.devtools.logger.DevToolsLogFactory.Listener
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.devtools.autoconfigure.DevToolsDataSourceAutoConfiguration,\
org.springframework.boot.devtools.autoconfigure.DevToolsR2dbcAutoConfiguration,\
org.springframework.boot.devtools.autoconfigure.LocalDevToolsAutoConfiguration,\
org.springframework.boot.devtools.autoconfigure.RemoteDevToolsAutoConfiguration
# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.boot.devtools.env.DevToolsHomePropertiesPostProcessor,\
org.springframework.boot.devtools.env.DevToolsPropertyDefaultsPostProcessor
其中的Auto Configure就是该starter(场景)下,需要加载且自动导入的相关类
所以,资源加载过后,然后再实例化并放入容器中,最终实现了自动配置
第二个类讲解的总结,捋顺下思路
1、利用getAutoConfigurationEntry(annotationMetadata);给容器中批量导入一些组件
2、调用List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes)获取到所有需要导入到容器中的配置类
3、利用工厂加载 Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader);得到所有的组件
4、从META-INF/spring.factories位置来加载一个文件。
默认扫描我们当前系统里面所有META-INF/spring.factories位置的文件
spring-boot-autoconfigure-2.3.4.RELEASE.jar包里面也有META-INF/spring.factories
至此,自动配置的基本实现思路已经结束。
但是,以下需要注意
-
扫描spring.factories资源后,所有配置类都会被加载不错,但是起作用的可不一定是全部,一些配置类中的注解是需要一定条件下才会生效,一些对象才会被实例化放入容器等操作才会生效。如图:
条件注解:@ConditionOnProperty,若不满足该条件此类中所有注解不会生效 -
SpringBoot默认会在底层配好所有的组件。但是如果用户自己配置了以用户的优先
举例:
关键注解:@ConditionalOnMissingClass
SpringBoot自动配置原理总结如下:
- SpringBoot先加载所有的自动配置类 xxxxxAutoConfiguration
- 每个自动配置类按照条件进行生效,默认都会绑定配置文件指定的值。xxxxProperties里面拿。xxxProperties和配置文件进行了绑定
- 生效的配置类就会给容器中装配很多组件
- 只要容器中有这些组件,相当于这些功能就有了
- 定制化配置
○ 直接用@Bean替换底层的组件
○ 去看这个组件是获取的配置文件什么值就去修改,只需要修改对应名字的值就行