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的自动配置原理我们心里也有了答案。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值