当前位置: 首页 > news >正文

Spring Boot 自动配置全流程深度解析

        在 Spring Boot 的世界里,“约定优于配置” 理念通过自动配置机制展现得淋漓尽致。从一个简单的@SpringBootApplication注解开始,背后隐藏着一套精妙的自动配置加载流程。本文将从@SpringBootApplication出发,逐步拆解自动配置类是如何被发现、加载到 Spring 容器中的,结合关键源码,带你看透自动配置的本质。

一、@SpringBootApplication:自动配置的入口

注解拆解

@SpringBootApplication是一个复合注解,核心包含以下三个关键注解:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration // 标记为Spring Boot配置类,本质是@Configuration
@EnableAutoConfiguration // 开启自动配置核心注解
@ComponentScan(excludeFilters = { // 组件扫描,默认扫描当前包及子包@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {//... 其他属性
}

其中,@EnableAutoConfiguration是触发自动配置的关键开关,它的功能实现依赖 @Import 导入的ImportSelector实现类,开启自动配置类的加载流程。

关于@Import注解的详细内容,可以看:

​​​​​​Spring 中 @Import 注解:Bean 注入的灵活利器-CSDN博客

二、@EnableAutoConfiguration:开启自动配置引擎

@EnableAutoConfiguration 的结构

@EnableAutoConfiguration注解定义如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage 
@Import(AutoConfigurationImportSelector.class) 
public @interface EnableAutoConfiguration {String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";Class<?>[] exclude() default {};String[] excludeName() default {};
}

这里通过 @Import 引入 AutoConfigurationImportSelector ,这个类实现了 ImportSelector接口,负责收集并返回需要自动配置的类名集合,是自动配置类加载的核心桥梁。

三、AutoConfigurationImportSelector:筛选自动配置类

1)selectImports 方法:启动加载流程

AutoConfigurationImportSelector 实现了 ImportSelector接口,重写 selectImports方法

@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {if (!isEnabled(annotationMetadata)) { return NO_IMPORTS;}AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); 
}

逻辑是:先判断自动配置是否启用(可通过spring.boot.enableautoconfiguration属性控制),若启用则调用getAutoConfigurationEntry获取自动配置入口,最终返回需要注入容器的配置类名数组。

2)getAutoConfigurationEntry:构建配置入口

getAutoConfigurationEntry方法是加载自动配置类的核心逻辑入口:

protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {if (!isEnabled(annotationMetadata)) {return EMPTY_ENTRY;}// 1. 获取注解属性AnnotationAttributes attributes = getAttributes(annotationMetadata); // 2. 获取候选配置类List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); // 3. 去重、排除处理// 去重configurations = removeDuplicates(configurations); // 排除指定配置Set<String> exclusions = getExclusions(annotationMetadata, attributes); checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions); // 4. 条件过滤configurations = getConfigurationClassFilter().filter(configurations);//  5. 事件发布fireAutoConfigurationImportEvents(configurations, exclusions); return new AutoConfigurationEntry(configurations, exclusions); 
}
1. 获取注解属性

AnnotationAttributes attributes = getAttributes(annotationMetadata);

通过 getAttributes 方法,从 @EnableAutoConfiguration 注解元数据(annotationMetadata)里,提取注解属性(像 exclude、excludeName 等配置 ),为后续筛选配置类提供依据,明确哪些配置类要排除。

2. 获取候选配置类

List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);

getCandidateConfigurations 方法是核心,它会去加载 META-INF/spring.factories 文件中,key 为 EnableAutoConfiguration 对应的配置类全限定名,把这些配置类收集成列表,作为自动配置的 “候选池” 。

3. 去重、排除处理

去重:

configurations = removeDuplicates(configurations);

调用 removeDuplicates,遍历 configurations 列表,剔除重复的配置类全限定名,保证配置类唯一。

排除指定配置

Set<String> exclusions = getExclusions(annotationMetadata, attributes); checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions);

4. 条件过滤
  • getExclusions:从注解属性里解析出用户通过 exclude 等指定的、要排除的配置类名,存入 exclusions 集合。
  • checkExcludedClasses:校验排除的类是否合法(比如类是否存在等 )。
  • configurations.removeAll(exclusions):从候选配置类列表里,移除 exclusions 中指定的配置类,实现用户自定义排除。

configurations = getConfigurationClassFilter().filter(configurations);

getConfigurationClassFilter 获取条件过滤器(内部结合 @Conditional 系列注解,如 @ConditionalOnClass、@ConditionalOnBean 等逻辑 ),对 configurations 列表遍历筛选,只有满足所有 @Conditional 条件的配置类,才会保留,确保最终加载的配置类符合运行环境要求。

5. 事件发布

fireAutoConfigurationImportEvents(configurations, exclusions);

fireAutoConfigurationImportEvents 方法发布自动配置加载事件(如AutoConfigurationImportEvent ),让项目里的 ApplicationListener 监听器能感知到自动配置加载过程,便于做拓展(比如记录日志、额外初始化逻辑等 )。

3)getCandidateConfigurations:加载 spring.factories 配置

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;
}@Override
protected Class<?> getSpringFactoriesLoaderFactoryClass() {return EnableAutoConfiguration.class; 
}

