写于:2018-12-03 12:50:37

# 什么是自动化配置

Springboot 概述 中,通过简单的配置就实现了一个web应用,并且成功调用接口 localhost:8080/hello。整个操作中,没有任何的配置,直接就启动了一个web应用,这得益于 spring boot 的自动化配置。

在 spring boot 存在很多的 xxxxAutoConfigration 结尾的配置类,而这些类都是相关的自动化配置类。其原理就是:springboot 提前帮我们写好了配置 ,这也是 Spring 贯彻始终的思想 简化开发,根据常规的开发方式,使用默认的配置,减少的代码量,提高了开发效率。为了提供灵活性高,当自动配置类无法满足我们需求时可以定制化。

下面来看看官方对 自动化配置的解释

Spring Boot auto-configuration attempts to automatically configure your Spring application based on the jar dependencies that you have added. For example, if HSQLDB is on your classpath, and you have not manually configured any database connection beans, then Spring Boot auto-configures an in-memory database.

You need to opt-in to auto-configuration by adding the @EnableAutoConfiguration or @SpringBootApplication annotations to one of your @Configuration classes.

大体翻译如下:

Spring Boot 会根据你添加的 jar 依赖尝试自动配置相关的配置。

而需要开启自动配置功能需要将 @EnableAutoConfiguration 或者 @SpringBootApplication 添加到 应用的某个配置类上。

换句话说,只要在 spring 会扫描到的 class 文件加上 @EnableAutoConfiguration 或者 @SpringBootApplication 便能够开启自动配置功能,而自动配置功能,会根据依赖的 jar 包中提供的自动配置进行自动配置。

小贴士

通过查看 @SpringBootApplication 源码,

  @EnableAutoConfiguration
  public @interface SpringBootApplication {
    ......
  }

得知 @SpringBootApplicaion 之所以能够支持自动配置功能,来自于其派生了 @EnableAutoConfiguration 的能力。

# 自动化配置原理

根据官方给出的提示从:@EnableAutoConfiguration 入手进行源码分析

# step1、找到入口 @EnableAutoConfiguration,代码如下

@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 {};

}

通过 @Import 能够知道,@EnableAutoConfiguration 加载的配置其实为 AutoConfigurationImportSelector

小贴士

@EnableAutoConfiguration 中的 spring.boot.enableautoconfiguration 是用来控制自动配置的开关。当不想使用自动化配置的时候,将 spring.boot.enableautoconfiguration 设置为 false 即可关闭自动配置功能。

相关源码: AutoConfigurationImportSelector

  public class AutoConfigurationImportSelector ......{
    protected boolean isEnabled(AnnotationMetadata metadata) {
		if (getClass() == AutoConfigurationImportSelector.class) {
		  return getEnvironment().getProperty(
			EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY
			,Boolean.class,true);
		}
		return true;
    }
  }

# step2、聚焦 被 Import 的类 AutoConfigurationImportSelector 相关代码

代码追踪图如下

# 下来看看 UML 中 ⑤ 、⑥ 的部分源码

public final class SpringFactoriesLoader {
	public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
		// 自动配置时, factoryClassName = org.springframework.boot.autoconfigure.EnableAutoConfiguration
		String factoryClassName = factoryClass.getName(); 
		return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
	}

	/** The location to look for factories. 
	 * Can be present in multiple JAR files
	 */
	public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
	private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
		......
		Enumeration<URL> urls = (classLoader != null ?
					classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
					ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
		......
	}
}

上述源码功能讲解

  • SpringFactoriesLoader#loadSpringFactories 会加载 META-INF/spring.factories 中配置的所有的 class 。
  • SpringFactoriesLoader#loadFactoryNames 从加载的所有信息中过滤出在 META-INF/spring.factories 中 key = org.springframework.boot.autoconfigure.EnableAutoConfiguration 的所有 clas 文件。

下面配上一张 META-INF/spring.factories 部分信息图

# 接下来再来看看 ② 的相关代码

public class AutoConfigurationImportSelector ......{
	protected AutoConfigurationEntry getAutoConfigurationEntry(){
		......
		// 获取到所有的自动配置 class
		List<String> configurations = getCandidateConfigurations(annotationMetadata,attributes);
		// 过滤多余的自动配置 class
		configurations = removeDuplicates(configurations);
		// 根据 @EnableAutoConfiguraton 配置的 exclude 去除相关的 clas 
		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
		checkExcludedClasses(configurations, exclusions);
		configurations.removeAll(exclusions);
		configurations = filter(configurations, autoConfigurationMetadata);
		fireAutoConfigurationImportEvents(configurations, exclusions);
		return new AutoConfigurationEntry(configurations, exclusions);
	}
}

上述源码大体功能讲解

获取到所有依赖 jar 中的 META-INF/spring.factories 中的 org.springframework.boot.autoconfigure.EnableAutoConfiguration 下的所有 自动配置 class 文件,然后去除重复的,被排除的一些 class 文件,最后得出本次启动需要加载的自动配置 class 列表。 class 列表最终由 ImportSelector#selectImports ,然后被初始化并注入到 IOC 容器中。

下面配上一张,最简 spring boot 应用需要加载的自动配置 class 列表

小贴士

TODO ImportSelector 接口文章(springframework)

# 总结

@EnableAutoConfiguration 会将 AutoConfigurationImportSelector 通过 @Import 加载到 Spring 上下文中。

AutoConfigurationImportSelector 会通过其他类的帮助从 META-INF/spring.factories 中获取到 org.springframework.boot.autoconfigure.EnableAutoConfiguration 配置下的所有 class 文件全路径列表,并根据相关规则过滤掉,如重复的,被排除的,最后得到本次应用启动需要被加载的所有自动配置类的 class 全路径列表。最后有 Spring 将其实例化并装配。

小贴士

Spring Boot 自动配置实现的前提是自动配置类在被引入依赖时未被 Spring 中的 @ComponentScan 扫描到。

需要注意的是,@SpringBootApplication 中的 @ComponentScan 默认扫描的是 Application.class main class 下的包路径,如果引入的依赖 starter 的包路径和应用程序的扫描路径一直,则被引用的依赖包中的所有配置类都会被扫到 IOC 容器中,按需开启的注解式自动配置不再有效。

精彩内容推送,请关注公众号!
最近更新时间: 4/5/2020, 3:33:26 PM