likes
comments
collection
share

Spring Boot 自动配置原理

作者站长头像
站长
· 阅读数 17

前言

使用 Spring Boot 可以高效地开发各种场景,特别是 web 场景下,导入一个 web-starter 依赖就可以完成 web 开发,并且我们并不需要做过多的配置。

简而言之,Spring Boot 的自动配置帮助我们将某些场景的通用配置自动加载到系统中,从而节省时间去完成主要功能

那么其实现原理是什么呢?

原理

首先查看 Spring Boot 的主启动程序的注解 @SpringBootApplication

我们查看源码可以看到它其实是一个组合注解:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
1@SpringBootConfiguration
2@EnableAutoConfiguration
3@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication{}

【1】@SpringBootConfiguration:标注当前类为一个配置类

【2】@EnableAutoConfiguration:开启自动配置功能,自动配置实现的重点❗

【3】@ComponentScan:包扫描,将哪些类作为容器注入到 Spring Boot 中

重点查看 @EnableAutoConfiguration ,查看源码可以看到:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
1@AutoConfigurationPackage	// 将主启动类所在的包的组件注入
2@Import(AutoConfigurationImportSelector.class)// 自动配置的原理
public @interface EnableAutoConfiguration {}

查看 @AutoConfigurationPackage ,查看源码:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
// 导入的是这个类,继续查看
@Import(AutoConfigurationPackages.Registrar.class)	
public @interface AutoConfigurationPackage {}
//===========================================================================
// AutoConfigurationPackage 所导入的类
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        // 这个方法会将主启动类所在包下的组件加入容器,
        // 在此处打个端点调试运行,可以看到 getPackageNames() 即为主启动类所在的包下的所有包名
        register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
    }

    @Override
    public Set<Object> determineImports(AnnotationMetadata metadata) {
        return Collections.singleton(new PackageImports(metadata));
    }

}

❗ 自动配置的重点:@Import(AutoConfigurationImportSelector.class),重点查看这个类 AutoConfigurationImportSelector

public class AutoConfigurationImportSelector implements ...... {
    // 获取自动配置类的集合,这些集合即为要注入的组件,排除掉部分组件后,再封装返回最后要注入的组件
    protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata){
        if (!isEnabled(annotationMetadata)) {
			return EMPTY_ENTRY;
		}
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
        // 【1】获取一系列自动配置类集合,在这里打个断点,可以查看获取到的自动配置类有哪些。可以查看 22 行的源码
		List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
		configurations = removeDuplicates(configurations);
        // 【2】被设置禁用自动配置的类
		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
		checkExcludedClasses(configurations, exclusions);
		configurations.removeAll(exclusions);
		configurations = getConfigurationClassFilter().filter(configurations);
		fireAutoConfigurationImportEvents(configurations, exclusions);
        // 【3】封装返回
		return new AutoConfigurationEntry(configurations, exclusions);
    }
   
    // 获取一系列自动配置类集合
    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        // 获取候选的自动配置类集合,通过工厂类完成,继续查看 SpringFactoriesLoader.loadFactoryNames() 源码,跳转到第 32 行
		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;
	}
}
//===========================================================================
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
    String factoryTypeName = factoryType.getName();
    // 调用了 loadSpringFactories,继续查看源码,跳转到第 38 行
    return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
//===========================================================================
// 为了方便阅读,逻辑有些许改动
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
    MultiValueMap<String, String> result = new LinkedMultiValueMap<>();
    // 可以看到,urls 后面被反复操作并将结果封装为 result 返回,可见 urls 是获取候选的自动配置类的重点
    // 查看源码,得知 FACTORIES_RESOURCE_LOCATION 的值为 META-INF/spring.factories
	Enumeration<URL> urls = (classLoader != null ?
					classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
					ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
    while (urls.hasMoreElements()) {
        URL url = urls.nextElement();
        UrlResource resource = new UrlResource(url);
        Properties properties = PropertiesLoaderUtils.loadProperties(resource);
        for (Map.Entry<?, ?> entry : properties.entrySet()) {
            String factoryTypeName = ((String) entry.getKey()).trim();
            for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
                result.add(factoryTypeName, factoryImplementationName.trim());
            }
        }
    }
    cache.put(classLoader, result);
    return result;
}

