Springboot项目中最核心也是入门级的注解就是@SpringBootApplication,这是由@EnableAutoConfiguration、@ComponentScan和@Configuration组成的一个组合注解。
接下来我们将分析其中的EnableAutoConfiguration注解的源码。
springboot的一大特性就是“约定大于配置”,EnableAutoConfiguration这个注解从字面上就可以理解是一个实现自动配置的注解,也是“约定”能够生效的一大保障。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

	String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

	/**
	 * Exclude specific auto-configuration classes such that they will never be applied.
	 * @return the classes to exclude
	 */
	Class<?>[] exclude() default {};

	/**
	 * Exclude specific auto-configuration class names such that they will never be
	 * applied.
	 * @return the class names to exclude
	 * @since 1.3.0
	 */
	String[] excludeName() default {};

}

EnableAutoConfiguration有两大属性,exclude和excludeName都是用来指定不需要自动配置的配置类。
分析完EnableAutoConfiguration的属性,接下来和其它Enable类型的注解一下,我们进入@import注解指定的类,分析springboot是如何实现自动配置的。
AutoConfigurationImportSelector是一个ImportSelector类,selectImports的返回值就是其最后注入IOC容器的配置类。另外,AutoConfigurationImportSelector还用到了beanClassLoader、environment、resourceLoader,因此其还实现了BeanClassLoaderAware,EnviromentAware,ResourceLoaderAware接口。

@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
	if (!isEnabled(annotationMetadata)) {
		return NO_IMPORTS;
	}
	try {
		AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
				.loadMetadata(this.beanClassLoader);
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
		List<String> configurations = getCandidateConfigurations(annotationMetadata,
				attributes);
		configurations = removeDuplicates(configurations);
		configurations = sort(configurations, autoConfigurationMetadata);
		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
		checkExcludedClasses(configurations, exclusions);
		configurations.removeAll(exclusions);
		configurations = filter(configurations, autoConfigurationMetadata);
		fireAutoConfigurationImportEvents(configurations, exclusions);
		return StringUtils.toStringArray(configurations);
	}
	catch (IOException ex) {
		throw new IllegalStateException(ex);
	}
}

阅读代码,可以发现其步骤如下:

  1. 获取所有的候选配置
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的相关方法如下:

public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
	String factoryClassName = factoryClass.getName();
	return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
	MultiValueMap<String, String> result = cache.get(classLoader);
	if (result != null)
		return result;
	try {
		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()) {
				List<String> factoryClassNames = Arrays.asList(
						StringUtils.commaDelimitedListToStringArray((String) entry.getValue()));
				result.addAll((String) entry.getKey(), factoryClassNames);
			}
		}
		cache.put(classLoader, result);
		return result;
	}
	catch (IOException ex) {
		throw new IllegalArgumentException("Unable to load factories from location [" +
				FACTORIES_RESOURCE_LOCATION + "]", ex);
	}
}

loadSpringFactories方法获取当前项目和所有jar包路径下的"META-INF/spring.factories"文件,其中就是约定的所有自动配置的类。
如spring-boot-autoconfigure-2.0.2.RELEASE.jar和spring-cloud-openfeign-core-2.0.0.M1.jar下的该文件:
EnableAutoConfiguration源码解析
EnableAutoConfiguration源码解析

  1. 去除重复项
protected final <T> List<T> removeDuplicates(List<T> list) {
	return new ArrayList<>(new LinkedHashSet<>(list));
}
  1. 排序
    如下所示,按order排序
