【Spring】面试突击系列(二):SpringBoot 入门与自动配置原理

SpringBoot 入门与自动配置原理

目录

一、面试考点速览

考点 面试频率 难度 核心考察点
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 有什么优势?

频次 :⭐⭐⭐⭐⭐

难度:初级

标准回答

  1. 起步依赖spring-boot-starter-* 一站式引入所有相关依赖,版本由 parent POM 统一管理,消除了依赖冲突
  2. 自动配置:根据 classpath 中的类自动装配 Bean,开发者可以零 XML 配置启动项目
  3. 内嵌容器 :内置 Tomcat/Jetty/Undertow,java -jar 直接运行,不依赖外部 Servlet 容器
  4. Actuator 监控:生产级端点(health/metrics/env),无需额外集成
  5. 外部化配置 :统一的 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?

频次 :⭐⭐⭐

难度:高级

要点

  1. 命名:{模块}-spring-boot-starter
  2. 创建自动配置类,使用 @AutoConfiguration + 条件注解
  3. 创建配置属性类,使用 @ConfigurationProperties(prefix = "...")
  4. META-INF/spring.factories(或 2.7+ 的 .imports 文件)中注册自动配置类
  5. 可选的 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,谁生效?

→ 这取决于 @AutoConfigurationbefore/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 层

九、本章总结

核心要点回顾

  1. SpringBoot ≠ 替代 Spring:它是 Spring 之上的脚手架,底层仍是 IoC/AOP
  2. 三大核心设计:约定优于配置(默认即最佳)、起步依赖(版本统一管理)、自动配置(classpath 驱动装配)
  3. @SpringBootApplication = @Configuration + @EnableAutoConfiguration + @ComponentScan
  4. 自动配置原理AutoConfigurationImportSelector → 读 spring.factories → 条件注解筛选 → 注册 Bean
  5. 条件注解是自动配置的灵魂@ConditionalOnClass / @ConditionalOnMissingBean 实现了"有则用默认,无则用你的"
  6. 自定义 Starter:自动配置类 + 配置属性类 + spring.factories 注册,三件套

面试 checklist

  • 能说清 SpringBoot 三大核心设计
  • 能拆解 @SpringBootApplication 的三层结构
  • 能画出自动配置的完整链路图(spring.factories → 条件筛选 → Bean 注册)
  • 能解释 @ConditionalOnMissingBean 的退让机制
  • 能从零写一个自定义 Starter
  • 能回答 DataSource 自动配置的源码级别问题

下一步学习建议

  1. 动手实践:基于 EduLearn 课程管理模块,扩展自定义缓存 Starter(加 Redis 版本)
  2. 深入源码 :阅读 AutoConfigurationImportSelectorConfigurationClassPostProcessor 的完整源码
  3. 扩展知识:了解 SpringBoot 3.x 的 AOT 编译和 GraalVM 原生镜像支持

下期预告:第 3 期《Spring Web MVC 深度解析》,将深入 DispatcherServlet 的请求处理流程、拦截器与过滤器的区别、RESTful API 设计最佳实践。


上期内容【Spring】面试突击系列(一):IoC 与 DI 深度解析

相关推荐
Full Stack Developme1 小时前
Spring AOP 与 AspectJ
java·后端·spring
快乐的木子李1 小时前
最新版Maven免安装配置教程
java·maven
wuminyu2 小时前
Java锁机制之Java对象重量级锁源码剖析
java·linux·c语言·jvm·c++
艾利克斯冰3 小时前
Java设计模式-创建型设计模式
java
心之伊始3 小时前
MySQL EXPLAIN 执行计划实战:从 type、Extra 到慢 SQL 定位与优化
java·架构·源码分析·csdn
Java_2017_csdn3 小时前
ComplexKeysShardingAlgorithm 小结
java·大数据·算法
海梨花3 小时前
快手面试高频算法题
java·算法·面试
云烟成雨TD3 小时前
Spring AI 1.x 系列【37】RAG 知识库平台案例:知识库管理
java·人工智能·spring
KANGBboy3 小时前
java知识四(面向对象编程)
android·java·开发语言