通过上面的源码查看,我们能够大致地知道 Spring Boot 自动配置的原理为:

  1. 获取META-INF/spring.factories配置文件,从而得到一系列的候选组件
  2. 排除掉部分候选组件,最后封装返回

META-INF/spring.factories 在哪呢?一般能够实现自动配置功能的依赖都会有这个文件,而 Spring Boot 就提供了自动配置功能,查看 spring-boot-autoconfigure-2.3.7.RELEASE.jar,可以看到:

Spring Boot 自动配置原理

查看文件,果不其然有各种配置类定义:

# 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.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.LifecycleAutoConfiguration,\
......

但是,这些自动配置类是否会全部加载,并且其中定义的 Bean 是否也会一并注入到容器中呢?

Spring Boot 是按需加载的,我们打开 AopAutoConfiguration的源码查看一下

@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration {

	@Configuration(proxyBeanMethods = false)
	@ConditionalOnClass(Advice.class)
	static class AspectJAutoProxyingConfiguration {}
}

可以看到使用了注解@ConditionalOnXxx,也就是说,Spring Boot 会根据条件选择是否加载这些配置类、组件到容器中。

因此,当我们需要启用某一块功能时,可以查看是否需要引入相应的 jar 包、启用特定的配置项。

并且有些自动配置类还绑定了配置类XxxPropertiesXxxProperties又与 Spring Boot配置文件绑定,而自动配置类注入的组件默认就是从 XxxProperties获取默认值进行设置。如

@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
public class DispatcherServletAutoConfiguration {

	@Configuration(proxyBeanMethods = false)
	@Conditional(DefaultDispatcherServletCondition.class)
	@ConditionalOnClass(ServletRegistration.class)
	@EnableConfigurationProperties(WebMvcProperties.class)
	protected static class DispatcherServletConfiguration {

		@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
		public DispatcherServlet dispatcherServlet(WebMvcProperties webMvcProperties) {
			DispatcherServlet dispatcherServlet = new DispatcherServlet();
            // 获取配置信息:webMvcProperties
			dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
			dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
			dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
			dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
			dispatcherServlet.setEnableLoggingRequestDetails(webMvcProperties.isLogRequestDetails());
			return dispatcherServlet;
		}
    }
}

总结

SpringBoot 主启动类的注解@SpringBootApplication,其实是组合注解,其组成为:

  • @SpringBootConfiguration 表明是一个配置类
  • @EnableAutoConfiguration 启用自动配置功能,自动配置的核心
    • @AutoConfigurationPackage 扫描主启动类所在包下的所有组件
    • @Import(AutoConfigurationImportSelector.class) 完成自动配置功能的类的核心组件
  • @ComponentScan 包扫描配置

SpringBoot 的自动配置流程大致为:

  1. 启动 SpringBoot 程序,SpringBoot 会扫描 jar 包下的 META_INF/spring.factories中定义的一系列自动配置类
  2. 将这些自动配置类扫描进来后,排除掉部分自动配置类
  3. 按需加载,根据条件@ConditionalXxx决定是否加载该自动配置类
    1. 通常也会绑定配置类XxxProperties.class
  4. 生效的配置文件会往容器中注入各种组件
  5. 由于通过自动配置注入了各种组件,所以某个场景的功能所需的配置就被设置好了,可以直接使用。例如 Spring MVC、DataSource 等等
  6. 可以自定义配置
    1. 使用@Bean覆盖掉 SpringBoot 底层使用的组件
    2. 查看底层XxxProperties源码使用到的哪些配置项,在配置文件自行配置
    3. 查看官方文档中各个配置项的说明 Common Application Properties