private List<String> sort(List<String> configurations,
		AutoConfigurationMetadata autoConfigurationMetadata) throws IOException {
	configurations = new AutoConfigurationSorter(getMetadataReaderFactory(),
			autoConfigurationMetadata).getInPriorityOrder(configurations);
	return configurations;
}
public List<String> getInPriorityOrder(Collection<String> classNames) {
		final AutoConfigurationClasses classes = new AutoConfigurationClasses(
				this.metadataReaderFactory, this.autoConfigurationMetadata, classNames);
		List<String> orderedClassNames = new ArrayList<>(classNames);
		// Initially sort alphabetically
		Collections.sort(orderedClassNames);
		// Then sort by order
		orderedClassNames.sort((o1, o2) -> {
			int i1 = classes.get(o1).getOrder();
			int i2 = classes.get(o2).getOrder();
			return Integer.compare(i1, i2);
		});
		// Then respect @AutoConfigureBefore @AutoConfigureAfter
		orderedClassNames = sortByAnnotation(classes, orderedClassNames);
		return orderedClassNames;
	}
  1. exclusions项处理
    如何对于项目中某些自动配置类,我们不希望其自动配置,name我们可以通过EnableAutoConfiguration的exclude或excludeName属性进行配置,或者也可以在配置文件里通过配置项“spring.autoconfigure.exclude”进行配置。
    (1)找到不希望自动配置的配置类exclusions
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
protected Set<String> getExclusions(AnnotationMetadata metadata,
		AnnotationAttributes attributes) {
	Set<String> excluded = new LinkedHashSet<>();
	excluded.addAll(asList(attributes, "exclude"));
	excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName")));
	excluded.addAll(getExcludeAutoConfigurationsProperty());
	return excluded;
}
private List<String> getExcludeAutoConfigurationsProperty() {
	if (getEnvironment() instanceof ConfigurableEnvironment) {
		Binder binder = Binder.get(getEnvironment());
		return binder.bind(PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE, String[].class)
				.map(Arrays::asList).orElse(Collections.emptyList());
	}
	String[] excludes = getEnvironment()
			.getProperty(PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE, String[].class);
	return (excludes == null ? Collections.emptyList() : Arrays.asList(excludes));
}

(2)对exclusions进行验证
exclusions必须是自动配置类,否则抛出异常。

checkExcludedClasses(configurations, exclusions);
private void checkExcludedClasses(List<String> configurations,
		Set<String> exclusions) {
	List<String> invalidExcludes = new ArrayList<>(exclusions.size());
	for (String exclusion : exclusions) {
		if (ClassUtils.isPresent(exclusion, getClass().getClassLoader())
				&& !configurations.contains(exclusion)) {
			invalidExcludes.add(exclusion);
		}
	}
	if (!invalidExcludes.isEmpty()) {
		handleInvalidExcludes(invalidExcludes);
	}
}

/**
 * Handle any invalid excludes that have been specified.
 * @param invalidExcludes the list of invalid excludes (will always have at least one
 * element)
 */
protected void handleInvalidExcludes(List<String> invalidExcludes) {
	StringBuilder message = new StringBuilder();
	for (String exclude : invalidExcludes) {
		message.append("\t- ").append(exclude).append(String.format("%n"));
	}
	throw new IllegalStateException(String
			.format("The following classes could not be excluded because they are"
					+ " not auto-configuration classes:%n%s", message));
}

(3)移除exclusions

configurations.removeAll(exclusions);
  1. 根据项目中的AutoConfigurationImportFilter类进行过滤
    在项目中找到所有AutoConfigurationImportFilter类进行过滤,对于自动配置类,只要其不满足任意一个filter的match方法,就将其进行过滤,不再自动配置。
    最常见的filter类是OnClassCondition类,与ConditionalOnClass注解息息相关,判断当前类加载路径下是否有对应文件。
configurations = filter(configurations, autoConfigurationMetadata);
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);
}
protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
	return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class,
			this.beanClassLoader);
}
  1. 配置监听事件
fireAutoConfigurationImportEvents(configurations, exclusions);
private void fireAutoConfigurationImportEvents(List<String> configurations,
		Set<String> exclusions) {
	List<AutoConfigurationImportListener> listeners = getAutoConfigurationImportListeners();
	if (!listeners.isEmpty()) {
		AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this,
				configurations, exclusions);
		for (AutoConfigurationImportListener listener : listeners) {
			invokeAwareMethods(listener);
			listener.onAutoConfigurationImportEvent(event);
		}
	}
}

至此,自动配置的逻辑实现。
最后提一点,按照springboot的编码格式,springboot源码中所有形如XXXAutoConfiguration的配置类都是自动配置类,无需其他操作即可注入IOC容器。

相关文章:

  • 2021-06-04
  • 2022-01-19
  • 2021-07-18
  • 2021-04-02
  • 2021-11-20
  • 2021-06-04
  • 2021-07-24
猜你喜欢
  • 2021-12-06
  • 2021-10-12
  • 2021-07-20
  • 2021-06-02
  • 2022-12-23
  • 2021-07-08
相关资源
相似解决方案