Spring Boot 自动配置原理:从注解到条件装配,一篇讲透
面试官:“Spring Boot 自动配置的原理是什么?”
你:“启动类上的@SpringBootApplication注解内部包含了@EnableAutoConfiguration,它通过AutoConfigurationImportSelector读取META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件,加载所有自动配置类,然后根据@Conditional系列条件注解按需装配 Bean。”
面试官:“那你知道条件注解有哪些?如何自定义自己的 Starter 吗?”
你:“……”
很多人能说出“约定大于配置”,但一追问底层实现就含糊了。本文从源码到实战,彻底讲透 Spring Boot 自动配置的核心原理。
一、什么是自动配置?
传统 Spring 开发需要手动配置大量 XML 或 Java Config,例如配置数据源、事务管理器、视图解析器等。Spring Boot 通过自动配置,根据项目依赖的 Jar 包、配置文件和系统环境,自动推断并注册合理的默认 Bean,极大减少了样板配置。
二、核心入口:@SpringBootApplication
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan(excludeFilters=...)public@interfaceSpringBootApplication{}@SpringBootApplication是一个复合注解,包含三个关键注解:
@SpringBootConfiguration:本质是@Configuration,表示当前类是配置类。@EnableAutoConfiguration:开启自动配置的核心。@ComponentScan:自动扫描当前包及子包下的组件。
所以,启动类上的@SpringBootApplication等于同时开启了组件扫描和自动配置。
三、核心:@EnableAutoConfiguration
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@AutoConfigurationPackage@Import(AutoConfigurationImportSelector.class)public@interfaceEnableAutoConfiguration{}关键点是@Import(AutoConfigurationImportSelector.class),它告诉 Spring 去加载AutoConfigurationImportSelector中定义的配置类。
AutoConfigurationImportSelector
AutoConfigurationImportSelector实现了DeferredImportSelector接口,它的selectImports方法会:
- 从
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件中读取所有自动配置类的全限定名。 - 经过过滤(排除、条件筛选)后,返回需要导入的配置类数组。
版本差异:Spring Boot 2.7 之前,自动配置类定义在
META-INF/spring.factories文件中,key 为org.springframework.boot.autoconfigure.EnableAutoConfiguration。2.7 开始逐步废弃,3.0 完全切换到AutoConfiguration.imports文件。
四、自动配置的加载流程
- 启动类被加载,
@SpringBootApplication触发@EnableAutoConfiguration。 AutoConfigurationImportSelector读取AutoConfiguration.imports文件,获取所有候选自动配置类(例如RedisAutoConfiguration、DataSourceAutoConfiguration等)。- 对每个候选配置类,应用一系列
@Conditional条件注解进行过滤。 - 只有满足条件的配置类才会被解析,其内部通过
@Bean定义的各种组件才会被注册到 Spring 容器中。
整个流程可以用下图表示:
启动类 @SpringBootApplication ↓ @EnableAutoConfiguration ↓ @Import(AutoConfigurationImportSelector) ↓ 读取 META-INF/spring/.../AutoConfiguration.imports ↓ 候选自动配置类列表 (如 RedisAutoConfiguration) ↓ 过滤 @Conditional 条件 ↓ 满足条件的配置类生效 ↓ 内部 @Bean 注册到容器五、条件注解:@Conditional 家族
自动配置之所以“智能”,核心在于条件注解。常用的包括:
| 注解 | 作用 |
|---|---|
@ConditionalOnClass | 类路径下存在指定类时生效 |
@ConditionalOnMissingClass | 类路径下不存在指定类时生效 |
@ConditionalOnBean | 容器中已存在指定 Bean 时生效 |
@ConditionalOnMissingBean | 容器中不存在指定 Bean 时生效 |
@ConditionalOnProperty | 配置文件中存在指定属性且值匹配时生效 |
@ConditionalOnWebApplication | 当前环境是 Web 应用时生效 |
@ConditionalOnNotWebApplication | 当前环境不是 Web 应用时生效 |
@ConditionalOnExpression | 根据 SpEL 表达式判断 |
举例:DataSourceAutoConfiguration中有如下条件:
@ConditionalOnClass({DataSource.class,EmbeddedDatabaseType.class})@ConditionalOnMissingBean(DataSource.class)只有当类路径下存在DataSource和EmbeddedDatabaseType类,且容器中没有自定义DataSourceBean 时,自动配置才会创建一个默认的 HikariCP 数据源。
六、用户配置覆盖自动配置
Spring Boot 自动配置遵循**“用户优先”**原则:
- 如果用户显式定义了某个 Bean,自动配置会通过
@ConditionalOnMissingBean自动跳过,不会重复注册。 - 配置文件(
application.properties或application.yml)中的属性会绑定到@ConfigurationProperties类,进而覆盖自动配置的默认值。
例如,RedisAutoConfiguration中:
@Bean@ConditionalOnMissingBean(name="redisTemplate")publicRedisTemplate<Object,Object>redisTemplate(RedisConnectionFactoryredisConnectionFactory){// 默认配置}如果用户自己定义了redisTemplateBean,默认的就不会被创建。
七、如何自定义 Starter?
理解了自动配置原理,就能轻松写出自己的 Starter。步骤:
1. 创建自动配置类
@Configuration@ConditionalOnClass(MyService.class)@EnableConfigurationProperties(MyProperties.class)publicclassMyAutoConfiguration{@Bean@ConditionalOnMissingBeanpublicMyServicemyService(MyPropertiesproperties){returnnewMyService(properties.getPrefix());}}2. 定义配置属性类
@ConfigurationProperties(prefix="my.starter")publicclassMyProperties{privateStringprefix="default";// getter/setter}3. 创建META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
文件内容为自动配置类的全限定名:
com.example.myautoconfig.MyAutoConfiguration4. 添加依赖
将该项目打包成 Jar,其他 Spring Boot 项目引入后,自动配置就会生效,用户可通过my.starter.prefix=xxx自定义配置。
八、常见面试追问
Q1:@SpringBootApplication中的@ComponentScan会扫描哪些包?
默认扫描启动类所在包及其子包。如果希望扫描其他包,可以用@ComponentScan指定,或者使用@SpringBootApplication(scanBasePackages = "...")。
Q2:自动配置的优先级如何?
自动配置的加载顺序可以通过@AutoConfigureBefore、@AutoConfigureAfter、@AutoConfigureOrder来控制。例如DataSourceAutoConfiguration通常在其他依赖数据源的自动配置之前执行。
Q3:如何排除某个自动配置类?
使用@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})或配置文件:
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfigurationQ4:为什么 Spring Boot 启动变慢了?如何优化?
启动时会加载大量自动配置类。可以通过spring.autoconfigure.exclude排除不需要的配置,或者使用@Conditional减少条件匹配开销。也可以开启debug=true查看自动配置匹配报告。
九、总结
| 关键组件 | 作用 |
|---|---|
@SpringBootApplication | 复合注解,开启自动配置和组件扫描 |
@EnableAutoConfiguration | 导入AutoConfigurationImportSelector |
AutoConfigurationImportSelector | 读取AutoConfiguration.imports文件 |
META-INF/spring/.../AutoConfiguration.imports | 存放所有自动配置类的全限定名 |
@Conditional系列注解 | 条件判断,按需加载 |
@ConfigurationProperties | 绑定外部配置属性 |
一句话记住自动配置原理:启动时扫描约定位置,条件注解按需加载,用户配置优先覆盖。
希望这篇文章能帮你彻底掌握 Spring Boot 自动配置,轻松应对面试和自定义 Starter 开发,欢迎继续讨论。