关键逻辑:

SpringFactoriesLoader.loadFactoryNames 会从所有 Jar 包的META-INF/spring.factories文件中,读取key为 EnableAutoConfiguration.class 对应的配置类列表。例如,Spring Boot 自身的自动配置、第三方 Starter(如 MyBatis - Starter)的自动配置,都通过这种方式 “注册” 到加载流程中。

四、SpringFactoriesLoader:实现配置类发现

SpringFactoriesLoader是 Spring 框架提供的工具类,核心方法loadFactoryNames逻辑简化如下:

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {String factoryTypeName = factoryType.getName();return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, 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)); Map<String, List<String>> factories = new LinkedHashMap<>();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())) {factories.add(factoryTypeName, factoryImplementationName.trim()); }}}cache.put(classLoader, factories);return factories;}catch (IOException ex) {throw new IllegalArgumentException("Unable to load factories from location [" +FACTORIES_RESOURCE_LOCATION + "]", ex);}
}

其中:

  • FACTORIES_RESOURCE_LOCATION 常量值为"META-INF/spring.factories",指定了配置文件路径。
  • 方法会遍历类路径下所有 Jar 包的META-INF/spring.factories文件,将文件内容解析为Properties,再根据factoryType(这里是EnableAutoConfiguration)的全类名,提取对应的配置类名列表。

五、自动配置类的生效:@Conditional 的把关

加载到候选配置类后,Spring Boot 并非直接全部注入容器,而是通过 **@Conditional系列注解 ** 做 “条件校验”。常见条件注解:

  • @ConditionalOnClass:类路径下存在指定类时生效。
  • @ConditionalOnBean:容器中存在指定 Bean 时生效。
  • @ConditionalOnProperty:配置属性满足条件时生效(如spring.datasource.enabled=true )。

以DataSourceAutoConfiguration为例,简化代码:

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class }) 
@ConditionalOnMissingBean(type = "javax.sql.DataSource") 
@EnableConfigurationProperties(DataSourceProperties.class) 
public class DataSourceAutoConfiguration {//... 配置数据源Bean逻辑
}

只有项目依赖了DataSource相关类、容器中没有自定义DataSource Bean 时,该自动配置类才会真正生效,向容器注入数据源相关 Bean。

六、总结:自动配置全流程脉络

@SpringBootApplication开始,自动配置的完整流程可梳理为:

1. 注解触发:@SpringBootApplication 包含 @EnableAutoConfiguration,通过 @Import引入AutoConfigurationImportSelector(ImportSelector接口的实现类)。

2. 收集配置类AutoConfigurationImportSelectorselectImports 方法调用getAutoConfigurationEntry,再通过 getCandidateConfigurations,借助SpringFactoriesLoader 读取所有 Jar 包META-INF/spring.factories 中EnableAutoConfiguration 对应的配置类。

3. 筛选生效类:结合 @Conditional 注解,过滤出满足条件的自动配置类,注入 Spring 容器,完成自动配置。

这套机制让 Spring Boot 能根据项目依赖 “智能” 配置环境,既降低了手动配置的复杂度,又通过@Conditional保证了灵活性,是 Spring Boot “开箱即用” 特性的核心支撑。理解这一流程,无论排查自动配置问题,还是自定义 Starter,都能做到心中有数 。 

http://www.dtcms.com/a/344409.html

相关文章:

  • Linux虚拟机安装FTP
  • 「越短越合法」型滑动窗口
  • Seaborn数据可视化实战:Seaborn基础图表绘制入门
  • 分布式日志分析平台(ELFK 与 EFK)理论
  • 【机器学习深度学习】大模型分布式推理概述:从显存困境到高并发挑战的解决方案
  • 技术干货|使用Prometheus+Grafana监控Tomcat实例详解
  • [特殊字符] TTS格局重塑!B站推出Index-TTS,速度、音质、情感表达全维度领先
  • TTC协议(TTS即ORACLE DATA)协议分析
  • 代码随想录刷题Day40
  • week3-[二维数组]最大列
  • 文件系统层面的可用块数量可用空间和比例
  • 【Python-Day 40】告别内存溢出!Python 生成器 (Generator) 的原理与实战
  • 网络抓包介绍
  • Conmi的正确答案——Ubuntu24.04禁用任何休眠
  • CTF-RSA-openssl-pem格式的key
  • C++中不加{}导致的BUG
  • 笔记本怎么才能更快散热?
  • vsCode或Cursor 使用remote-ssh插件链接远程终端
  • Flask数据库迁移实战指南
  • Flask电影投票系统全解析
  • 近期https接口的联调小记
  • STM32——SPI通信+W25Q64
  • 一体化伺服电机在特种机器人(炉管爬行器)中的应用案例
  • ShimetaPi M4-R1 :OpenHarmony 开发板解析
  • Mysql事务特性及原理
  • 网络安全基础知识
  • 异步开发的三种实现方式
  • 香港券商櫃台系統跨境金融研究
  • CTFshow系列——命令执行web45-48
  • 优选算法1:双指针