使用 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 自动配置的原理为:
META-INF/spring.factories配置文件,从而得到一系列的候选组件而META-INF/spring.factories 在哪呢?一般能够实现自动配置功能的依赖都会有这个文件,而 Spring Boot 就提供了自动配置功能,查看 spring-boot-autoconfigure-2.3.7.RELEASE.jar,可以看到:

查看文件,果不其然有各种配置类定义:
- # 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 包、启用特定的配置项。
并且有些自动配置类还绑定了配置类XxxProperties,XxxProperties又与 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 的自动配置流程大致为:
META_INF/spring.factories中定义的一系列自动配置@ConditionalXxx决定是否加载该自动配置类
XxxProperties.class@Bean覆盖掉 SpringBoot 底层使用的组件XxxProperties源码使用到的哪些配置项,在配置文件自行配置