SpringBoot 入门与自动配置原理
目录
- 一、面试考点速览
- [二、SpringBoot 核心理念](#二、SpringBoot 核心理念)
- [三、@SpringBootApplication 拆解](#三、@SpringBootApplication 拆解)
- 四、自动配置原理深度解析
- [五、案例实战:自定义 Starter](#五、案例实战:自定义 Starter)
- 六、面试真题解析
- 七、常见错误与避坑指南
- 八、面试追问链设计
- 九、本章总结

一、面试考点速览
| 考点 | 面试频率 | 难度 | 核心考察点 |
|---|---|---|---|
| SpringBoot 核心优势 | ⭐⭐⭐⭐⭐ | 初级 | 约定优于配置、起步依赖、自动配置 |
| @SpringBootApplication 注解 | ⭐⭐⭐⭐ | 初级 | 三个元注解的作用 |
| 自动配置原理 | ⭐⭐⭐⭐⭐ | 中级 | spring.factories、条件注解、AutoConfigurationImportSelector |
| 条件注解机制 | ⭐⭐⭐⭐ | 中级 | @ConditionalOnClass、@ConditionalOnMissingBean 等 |
| 自定义 Starter | ⭐⭐⭐ | 高级 | 命名规范、自动配置类编写、spring.factories 注册 |
本期核心线索 :从一个 @SpringBootApplication 注解出发,一路追踪到 spring.factories 文件,彻底搞懂 SpringBoot「零配置」背后的真相。
二、SpringBoot 核心理念
2.1 传统 SSM 开发的痛点
先看一段没有 SpringBoot 时的项目配置:
xml
<!-- pom.xml:手动管理几十个依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.23</version> <!-- 版本得跟其他 Spring 组件对齐 -->
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.23</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.4</version>
</dependency>
<!-- 还有 20+ 个依赖... -->
xml
<!-- web.xml:配置 DispatcherServlet -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</init-param>
</servlet>
xml
<!-- applicationContext.xml:配置数据源、事务管理器、视图解析器... -->
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource">
<property name="url" value="${jdbc.url}"/>
<property name="driverClassName" value="${jdbc.driver}"/>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 还有几十个 Bean 配置... -->
三大痛点:
| 痛点 | 描述 |
|---|---|
| 依赖地狱 | 手动管理几十个依赖的版本兼容性 |
| 配置地狱 | web.xml + applicationContext.xml + springmvc.xml,XML 写到吐 |
| 部署地狱 | 需要外置 Tomcat,打 WAR 包,配置 Servlet 容器 |
2.2 SpringBoot 的三大核心设计
SpringBoot 不是 Spring 的替代品,而是在 Spring 之上的脚手架。它的三大核心设计直接解决了上述痛点:
| 设计理念 | 解决的问题 | 实现方式 |
|---|---|---|
| 约定优于配置 | 配置地狱 | 内置大量默认配置,开发者只需覆盖差异部分 |
| 起步依赖 | 依赖地狱 | spring-boot-starter-* 一站式引入所有相关依赖,版本由 parent 统一管理 |
| 自动配置 | 繁琐的 Bean 注册 | 根据 classpath 中的类自动装配 Spring Bean |
同样的项目,SpringBoot 只需:
xml
<!-- pom.xml:一个 starter 替代几十个依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
yaml
# application.yml:数据库连接(其他全部默认)
spring:
datasource:
url: jdbc:mysql://localhost:3306/edulearn
username: root
password: 123456
java
// 一个注解替代所有 XML 配置
@SpringBootApplication
public class EduLearnApplication {
public static void main(String[] args) {
SpringApplication.run(EduLearnApplication.class, args);
}
}
面试回答模板:
"SpringBoot 的核心设计理念是约定优于配置。传统 SSM 项目需要大量 XML 配置和手动依赖管理,SpringBoot 通过起步依赖(starter)解决了依赖兼容性问题,通过自动配置(auto-configuration)根据 classpath 自动装配 Bean,通过内嵌容器(Embedded Tomcat)实现直接运行。这三者让开发者可以专注于业务代码而非配置。"
三、@SpringBootApplication 拆解
3.1 三层注解结构
@SpringBootApplication 是 SpringBoot 的灵魂注解,但它本身是一个组合注解:
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 {
// ...
}

3.2 第一层:@SpringBootConfiguration
java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration // ← 本质上就是一个 @Configuration
@Indexed
public @interface SpringBootConfiguration {
}
作用 :标记当前类是一个 Spring 配置类(等价于 @Configuration),意味着可以在该类中使用 @Bean 定义 Bean。
为什么多此一举? SpringBoot 通过层次化配置 来找配置类:先找 @SpringBootConfiguration,找不到再找 @Configuration。这给了 SpringBoot 测试框架(@SpringBootTest)更大的灵活性来自动定位主配置类。
3.3 第二层:@EnableAutoConfiguration(★★★ 核心)
这是自动配置的入口注解,也是本章最重要的内容:
java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage // ← 子注解1:自动配置包
@Import(AutoConfigurationImportSelector.class) // ← 子注解2:导入自动配置选择器
public @interface EnableAutoConfiguration {
}
两个关键子注解:
| 子注解 | 作用 |
|---|---|
@AutoConfigurationPackage |
将主配置类所在包注册为「自动配置包」,后续自动扫描基于此包路径 |
@Import(AutoConfigurationImportSelector.class) |
自动配置的核心 :加载并筛选所有 META-INF/spring.factories 中的配置类 |
3.4 第三层:@ComponentScan
java
// @SpringBootApplication 中的 @ComponentScan 有默认行为
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class)
})
作用 :扫描主配置类所在包及其子包,将 @Component、@Service、@Controller、@Repository 等注解的类注册为 Bean。
关键细节 :扫描范围是主配置类所在的包路径。这就是为什么 SpringBoot 项目通常把启动类放在根包下------确保所有子包的组件都能被扫描到。
四、自动配置原理深度解析
这是本期最核心的章节,也是面试中拉开差距的地方。
4.1 自动配置的完整链路
SpringBoot 启动
│
▼
SpringApplication.run()
│
▼
refresh() → invokeBeanFactoryPostProcessors()
│
▼
ConfigurationClassPostProcessor 解析 @Import(AutoConfigurationImportSelector.class)
│
▼
AutoConfigurationImportSelector.selectImports()
│
├── 1. 从 META-INF/spring.factories 读取所有候选配置类
├── 2. 从 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 读取(2.7+)
├── 3. 通过条件注解筛选(@ConditionalOnClass / @ConditionalOnMissingBean 等)
└── 4. 返回符合条件的配置类列表给 Spring 容器

