开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第14天,点击查看活动详情
在SpringBoot中约定大于配置。我们通过引入不同的官方或者第三方的场景启动器,就能应用不同场景的开发。只要想修改就直接通过配置文件定义就行,这样的自动配置原理又是什么呢?我们来一起看看吧~
引导加载自动配置类
我们从主启动程序入手分析。
@SpringBootApplication
public class SpringbootApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootApplication.class, args);
}
}
这里标注了一个@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//标志当前是一个配置类
public @interface SpringBootConfiguration {
@AliasFor(annotation = Configuration.class)
boolean proxyBeanMethods() default true;
}
这个类除了元注解就只有@Configuration,所以它的作用也就是标志当前是一个配置类。也就是说我们的主启动程序,也是一个配置类,不过是SpringBoot的一个核心配置类。
@ComponentScan
//SpringBoot配置类
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {}
接着我们在来看一下@ComponentScan:主要就是定义扫描的路径从中找出标识了需要装配的类自动装配到spring的bean容器中。
不过这里指定了那些类型和类不扫描,也就是过滤掉。
//SpringBoot配置类
@SpringBootConfiguration
@EnableAutoConfiguration
//组件扫描自动装配到IOC容器
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {}
@EnableAutoConfiguration
接下来只剩下@EnableAutoConfiguration注解了。这个就是自动配置的核心注解。我们查看源码
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
发现有两个注解@AutoConfigurationPackage 和@Import。
@AutoConfigurationPackage
查看@AutoConfigurationPackage源码。
//导入AutoConfigurationPackages.Registrar对象
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
}
发现该类通过@Import导入了AutoConfigurationPackages.Registrar组件。我们继续点进去查看源码
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
}
@Override
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new PackageImports(metadata));
}
}
发现该类只有两个方法registerBeanDefinitions,determineImports.我们在第一个方法中打一个断点,debug查看。
AnnotationMetadata的信息,来自标注@SpringBootApplication(合成注解)的主启动类上。
接着通过AnnotationMetadata或者注解标注类所在的包信息转换成一个数组。然后register注册到IOC容器中。
也就是说@AutoConfigurationPackage注解通过利用Registrar导入注册主程序所在包下所有添加@Component,@Controller....的类到IOC容器中。
这也就就解释了为啥我们在主程序包下或者子包编写配置类,Cotroller...能被自动扫描注册到容器中。
@Import
//导入注册主程序包下的组件
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
我们在来看看@Import(AutoConfigurationImportSelector.class),@Import注解的作用很简单,就是导入注册一个组件到容器中。所以关键是AutoConfigurationImportSelector.class
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
可以看出这里的核心是getAutoConfigurationEntry(annotationMetadata);方法
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);获取到的configurations的size是130。
这130个组件是要导入到容器中的,它是哪里获取的呢??我们继续查看getCandidateConfigurations
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());
我们继续查看loadFactoryNames方法
我们继续查看loadSpringFactories方法
打个断点,debug执行。
我们可以得知它是加载FACTORIES_RESOURCE_LOCATION这个属性, 默认扫描我们当前系统里面所有META-INF/spring.factories位置文件加载组件的。
而自动配置最核心的也就是spring-boot-autoconfigure-2.4.1.jar。我们点开该jar包发现META-INF/spring.factories文件查看。
151-22+1=130正好是我们先前看到的加载的组件个数130个。文件里面写死了spring-boot一启动就要给容器中加载的所有配置类。
按需开启自动配置项
虽然我们130个场景的所有自动配置启动的时候默认全部加载。xxxxAutoConfiguration都是都按照条件装配规则@Conditional,最终会按需配置。
例如我们查看文件中的RabbitAutoConfiguration类
@ConditionalOnClass({ RabbitTemplate.class, Channel.class })不存在RabbitTemplate,Channel类所以以下配置都不生效。
用户自定义优先
SpringBoot默认会在底层配好所有的组件。但是如果用户自己配置了以用户的优先。例如WebMvcAutoConfiguration
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)当容器中没有WebMvcConfigurationSupport类型的组件时配置才生效。
总结
-
SpringBoot会扫描所有
META-INF/spring.factories位置文件加载组件。而自动配置最核心的也就是spring-boot-autoconfigure-2.4.1.jar。所以加载的是spring-boot-autoconfigure-2.4.1.jar下META-INF/spring.factories文件中写好的自动配置类(xxxxxAutoConfiguration)。 -
每个自动配置类按照条件进行生效,默认都会绑定配置文件指定的值。xxxxProperties里面拿。xxxProperties和配置文件进行了绑定
-
生效的配置类就会给容器中装配很多组件
-
只要容器中有这些组件,相当于这些功能就有了
定制化配置
-
用户直接自己@Bean替换底层的组件
-
用户去看这个组件是获取的配置文件什么值就去修改。(配置文件配置)
spring-boot-autoconfigure.jar ---> META-INF/spring.factories --->xxxxxAutoConfiguration ---> 组件 --->xxxxProperties里面拿值 ----> application.properties