blog

SpringBoot的spring-autoconfigure-metadata.properties配置文件作用

之前一直不清楚SpringBoot的jar包中的spring-autoconfigure-metadata.properties的作用,今天查了一下官方文档,发现是有介绍的。记录一下。


autoconfigure模块包含了库启动必须的所有信息。也可以包含配置key的定义(如@ConfigurationProperties)以及任何用于定制组件如何初始化的回调接口。

最好标识对库的依赖为可选,这样你就可以更加容易引入autoconfigure模块。如果你这样做了(指标识对库的依赖为可选),如果库没有提供,那么SpringBoot会使用默认的方式。

SpringBoot使用注解处理器从元数据文件(metadata file)中收集自动配置条件(META-INF/spring-autoconfigure-metadata.properties)。如果文件存在,则用它来过滤不匹配的自动配置,这样可以提高启动时间。建议添加如下包括自动配置的依赖。

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-autoconfigure-processor</artifactId>
	<optional>true</optional>
</dependency>

SpringBoot中关于spring-autoconfigure-metadata.properties文件作用的描述就上面一点点,看了和没看一个样。看来只能通过查看源代码的方式看它的作用了

其实关于这个文件,在@SpringBootApplication注解中就有看到,只不过当时没有注意而已。这里接着@SpringBootApplication注解AutoConfigurationImportSelector类那里来写。

AutoConfigurationImportSelector类中selectImports方法如下:

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);
}

其中会调用AutoConfigurationMetadataLoader类的loadMetadata方法

loadMetadata方法如下:

protected static final String PATH = META-INF/
			+ spring-autoconfigure-metadata.properties;
public static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {
		return loadMetadata(classLoader, PATH);
	}

	static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader, String path) {
		try {
			Enumeration<URL> urls = (classLoader != null) ? classLoader.getResources(path)
					: ClassLoader.getSystemResources(path);
			Properties properties = new Properties();
			while (urls.hasMoreElements()) {
				properties.putAll(PropertiesLoaderUtils
						.loadProperties(new UrlResource(urls.nextElement())));
			}
			return loadMetadata(properties);
		}
		catch (IOException ex) {
			throw new IllegalArgumentException(
					Unable to load @ConditionalOnClass location [ + path + ], ex);
		}
	}
static AutoConfigurationMetadata loadMetadata(Properties properties) {
		return new PropertiesAutoConfigurationMetadata(properties);
	}	

loadMetadata会加载所有jar包中的META-INF/spring-autoconfigure-metadata.properties文件,然后返回PropertiesAutoConfigurationMetadata类的实例。

回到selectImports方法中,通过调用getCandidateConfigurations方法获取所有META-INF/spring.factories文件中key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的值,将配置了excludeexcludeName的类排除掉。然后调用filter方法

	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);
			...
		}
		...
	}

getAutoConfigurationImportFilters方法返回的是META-INF/spring.factories文件中key为org.springframework.boot.autoconfigure.AutoConfigurationImportFilter的值。

AutoConfigurationImportFilter的实现类只有一个,org.springframework.boot.autoconfigure.condition.OnClassCondition,对应的match方法及相关方法如下:

public boolean[] match(String[] autoConfigurationClasses,
			AutoConfigurationMetadata autoConfigurationMetadata) {
		ConditionEvaluationReport report = getConditionEvaluationReport();
		ConditionOutcome[] outcomes = getOutcomes(autoConfigurationClasses,
				autoConfigurationMetadata);
		...
	}
	
	private ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses,
			AutoConfigurationMetadata autoConfigurationMetadata) {
		// Split the work and perform half in a background thread. Using a single
		// additional thread seems to offer the best performance. More threads make
		// things worse
		int split = autoConfigurationClasses.length / 2;
		OutcomesResolver firstHalfResolver = createOutcomesResolver(
				autoConfigurationClasses, 0, split, autoConfigurationMetadata);
		OutcomesResolver secondHalfResolver = new StandardOutcomesResolver(
				autoConfigurationClasses, split, autoConfigurationClasses.length,
				autoConfigurationMetadata, this.beanClassLoader);
		ConditionOutcome[] secondHalf = secondHalfResolver.resolveOutcomes();
		ConditionOutcome[] firstHalf = firstHalfResolver.resolveOutcomes();
		ConditionOutcome[] outcomes = new ConditionOutcome[autoConfigurationClasses.length];
		System.arraycopy(firstHalf, 0, outcomes, 0, firstHalf.length);
		System.arraycopy(secondHalf, 0, outcomes, split, secondHalf.length);
		return outcomes;
	}

org.springframework.boot.autoconfigure.condition.OnClassCondition.StandardOutcomesResolverresolveOutcomes方法

public ConditionOutcome[] resolveOutcomes() {
			return getOutcomes(this.autoConfigurationClasses, this.start, this.end,this.autoConfigurationMetadata);
	}

	private ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses,
				int start, int end, AutoConfigurationMetadata autoConfigurationMetadata) {
		ConditionOutcome[] outcomes = new ConditionOutcome[end - start];
		for (int i = start; i < end; i++) {
			String autoConfigurationClass = autoConfigurationClasses[i];
			Set<String> candidates = autoConfigurationMetadata
					.getSet(autoConfigurationClass, "ConditionalOnClass");
			if (candidates != null) {
				outcomes[i - start] = getOutcome(candidates);
			}
		}
		return outcomes;
	}

整体读下来,发现其实获取的是META-INF/spring-autoconfigure-metadata.properties文件中配置了类名.ConditionalOnClass对应的值。

打开spring-boot-actuator-autoconfigure-2.0.9.RELEASE.jar中的/META-INF/spring-autoconfigure-metadata.properties文件,随便找一个类的配置,比如RedisHealthIndicatorAutoConfiguration,发现这里的配置和class文件中注解其实是一一对应的。 所以感觉spring-autoconfigure-metadata.properties应该是工具自动生成出来的。但现在还不知道这个猜测对不对。后续知道了再补充吧