👋 前言
我们已经完整打通了 Spring 核心底层:IOC → Bean 生命周期 → 循环依赖 → AOP → 事务 → Spring MVC。
而 Spring Boot 的出现,让我们告别了复杂的 XML 配置,一个 @SpringBootApplication 注解就能启动 Web 应用。这背后,就是 自动配置(Auto-Configuration) 的魔力。
本篇基于 Spring Boot 2.7.x 版本进行拆解:
@SpringBootApplication三大注解到底做了什么?@EnableAutoConfiguration是如何实现自动装配的?spring.factories与 SPI 机制的底层逻辑?@Conditional条件注解家族如何生效?- 自定义 Starter 的完整实现与最佳实践。
一、核心入口:@SpringBootApplication 注解拆解
java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class)
})
public @interface SpringBootApplication {
// ...
}
它本质是一个组合注解,核心由三部分组成:
- @SpringBootConfiguration :继承自
@Configuration,标记当前类为配置类 - @EnableAutoConfiguration:自动配置的核心开关
- @ComponentScan:默认扫描当前包及其子包下的所有组件
注意 :
@SpringBootApplication底层还隐式包含@AutoConfigurationPackage,作用是注册启动类所在包为自动配置包,这是 "同包 / 子包组件能被扫描" 的关键底层原因。
二、灵魂核心:@EnableAutoConfiguration 工作原理
java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
// ...
}
真正的魔法,来自它导入的 AutoConfigurationImportSelector。
1. AutoConfigurationImportSelector 核心作用
它实现了 DeferredImportSelector 接口,会在所有 @Configuration 配置类处理完之后,再执行导入逻辑,负责:
- 加载所有自动配置类
- 收集候选配置,交由 Spring 容器做条件判断
- 返回要导入的类全限定名,不直接注册配置类到容器,由 Spring 统一注册
核心入口方法是 selectImports:
java
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
selectImports() 方法正常情况下会返回一个包含所有候选自动配置类全限定名的 String[] 数组。只有在自动配置被全局禁用(即配置 spring.boot.enableautoconfiguration=false)时,才会返回 NO_IMPORTS 常量。
2. 自动配置类的加载过程
关键方法 getAutoConfigurationEntry:
java
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
// 1. 获取候选的自动配置类列表
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 2. 去重、排除、过滤
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = getConfigurationClassFilter().filter(configurations);
// 3. 触发自动配置导入事件
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
三、自动配置类从哪来?SPI 机制与 spring.factories
1. 传统 SPI 机制回顾
Java SPI 允许服务提供者通过 META-INF/services/接口名 文件,声明接口的实现类,由 ServiceLoader 加载。Spring Boot 正是利用了类似的思想。
2. Spring Boot 自动配置的 SPI 文件
Spring Boot 2.7.x 之前 ,自动配置类的定义在 META-INF/spring.factories 文件中,例如 spring-boot-autoconfigure 模块中:
properties
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
...
getCandidateConfigurations 方法就是通过 SpringFactoriesLoader.loadFactoryNames 来加载这些类名的。
3. Spring Boot 2.7.x+ 的新方式
从 2.7.x 开始,改为使用 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件,格式更简洁,加载性能更高,由 ImportCandidates 加载,不再使用 SpringFactoriesLoader。
四、条件装配:@Conditional 注解家族
加载到的自动配置类,并不是都会生效,Spring Boot 会通过 @Conditional 系列注解进行条件判断,条件判断在配置类解析阶段执行,不在 ImportSelector 中过滤,只有满足条件的配置类才会被实例化。
常用条件注解
| 注解 | 作用 |
|---|---|
@ConditionalOnClass |
类路径下存在指定类时生效 |
@ConditionalOnMissingClass |
类路径下不存在指定类时生效 |
@ConditionalOnBean |
容器中存在指定 Bean 时生效 |
@ConditionalOnMissingBean |
容器中不存在指定 Bean 时生效 |
@ConditionalOnProperty |
配置文件中存在指定属性且值匹配时生效 |
@ConditionalOnWebApplication |
当前是 Web 应用时生效 |
@ConditionalOnNotWebApplication |
当前不是 Web 应用时生效 |
注意 :这些注解的底层,都是
@Conditional,并配合对应的Condition实现类,例如OnClassCondition、OnBeanCondition等,会在配置类解析阶段进行判断,决定是否加载该配置类。
五、案例拆解:WebMvcAutoConfiguration 自动配置
以最常用的 Web MVC 自动配置为例,看它是如何生效的:
java
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({
DispatcherServletAutoConfiguration.class,
TaskExecutionAutoConfiguration.class,
ValidationAutoConfiguration.class
})
public class WebMvcAutoConfiguration {
// 配置默认的视图解析器、消息转换器等
@Configuration(proxyBeanMethods = false)
@Import(EnableWebMvcConfiguration.class)
@EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })
@Order(0)
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {
// ...
}
}
可以看到,它满足以下条件才会生效:
- 当前是 Servlet Web 应用
- 类路径下存在
Servlet、DispatcherServlet - 容器中没有
WebMvcConfigurationSupport类型的 Bean
六、自定义 Starter:从零实现一个
1. 核心步骤
- 创建一个 Maven 项目
- 编写自动配置类
MyServiceAutoConfiguration - 在配置类中使用
@Bean注册自定义的服务 Bean - 添加条件注解,确保配置类在合适的条件下生效
- 在
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports中声明你的配置类 - 打包发布,其他项目引入依赖即可自动装配
2. 示例代码
自动配置类
java
@Configuration
@ConditionalOnClass(MyService.class)
@EnableConfigurationProperties(MyServiceProperties.class)
public class MyServiceAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public MyService myService(MyServiceProperties properties) {
MyService service = new MyService();
service.setPrefix(properties.getPrefix());
return service;
}
}
配置属性类
java
@ConfigurationProperties(prefix = "my.service")
public class MyServiceProperties {
private String prefix = "Hello";
// getter/setter
}
声明自动配置
在 src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件中:
com.example.mystarter.MyServiceAutoConfiguration
---## 七、Spring Boot 自动配置精简流程
流程图:
┌─────────────────────┐
│ @SpringBootApplication │
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│ @EnableAutoConfiguration │
└──────────┬──────────┘
│
▼
┌─────────────────────────────┐
│ AutoConfigurationImportSelector │
└──────────┬──────────────────┘
│
▼
┌─────────────────────────────┐
│ 加载候选自动配置类列表 │
└──────────┬──────────────────┘
│
▼
┌─────────────────────────────┐
│ 过滤、去重、排除配置类 │
└──────────┬──────────────────┘
│
▼
┌─────────────────────────────┐
│ @Conditional 条件筛选 │
└──────────┬──────────────────┘
│
▼
┌─────────────────────────────┐
│ 符合条件的配置类生效 │
└──────────┬──────────────────┘
│
▼
┌─────────────────────────────┐
│ 注册到 Spring 容器 │
└──────────┬──────────────────┘
│
▼
┌─────────────────────────────┐
│ 完成自动装配 │
└─────────────────────────────┘
详细步骤说明:
- 启动入口 :
@SpringBootApplication注解触发@EnableAutoConfiguration - 选择器加载 :
AutoConfigurationImportSelector负责加载所有候选自动配置类 - 候选配置 :从
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件(Spring Boot 2.7.x+)或spring.factories(旧版本)加载配置类列表 - 初步过滤:对配置类进行去重、排除用户指定的配置类
- 条件筛选 :根据
@Conditional系列注解(如@ConditionalOnClass、@ConditionalOnBean等)进行条件判断 - 配置生效:只有满足所有条件的配置类才会被实例化并注册到 Spring 容器
- 完成装配:所有生效的配置类创建相应的 Bean,完成自动配置过程
核心要点:
- 自动配置是"按需加载"的,只有满足条件的配置类才会生效
- 条件判断基于类路径、Bean存在性、配置文件属性等多种因素
- 整个过程在应用启动时自动完成,无需手动配置
八、高频面试题
1. Spring Boot 自动配置的原理是什么?
答 :基于 @EnableAutoConfiguration,通过 AutoConfigurationImportSelector 加载所有自动配置类,再通过 @Conditional 条件注解进行筛选,将符合条件的配置类注册到容器中。
2. spring.factories 是做什么的?
答 :它是 Spring Boot 早期实现 SPI 扩展的文件,用于声明所有自动配置类的全限定名,由 SpringFactoriesLoader 加载。在 2.7.x 版本及之前,它是 Spring Boot SPI 机制的核心,用于声明自动配置类。但在 3.0 版本之后,它已被 AutoConfiguration.imports 文件取代。
3. 如何自定义一个 Spring Boot Starter?
答 :编写自动配置类、配置属性类,声明在 AutoConfiguration.imports 文件中,打包后引入依赖即可自动装配。
4. @Conditional 系列注解的作用?
答:条件装配,只有满足特定条件时,对应的配置类或 Bean 才会被创建,是自动配置实现按需加载的核心。
📚 总结
Spring Boot 自动配置通过 @EnableAutoConfiguration 和 AutoConfigurationImportSelector 实现了"约定大于配置"的理念。它结合了 Java SPI 机制和条件注解,让开发者能够快速搭建项目,同时保留了足够的灵活性进行自定义扩展。
理解自动配置原理,不仅能帮助我们更好地使用 Spring Boot,还能让我们在遇到问题时快速定位,并能够根据业务需求定制自己的 Starter,提升开发效率。