4.2 spring.factories:自动配置的注册表
spring.factories 是 SpringBoot 的 SPI(Service Provider Interface)机制,位于 spring-boot-autoconfigure jar 包的 META-INF/ 目录下。
# spring-boot-autoconfigure-2.7.x.jar!/META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
... (共 100+ 个自动配置类)
加载时机 :SpringFactoriesLoader.loadFactoryNames(type, classLoader) 在容器启动阶段读取这个文件。
版本注意 :SpringBoot 2.7 开始引入新的注册方式
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports,逐步取代spring.factories。SpringBoot 3.x 已完全迁移。面试中两种方式都需了解。
4.3 AutoConfigurationImportSelector:筛选器
java
public class AutoConfigurationImportSelector
implements DeferredImportSelector, BeanClassLoaderAware, ... {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
// 1. 加载所有候选配置类
AutoConfigurationEntry entry = getAutoConfigurationEntry(annotationMetadata);
// 2. 返回通过筛选的配置类
return StringUtils.toStringArray(entry.getConfigurations());
}
protected AutoConfigurationEntry getAutoConfigurationEntry(
AnnotationMetadata annotationMetadata) {
// Step 1: 从 spring.factories 加载所有候选
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// Step 2: 去重
configurations = removeDuplicates(configurations);
// Step 3: 获取用户排除的配置(spring.autoconfigure.exclude)
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
// Step 4: 应用过滤器链(条件注解筛选)
configurations = filter(configurations, autoConfigurationMetadata);
// Step 5: 发布自动配置导入事件
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
}
4.4 条件注解:自动配置的开关
自动配置不是无条件全部生效的。每个自动配置类上都有一系列条件注解,Spring 只在条件满足时才加载它。
| 条件注解 | 作用 | 示例 |
|---|---|---|
@ConditionalOnClass |
classpath 中存在指定类时生效 | @ConditionalOnClass({DataSource.class}) |
@ConditionalOnMissingClass |
classpath 中不存在指定类时生效 | @ConditionalOnMissingClass({"com.mysql.cj.jdbc.Driver"}) |
@ConditionalOnBean |
容器中存在指定 Bean 时生效 | @ConditionalOnBean(DataSource.class) |
@ConditionalOnMissingBean |
容器中不存在指定 Bean 时生效 | @ConditionalOnMissingBean(DataSource.class) |
@ConditionalOnProperty |
配置文件中存在指定属性时生效 | @ConditionalOnProperty(name = "spring.datasource.url") |
@ConditionalOnResource |
classpath 中存在指定资源时生效 | @ConditionalOnResource(resources = "classpath:mysql.properties") |
@ConditionalOnWebApplication |
当前是 Web 应用时生效 | --- |
@ConditionalOnExpression |
SpEL 表达式为 true 时生效 | --- |
4.5 以 DataSourceAutoConfiguration 为例
让我们用面试中最常被问到的 DataSource 自动配置来串联上述知识点:
java
// DataSourceAutoConfiguration.java(源码简化)
@AutoConfiguration(before = SqlInitializationAutoConfiguration.class)
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class }) // ① 有 DataSource 类才生效
@ConditionalOnMissingBean(type = "io.r2dbc.spi.ConnectionFactory") // ② 没使用 R2DBC 才生效
@EnableConfigurationProperties(DataSourceProperties.class) // ③ 绑定 spring.datasource.* 配置
@Import({ DataSourcePoolMetadataProvidersConfiguration.class,
DataSourceCheckpointRestoreConfiguration.class })
public class DataSourceAutoConfiguration {
@Configuration(proxyBeanMethods = false)
@Conditional(PooledDataSourceCondition.class) // ④ 连接池条件
@ConditionalOnMissingBean({ DataSource.class, XADataSource.class }) // ⑤ 用户没自定义 DataSource 才自动创建
@Import({ DataSourceConfiguration.Hikari.class, // ⑥ HikariCP(默认)
DataSourceConfiguration.Tomcat.class, // ⑥ Tomcat 连接池
DataSourceConfiguration.Dbcp2.class, ... }) // ⑥ DBCP2
static class PooledDataSourceConfiguration {
}
}
阅读这段源码需要回答三个问题:
Q1:SpringBoot 怎么知道用 HikariCP 而不是 DBCP2?
看 DataSourceConfiguration.Hikari:
java
@ConditionalOnClass(HikariDataSource.class) // classpath 中有 HikariCP 就生效
@ConditionalOnMissingBean(DataSource.class) // 且没有自定义 DataSource
@ConditionalOnProperty(name = "spring.datasource.type",
havingValue = "com.zaxxer.hikari.HikariDataSource", matchIfMissing = true) // 或显式指定
static class Hikari {
@Bean
DataSource dataSource(DataSourceProperties properties) {
HikariDataSource ds = properties.initializeDataSourceBuilder()
.type(HikariDataSource.class).build();
// ... 配置连接池参数
return ds;
}
}
spring-boot-starter-data-jpa 自带了 HikariCP 的依赖,所以 classpath 中有 HikariDataSource.class,HikariCP 配置自动生效。
Q2:如果我自己定义了一个 DataSource Bean 会怎样?
@ConditionalOnMissingBean(DataSource.class) 会检测到容器中已有 DataSource,自动装配被跳过,使用你的自定义 DataSource。
Q3:如果我想排除某个自动配置类,怎么做?
yaml
# application.yml
spring:
autoconfigure:
exclude:
- org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
或注解方式:
java
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class EduLearnApplication { ... }
五、案例实战:自定义 Starter
现在用 EduLearn 课程管理模块来实战------手写一个课程缓存 Starter。
5.1 需求场景
EduLearn 平台课程信息查询频繁,我们希望:引入一个 course-cache-spring-boot-starter 依赖,就能自动获得课程缓存能力,零配置开箱即用。
5.2 Starter 命名规范
Spring 官方对 Starter 命名有严格约定:
| 类型 | 命名 | 示例 |
|---|---|---|
| 官方 Starter | spring-boot-starter-{模块} |
spring-boot-starter-web |
| 第三方 Starter | {模块}-spring-boot-starter |
mybatis-spring-boot-starter |
我们的 Starter 命名为 course-cache-spring-boot-starter。
5.3 项目结构
course-cache-spring-boot-starter/
├── pom.xml
└── src/main/
├── java/com/edulearn/cache/
│ ├── CourseCacheAutoConfiguration.java // 自动配置类
│ ├── CourseCacheProperties.java // 配置属性类
│ ├── CourseCacheService.java // 缓存服务
│ └── CourseCacheAspect.java // AOP 切面(可选)
└── resources/META-INF/
└── spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports // 2.7+ 注册方式
5.4 核心代码
Step 1:配置属性类
java
@ConfigurationProperties(prefix = "edulearn.cache.course")
public class CourseCacheProperties {
/**
* 是否启用课程缓存,默认 true
*/
private boolean enabled = true;
/**
* 缓存过期时间(秒),默认 3600 秒(1 小时)
*/
private long ttl = 3600;
/**
* 最大缓存条目数,默认 1000
*/
private int maxSize = 1000;
// getter / setter 省略
}
Step 2:缓存服务
java
public class CourseCacheService {
private final Map<Long, Course> cache = new ConcurrentHashMap<>();
private final Map<Long, Long> expireMap = new ConcurrentHashMap<>();
private final long ttlMillis;
private final int maxSize;
public CourseCacheService(CourseCacheProperties properties) {
this.ttlMillis = properties.getTtl() * 1000;
this.maxSize = properties.getMaxSize();
}
public Course get(Long courseId) {
Long expireTime = expireMap.get(courseId);
if (expireTime != null && System.currentTimeMillis() > expireTime) {
cache.remove(courseId);
expireMap.remove(courseId);
return null;
}
return cache.get(courseId);
}
public void put(Long courseId, Course course) {
if (cache.size() >= maxSize) {
// 简易淘汰:清除最早过期的一半
evictExpired();
}
cache.put(courseId, course);
expireMap.put(courseId, System.currentTimeMillis() + ttlMillis);
}
public void evict(Long courseId) {
cache.remove(courseId);
expireMap.remove(courseId);
}
private void evictExpired() {
long now = System.currentTimeMillis();
expireMap.entrySet().removeIf(e -> e.getValue() <= now);
cache.keySet().retainAll(expireMap.keySet());
}
}
Step 3:自动配置类(核心)
java
@AutoConfiguration
@ConditionalOnClass(CourseService.class) // 项目中存在 CourseService 才生效
@EnableConfigurationProperties(CourseCacheProperties.class)
public class CourseCacheAutoConfiguration {
@Bean
@ConditionalOnMissingBean // 用户可以自己定义覆盖
@ConditionalOnProperty(
name = "edulearn.cache.course.enabled",
havingValue = "true",
matchIfMissing = true) // 默认启用
public CourseCacheService courseCacheService(CourseCacheProperties properties) {
return new CourseCacheService(properties);
}
@Bean
@ConditionalOnBean(CourseCacheService.class) // 有缓存服务才注册切面
public CourseCacheAspect courseCacheAspect(CourseCacheService cacheService) {
return new CourseCacheAspect(cacheService);
}
}
Step 4:注册自动配置(spring.factories)
# META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
com.edulearn.cache.CourseCacheAutoConfiguration
Step 5:主项目引入
xml
<dependency>
<groupId>com.edulearn</groupId>
<artifactId>course-cache-spring-boot-starter</artifactId>
<version>1.0.0</version>
</dependency>
yaml
# 可选:自定义缓存参数
edulearn:
cache:
course:
ttl: 1800 # 30 分钟过期
max-size: 500
5.5 Starter 工作原理总结
启动类 @SpringBootApplication
→ @EnableAutoConfiguration
→ @Import(AutoConfigurationImportSelector.class)
→ 扫描所有 jar 包中的 META-INF/spring.factories
→ 找到 course-cache-spring-boot-starter 中注册的
CourseCacheAutoConfiguration
→ 检查条件注解:
✅ @ConditionalOnClass(CourseService.class) → 通过
✅ @ConditionalOnProperty(enabled=true) → 通过
✅ @ConditionalOnMissingBean → 通过
→ 注册 CourseCacheService 和 CourseCacheAspect
六、面试真题解析
真题 1:SpringBoot 相比 Spring 有什么优势?
频次 :⭐⭐⭐⭐⭐
难度:初级
标准回答:
- 起步依赖 :
spring-boot-starter-*一站式引入所有相关依赖,版本由 parent POM 统一管理,消除了依赖冲突 - 自动配置:根据 classpath 中的类自动装配 Bean,开发者可以零 XML 配置启动项目
- 内嵌容器 :内置 Tomcat/Jetty/Undertow,
java -jar直接运行,不依赖外部 Servlet 容器 - Actuator 监控:生产级端点(health/metrics/env),无需额外集成
- 外部化配置 :统一的
application.yml/properties,支持多环境配置(profile)
加分回答 :SpringBoot 不是替代 Spring,而是对 Spring 的封装和增强------底层还是 Spring 的 IoC/AOP,SpringBoot 做的是把"该怎么做"变成了"默认帮你做好"。
真题 2:@SpringBootApplication 做了什么?
频次 :⭐⭐⭐⭐
难度:初级
标准回答:它是一个组合注解,包含三个核心注解:
- @SpringBootConfiguration (等价于
@Configuration):标记当前类是配置类 - @EnableAutoConfiguration :启用自动配置,通过
@Import(AutoConfigurationImportSelector.class)加载spring.factories中所有候选配置类,再用条件注解筛选 - @ComponentScan :扫描当前包及子包下的
@Component等注解类,自动注册为 Bean
真题 3:自动配置原理是什么?
频次 :⭐⭐⭐⭐⭐
难度:中级
标准回答流程:
@SpringBootApplication
→ @EnableAutoConfiguration
→ @Import(AutoConfigurationImportSelector.class)
→ selectImports() 方法执行
→ SpringFactoriesLoader.loadFactoryNames()
→ 读取 META-INF/spring.factories 中
org.springframework.boot.autoconfigure.EnableAutoConfiguration 的所有值
→ 得到 100+ 个候选配置类全限定名
→ 去重 + 排除(spring.autoconfigure.exclude)
→ 条件注解筛选(@ConditionalOnClass、@ConditionalOnMissingBean 等)
→ 返回符合条件的配置类列表
关键追问 :"怎么筛选的?" → 每个自动配置类上都有条件注解,如 @ConditionalOnClass 检查 classpath 中是否存在特定类,@ConditionalOnMissingBean 检查容器中是否已有自定义 Bean。
真题 4:如何自定义一个 Starter?
频次 :⭐⭐⭐
难度:高级
要点:
- 命名:
{模块}-spring-boot-starter - 创建自动配置类,使用
@AutoConfiguration+ 条件注解 - 创建配置属性类,使用
@ConfigurationProperties(prefix = "...") - 在
META-INF/spring.factories(或 2.7+ 的.imports文件)中注册自动配置类 - 可选的
spring-boot-configuration-processor依赖提供 IDE 自动提示
本文 5.2~5.5 节已给出完整实战代码,面试时可直接引用 EduLearn 课程缓存 Starter 案例。
七、常见错误与避坑指南
7.1 包扫描遗漏
java
// ❌ 错误:启动类在 com.edulearn 包下,但 Service 在 com.other 包
package com.edulearn;
@SpringBootApplication
public class EduLearnApplication { }
package com.other; // 不在 com.edulearn 及其子包下
@Service
public class OtherService { } // 不会被扫描到!
解决:
java
@SpringBootApplication
@ComponentScan(basePackages = {"com.edulearn", "com.other"}) // 显式添加扫描包
public class EduLearnApplication { }
7.2 条件注解的"失效"陷阱
java
@AutoConfiguration
@ConditionalOnClass(RedisTemplate.class)
public class RedisAutoConfiguration {
@Bean
public MyCacheManager cacheManager() {
return new MyCacheManager();
}
}
// 项目中定义了 RedisTemplate Bean,但自动配置仍不生效?
原因 :@ConditionalOnClass 检查的是 classpath 中是否有这个类 ,不是检查容器中是否有这个 Bean。如果需要检查 Bean,应该用 @ConditionalOnBean(RedisTemplate.class)。
7.3 spring.factories 注册遗漏
自定义 Starter 写好了,但在主项目中引入后没有任何效果------十有八九是忘记注册了。
检查清单:
-
META-INF/spring.factories文件存在 - 文件中
org.springframework.boot.autoconfigure.EnableAutoConfiguration的 key 拼写正确 - 自动配置类的全限定名拼写正确(没有多余空格)
- (2.7+)
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件也配置了
八、面试追问链设计
面试官会从"SpringBoot 有什么优势"开始,一路问到"自定义 Starter 怎么写",层层递进。
8.1 SpringBoot 核心优势(3 层追问)
第一层 :SpringBoot 相比 Spring 有什么优势?
→ 三大核心:起步依赖、自动配置、内嵌容器。详见 六、面试真题解析-真题1
第二层 :起步依赖是怎么解决版本冲突的?
→ SpringBoot 的 parent POM 定义了所有兼容版本的 <dependencyManagement>,起步依赖只需声明 GAV(不需要版本号),版本由 parent 统一管控。同时 spring-boot-dependencies BOM 使用了 <properties> 集中管理版本号,升级只需改一处。
第三层 :内嵌容器怎么启动的?
→ SpringApplication.run() 调用 refresh() 的 onRefresh() 步骤,ServletWebServerApplicationContext 在此创建并启动内嵌 Tomcat(TomcatServletWebServerFactory.getWebServer()),注册 DispatcherServlet,最后在 finishRefresh() 中启动 WebServer。
8.2 自动配置原理(5 层追问)
第一层 :自动配置是怎么实现的?
→ @EnableAutoConfiguration → @Import(AutoConfigurationImportSelector.class) → 读取 spring.factories → 条件注解筛选 → 返回配置类。详见 四、自动配置原理深度解析
第二层 :AutoConfigurationImportSelector 的执行时机?
→ 在 refresh() 的 invokeBeanFactoryPostProcessors() 步骤中,由 ConfigurationClassPostProcessor 解析 @Import 时触发。因为 AutoConfigurationImportSelector 实现了 DeferredImportSelector,所以它的执行被推迟到所有 @Configuration 类解析完成后,确保用户自定义 Bean 优先。
第三层 :spring.factories 和 2.7 新式的 .imports 文件有什么区别?
→ spring.factories 是 SpringBoot 1.x~2.6.x 的注册方式,一个文件可以注册多种类型(不只是自动配置类)。2.7 开始引入 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 专门用于注册自动配置类,每行一个类名,更简洁。SpringBoot 3.x 已移除 spring.factories 的支持。
第四层 :条件注解 @ConditionalOnMissingBean 在自动配置中有什么特殊作用?
→ 它是"用户自定义优先"的关键机制。如果用户定义了一个同类型 Bean,自动配置就退让,不做覆盖。这保证了"约定优于配置"的弹性------你什么都不做时用默认值,你做了就听你的。
第五层 :如果两个 Starter 都提供了同一个类型的自动配置 Bean,谁生效?
→ 这取决于 @AutoConfiguration 的 before/after 属性(类似于 @Order)。SpringBoot 对内置自动配置有严格的排序定义。如果有冲突且没有排序,后加载的会覆盖先加载的(后者覆盖前者的 BeanDefinition)。这就是为什么自定义 Starter 的 Bean 通常加 @ConditionalOnMissingBean------避免覆盖内置配置。
8.3 自定义 Starter(4 层追问)
第一层 :怎么自定义一个 Starter?
→ 创建自动配置类 + 配置属性类 + META-INF/spring.factories 注册。详见 五、案例实战
第二层 :Starter 的命名有什么规范?为什么这么规定?
→ 官方 spring-boot-starter-*,第三方 *-spring-boot-starter。这样规定的目的是:让 IDE 的依赖搜索和 Maven 仓库的分类更清晰。官方 Starter 以 spring-boot-starter- 开头,一眼可识别。
第三层 :spring-boot-configuration-processor 是干什么的?
→ 它是一个注解处理器,编译时解析 @ConfigurationProperties 类,生成 spring-configuration-metadata.json(包含属性名、类型、描述、默认值),让 IDE 在 application.yml 中提供自动补全和文档提示。
第四层 :Starter 和普通依赖的本质区别是什么?
→ 普通依赖只是把类加到 classpath,需要开发者手动配置才能生效。Starter 是自带配置的依赖 ,通过 spring.factories 和条件注解,让容器自动发现并装配。本质区别在于是否有自动配置的注册和条件激活机制。
面试追问链速查表
| 入口问题 | 追问方向 | 深度 |
|---|---|---|
| SpringBoot 有什么优势? | → 起步依赖版本管控 → 内嵌容器启动流程 | 3 层 |
| 自动配置原理? | → ImportSelector 执行时机 → spring.factories vs .imports → @ConditionalOnMissingBean 的退让机制 → 多 Starter Bean 冲突 | 5 层 |
| 自定义 Starter? | → 命名规范 → configuration-processor 作用 → Starter vs 普通依赖 | 4 层 |
| @SpringBootApplication? | → 三层拆解 → @EnableAutoConfiguration 源码 → @ComponentScan 默认扫描范围 | 3 层 |
九、本章总结
核心要点回顾
- SpringBoot ≠ 替代 Spring:它是 Spring 之上的脚手架,底层仍是 IoC/AOP
- 三大核心设计:约定优于配置(默认即最佳)、起步依赖(版本统一管理)、自动配置(classpath 驱动装配)
- @SpringBootApplication = @Configuration + @EnableAutoConfiguration + @ComponentScan
- 自动配置原理 :
AutoConfigurationImportSelector→ 读spring.factories→ 条件注解筛选 → 注册 Bean - 条件注解是自动配置的灵魂 :
@ConditionalOnClass/@ConditionalOnMissingBean实现了"有则用默认,无则用你的" - 自定义 Starter:自动配置类 + 配置属性类 + spring.factories 注册,三件套
面试 checklist
- 能说清 SpringBoot 三大核心设计
- 能拆解 @SpringBootApplication 的三层结构
- 能画出自动配置的完整链路图(spring.factories → 条件筛选 → Bean 注册)
- 能解释 @ConditionalOnMissingBean 的退让机制
- 能从零写一个自定义 Starter
- 能回答 DataSource 自动配置的源码级别问题
下一步学习建议
- 动手实践:基于 EduLearn 课程管理模块,扩展自定义缓存 Starter(加 Redis 版本)
- 深入源码 :阅读
AutoConfigurationImportSelector和ConfigurationClassPostProcessor的完整源码 - 扩展知识:了解 SpringBoot 3.x 的 AOT 编译和 GraalVM 原生镜像支持
下期预告:第 3 期《Spring Web MVC 深度解析》,将深入 DispatcherServlet 的请求处理流程、拦截器与过滤器的区别、RESTful API 设计最佳实践。