欢迎光临
我们一直在努力

【Spring】SpringBoot 入门与自动配置原理

SpringBoot 入门与自动配置原理

目录

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

在这里插入图片描述

一、面试考点速览

考点面试频率难度核心考察点
SpringBoot 核心优势 ⭐⭐⭐⭐⭐ 初级 约定优于配置、起步依赖、自动配置
@SpringBootApplication 注解 ⭐⭐⭐⭐ 初级 三个元注解的作用
自动配置原理 ⭐⭐⭐⭐⭐ 中级 spring.factories、条件注解、AutoConfigurationImportSelector
条件注解机制 ⭐⭐⭐⭐ 中级 @ConditionalOnClass、@ConditionalOnMissingBean 等
自定义 Starter ⭐⭐⭐ 高级 命名规范、自动配置类编写、spring.factories 注册

本期核心线索:从一个 @SpringBootApplication 注解出发,一路追踪到 spring.factories 文件,彻底搞懂 SpringBoot「零配置」背后的真相。

二、SpringBoot 核心理念

2.1 传统 SSM 开发的痛点

先看一段没有 SpringBoot 时的项目配置:

<!– 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+ 个依赖… –>

<!– 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>

<!– 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 只需:

<!– pom.xml:一个 starter 替代几十个依赖 –>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

# application.yml:数据库连接(其他全部默认)
spring:
datasource:
url: jdbc:mysql://localhost:3306/edulearn
username: root
password: 123456

// 一个注解替代所有 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 的灵魂注解,但它本身是一个组合注解:

@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

@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(★★★ 核心)

这是自动配置的入口注解,也是本章最重要的内容:

@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

// @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:筛选器

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 自动配置来串联上述知识点:

// 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:

@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:如果我想排除某个自动配置类,怎么做?

# application.yml
spring:
autoconfigure:
exclude:
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration

或注解方式:

@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:配置属性类

@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:缓存服务

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:自动配置类(核心)

@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:主项目引入

<dependency>
<groupId>com.edulearn</groupId>
<artifactId>course-cache-spring-boot-starter</artifactId>
<version>1.0.0</version>
</dependency>

# 可选:自定义缓存参数
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 包扫描遗漏

    // ❌ 错误:启动类在 com.edulearn 包下,但 Service 在 com.other 包
    package com.edulearn;
    @SpringBootApplication
    public class EduLearnApplication { }

    package com.other; // 不在 com.edulearn 及其子包下
    @Service
    public class OtherService { } // 不会被扫描到!

    解决:

    @SpringBootApplication
    @ComponentScan(basePackages = {"com.edulearn", "com.other"}) // 显式添加扫描包
    public class EduLearnApplication { }

    7.2 条件注解的"失效"陷阱

    @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 设计最佳实践。

    赞(0)
    未经允许不得转载:171主机测评 » 【Spring】SpringBoot 入门与自动配置原理
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址