SpringBoot:@EnableAutoConfiguration自动配置原理

  众所周知,springboot的出现为开发带来了很大的方便,主要就是提供了很多自动配置,免去了开发人员花在配置上的时间,使我们开发者能更专注于业务或者架构设计上。

  而springboot的自动配置是通过一系列注解来完成的,例如@EnableAutoConfiguration@Conditional@ConditionalOn*@EnableConfigurationProperties等等。

  这一系列注解我觉得大致可分为两类,一类是条件注解,一类是与配置相关的注解,下面分别举一些例子进行说明。

  • 条件注解

    • @ConditionalOnBean

      当容器里有指定的bean类或者名称时,满足匹配条件

    • @ConditionalOnMissingBean

      当容器里缺少指定的bean类或者名称时,满足匹配条件

    • @ConditionalOnProperty

      检查指定的属性是否有特定的值,name为指定值的key,havingValue为指定值,matchIfMissing为缺少或为设置值时,是否也进行匹配

    • @ConditionOnClass

      当有指定类在classpath时,满足匹配条件

    • @ConditionOnMissClass

      当指定类在classpath不存在时,满足匹配条件

  • 配置相关注解

    • @EnableAutoConfiguration

      开启自动配置

    • @AutoConfigureAfter

      在指定的自动配置类之后进行配置

    • @AutoConfigureBefore

      在指定的自动配置类之前进行配置

    • @EnableConfigurationProperties

      开启对@ConfigurationProperties修饰的bean的支持,将@ConfigurationProperties修饰的类注册成bean


  而最核心的我想就是@EnableAutoConfiguration注解了,毕竟只有有了它才能开启自动配置,是所有自动配置的起点,那么就从它开始探索下自动配置的原理。

以下源码版本为springboot 2.0.5.RELEASE

先看下这个注解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@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 {};

}

  可以看到这里导入了AutoConfigurationImportSelector,先看下该类核心的selectImports()方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return StringUtils.toStringArray(configurations);
}

  可以看出,先获取到所有配置,然后删除重复的配置,然后获取需要排除的配置类,再检查需要排除的配置类,从所有配置中移除需要排除的配置,再使用filter过滤掉不满足自动导入条件的配置(例如使用了@Contional及衍生的条件注解的配置类),最后将剩下的配置类返回。

  下面我们具体地看下selectImports()中的getCandidateConfigurations():

1
2
3
4
5
6
7
8
9
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
getSpringFactoriesLoaderFactoryClass(), 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;
}

  该方法主要通过SpringFactoriesLoader.loadFactorynames()使用当前的类加载器返回指定EnableAutoConfigurationMETA-INF/spring.factories对应的所有配置类名。

  那么META-INF/spring.factories是什么呢,在spring-boot-autoconfigure.jar包下有这样一个文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
...省略...

  可以看到,这里几乎写了所有springboot提供的各种配置类,所以@EnableAutoConfiguration导入了AutoConfigurationImportSelector,就几乎相当于将所有的配置类都引入进来加载了,所以springboot的自动配置换句话说,其实是帮我们定义了很多通用配置的配置类,再统一按条件引入进来加载。

  至于selectImports()中其他作用的实现都比较简单,值得一看的是通过filter(configurations, autoConfigurationMetadata)过滤不满足条件的配置类,源码:

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
private List<String> filter(List<String> configurations,
AutoConfigurationMetadata autoConfigurationMetadata) {
long startTime = System.nanoTime();
String[] candidates = StringUtils.toStringArray(configurations);
boolean[] skip = new boolean[candidates.length];
boolean skipped = false;
for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {
invokeAwareMethods(filter);
boolean[] match = filter.match(candidates, autoConfigurationMetadata);
for (int i = 0; i < match.length; i++) {
if (!match[i]) {
skip[i] = true;
skipped = true;
}
}
}
if (!skipped) {
return configurations;
}
List<String> result = new ArrayList<>(candidates.length);
for (int i = 0; i < candidates.length; i++) {
if (!skip[i]) {
result.add(candidates[i]);
}
}
if (logger.isTraceEnabled()) {
int numberFiltered = configurations.size() - result.size();
logger.trace("Filtered " + numberFiltered + " auto configuration class in "
+ TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime)
+ " ms");
}
return new ArrayList<>(result);
}

  这里主要是从META-INF/spring.factories中获取到AutoConfigurationImportFilter

1
2
3
# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnClassCondition

  即OnClassCondition,调用match()方法检查是否存在有@ConditionalOnClass@ContionalOnMissClass注解的配置类。

  至此,关于@EnableAutoConfiguration的自动配置原理我们心里也有了答案。

秋月 wechat
欢迎您扫一扫上面的微信公众号,订阅我的博客!
随缘打赏,您的支持将鼓励我继续创作!