SpringBoot自动配置原理

我们知道SpringBoot是Spring的一种便携式使用方式,可以达到开箱即用的效果,避免了原生Spring中那些繁杂的配置工作。那么SpringBoot是如何实现自动配置功能的呢?

这还要从启动类注解@SpringBootApplication说起。

我们知道@SpringBootApplication是一个组合注解,主要由三个注解组成:

  • SpringBootConfiguration注解 表示这是SpringBoot的配置类
  • @ComponentScan 开启组件扫描
  • @EnableAutoConfiguration这个注解的作用就是让SpringBoot开启自动配置

@EnableAutoConfiguration

@EnableAutoConfiguration定义在 org.springframework.boot:spring-boot-autoconfigure:xxxx 包中,其源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

Class<?>[] exclude() default {};

String[] excludeName() default {};
}

如上我们可以看到EnableAutoConfiguration注解上有两个注解

  1. @AutoConfigurationPackage 注解,从字面意思上来看就是自动配置包。点进去可以看到就是⼀个 @Import 注解: @Import(AutoConfigurationPackages.Registrar.class) ,导⼊了⼀个Registrar 的组件,这个注解的作用就是将主配置类(@SpringBootConfiguration标注的类)所在的包及其下面所有子包里面所有的组件扫描到IOC容器中。所以说,默认情况下主配置类所在包及其子包以外的组件,Spring IOC容器是扫描不到的。
  2. @Import(AutoConfigurationImportSelector.class)通过@Import导入AutoConfigurationImportSelector类,而这个类的selectImports方法会通过SpringFactoriesLoader得到大量的配置类。而每个配置类则根据条件化配置类做出决策,以实现自动配置的功能。下面就让我们来看看selectImports方法。

AutoConfigurationImportSelector的selectImports方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public String[] selectImports(AnnotationMetadata annotationMetadata) {
... ...
//这一行是核心
AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
... ...
}
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
... ...
//这一行获取spring.factories中所有对象
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
... ...
}
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
//调用loadFactoryNames
List<String> configurations = SpringFactoriesLoader.loadFactoryNames( this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
... ...
}
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
String factoryTypeName = factoryType.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
... ...
try {
//FACTORIES_RESOURCE_LOCATION的值是META-INF/spring.factories
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
cache.put(classLoader, result);
return result;
}
... ...
}

简单梳理一下思路:

  • FACTORIES_RESOURCE_LOCATION的值是META-INF/spring.factories
  • Spring启动的时候会扫描所有jar路径下的META-INF/spring.factories,将其文件包装成Properties对象
  • 从Properties对象获取到key值为EnableAutoConfiguration的数据,然后添加到容器里边。

@ConditionOnXXX注解

打开org.springframework.boot:spring-boot-autoconfigure:xxxx jar包,META-INF目录下的spring.factories文件,我们发现其中定义了多大130个配置类。 难道SpringBoot在启动的时候都要将这些类里的@Bean实例化到Spring容器中吗?

当然不用!

那么 SpringBoot是如何确定哪些配置类该使用,哪些该忽略呢?答案就是使用各种 ConditionalOnXXX 注解,也就是说当符合某些条件时才自动装配。下图总结了SpringBoot中常用条件注解:

总结

  1. SpringBoot启动时会扫描项目所依赖的JAR包,寻找包含spring.factories文件的JAR包。
  2. 根据spring.factories配置加载EnableAutoConfiguration,其中给容器中自动配置添加组件的时候,会从propeties类中获取配置文件中指定这些属性的值。xxxAutoConfiguration:⾃动配置类给容器中添加组件。xxxProperties:封装配置⽂件中相关属性。
  3. 根据@Conditional注解的条件,进行自动配置并将Bean注入Spring容器。
打赏
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2021-2022 Yin Peng
  • 引擎: Hexo   |  主题:修改自 Ayer
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信