0%

自动配置原理

自动配置原理

自动配置其实是由于启动类上配置了@EnableAutoConfiguration注解来进行开启的,那有可能有些人就纳闷了,我启动类上也没有配置@EnableAutoConfiguration注解呀,那是因为@SpringBootApplication是组合注解,其内部包含有@EnableAutoConfiguration注解

基于springboot2.x版本,与1.x版本不同,不过思路相似

主启动类

1
2
3
4
5
6
7
@SpringBootApplication //标注主程序类
public class DemoApplication {

public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}

SpringBootApplication注解源码

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
@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) })
public @interface SpringBootApplication {


@AliasFor(annotation = EnableAutoConfiguration.class)
Class<?>[] exclude() default {};


@AliasFor(annotation = EnableAutoConfiguration.class)
String[] excludeName() default {};


@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
String[] scanBasePackages() default {};


@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
Class<?>[] scanBasePackageClasses() default {};


@AliasFor(annotation = Configuration.class)
boolean proxyBeanMethods() default true;

}

即@SpringBootApplication = @Configuration + @EnableAutoConfiguration + @ComponentScan

@Configuration注解

@Configuration注解就相当于一个xml配置文件,可以在该类中配置bean,就像在xml中配置bean一样

@ComponentScan注解

对于xml配置文件中的<context:component-scan/>,用来进行组件扫描的,将bean加载到容器中

@EnableAutoConfiguration注解

@EnableAutoConfiguration注解就是用来进行自动配置的,也是springboot的核心,那么@EnableAutoConfiguration注解是如何做到的自动配置呢?主要利用了一个AutoConfigurationImportSelector选择器给Spring容器中来导入一些组件

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

}

可以看到Import了一个组件AutoConfigurationImportSelector,这个就是自动配置的真相了

AutoConfigurationImportSelector导入组件

主要是来读取META-INF/spring.factories来进行加载各个自动配置类

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
public String[] selectImports(AnnotationMetadata annotationMetadata) {
// 可以使用spring.boot.enableautoconfiguration配置来关闭或开启自动配置
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
// META-INF/spring-autoconfigure-metadata.properties 这个文件存储的是”待自动装配候选类“的过滤条件,框架会根据里面的规则逐一对候选类进行计算看是否需要被自动装配进容器
// 从配置文件 spring-autoconfigure-metadata.properties 获得自动装配类过滤相关的配置
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);

// 根据过滤规则筛选出来配置
// NO2
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}

// NO2
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// META-INF/spring.factories
// 借助SpringFactoriesLoader从配置文件 spring.factories 扫描自动配置,key为EnableAutoConfiguration类的
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 去除重复的
configurations = removeDuplicates(configurations);
// @EnableAutoConfiguration注解中配置的排除项 exclude和excludeName
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
// 去除掉排除项
configurations.removeAll(exclusions);
// 根据 spring-autoconfigure-metadata.properties 中的配置进行过滤
// NO3
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}

// NO3
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;
// 拿到OnBeanCondition,OnClassCondition和OnWebApplicationCondition, 然后遍历这三个条件类去过滤从spring.factories加载的大量配置类
for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {
// 调用各种aware方法,将beanClassLoader,beanFactory等注入到filter对象中
invokeAwareMethods(filter);
// 判断各种filter来判断每个candidate @ConditionalOnClass,@ConditionalOnBean和@ConditionalOnWebApplication里面的注解值是否匹配
boolean[] match = filter.match(candidates, autoConfigurationMetadata);
for (int i = 0; i < match.length; i++) {
if (!match[i]) {
// 不匹配的将记录在skip数组,标志skip[i]为true,也与candidates数组一一对应
skip[i] = true;
candidates[i] = null;
skipped = true;
}
}
}
// 若所有自动配置类经过OnBeanCondition,OnClassCondition和OnWebApplicationCondition过滤后,全部都匹配的话,则全部原样返回
if (!skipped) {
return configurations;
}
List<String> result = new ArrayList<>(candidates.length);
for (int i = 0; i < candidates.length; i++) {
// 若skip[i]为false,则说明是符合条件的自动配置类,此时添加到result集合中
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);
}

那么这个方法是什么时候调用的呢?

在进行上下文初始化的时候

1
refreshContext(context);

进行应用上下文刷新时,调用BeanFactory的前置处理器

1
invokeBeanFactoryPostProcessors(beanFactory);

找到所有的配置类来进行执行

1
2
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
.getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);