# SpringBoot 自动配置深度

用生活化的比喻,让你理解 SpringBoot 的启动流程、自动配置原理和 Starter 开发

前置知识:第04章 Spring 核心原理(IoC、Bean 生命周期、AOP)


阅读指南(初学者必看)

为什么你需要学习 SpringBoot 自动配置深度?

你每天都在用 SpringBoot,但出了问题往往不知道怎么排查。不理解自动配置,就无法:

  • 理解 SpringBoot 启动时到底做了什么
  • 理解 @ConditionalOnXxx 条件注解怎么控制 Bean 的创建
  • 开发自定义 Starter 来封装游戏服务器的通用组件

学完本章,你能回答:

  • SpringBoot 启动分几步?@EnableAutoConfiguration 什么时候生效?
  • 自动配置类是怎么被发现的?怎么被条件过滤的?
  • 如何开发一个自定义的 game-spring-boot-starter?
  • spring.factories 和 AutoConfiguration.imports 有什么区别?

本文结构

第一部分:启动流程(SpringApplication.run 做了什么)
第二部分:自动配置原理(@EnableAutoConfiguration 全流程)
第三部分:自定义 Starter(开发 game-spring-boot-starter)

一、启动流程

生活类比:SpringBoot 启动就像开一家餐厅——先注册公司、装修、招人、采购食材、开业。

// SpringBoot 启动流程
1. new SpringApplication()
   - 推断应用类型(Servlet / Reactive / None)
   - 加载 ApplicationContextInitializer
   - 加载 ApplicationListener
   
2. run()
   - 创建 ApplicationContext
   - 准备环境(Environment)
   - 刷新上下文(@EnableAutoConfiguration 生效)
   - 执行自动配置
   - 启动完成

启动流程详细拆解

SpringApplication.run()
│
├── 1. 创建 SpringApplication
│   ├── 推断 Web 应用类型
│   ├── 加载 ApplicationContextInitializer(从 spring.factories)
│   └── 加载 ApplicationListener(从 spring.factories)
│
├── 2. 准备 Environment
│   ├── 创建 Environment
│   ├── 加载配置文件(application.yml / application.properties)
│   └── 处理 @PropertySource
│
├── 3. 创建 ApplicationContext
│   ├── Servlet → AnnotationConfigServletWebServerApplicationContext
│   └── Reactive → AnnotationConfigReactiveWebServerApplicationContext
│
├── 4. 准备上下文
│   ├── 设置 Environment
│   ├── 执行 ApplicationContextInitializer
│   └── 注册启动参数 Bean
│
├── 5. 刷新上下文 ⭐ 核心
│   ├── 执行 Spring 的 refresh()(见第04章)
│   └── @EnableAutoConfiguration 在这里生效
│
├── 6. 后处理
│   ├── 执行所有 Runner(CommandLineRunner / ApplicationRunner)
│   └── 发布 ApplicationReadyEvent
│
└── 7. 启动完成!

二、自动配置原理

// @EnableAutoConfiguration 核心流程
1. 扫描 META-INF/spring.factories(Spring Boot 2.x)
   或 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports(3.x)
2. 加载所有 AutoConfiguration 类
3. 根据 @ConditionalOnXxx 条件注解过滤
4. 满足条件的配置类生效

@EnableAutoConfiguration 详解

生活类比:自动配置就像自动售货机——你投币(引入依赖),它自动出货(配置 Bean)。

@SpringBootApplication = 
    @SpringBootConfiguration      // 本质是 @Configuration
  + @EnableAutoConfiguration      // 自动配置的核心
  + @ComponentScan                // 组件扫描
@EnableAutoConfiguration 执行流程:

1. @Import(AutoConfigurationImportSelector.class)
   │
2. AutoConfigurationImportSelector.selectImports()
   │
3. 从 spring.factories 读取所有 AutoConfiguration 类
   │
4. 过滤:去掉 @Conditional 条件不满足的
   │
5. 排序:按 @AutoConfigureOrder 排序
   │
6. 排除:去掉 @SpringBootApplication.exclude 指定的
   │
7. 返回最终的配置类列表
   │
8. Spring 容器加载这些配置类,注册 Bean

条件注解大全

注解 条件 生活类比 游戏场景
@ConditionalOnClass classpath 中有指定类 厨房有烤箱才能做蛋糕 引入 Netty 依赖才配置 Netty
@ConditionalOnMissingBean 容器中没有指定 Bean 你自己带了碗就不给你发 用户自定义 Bean 优先
@ConditionalOnProperty 配置文件中有指定属性 菜单上写了才有这道菜 game.server.enabled=true
@ConditionalOnWebApplication 是 Web 应用 是餐厅才能点菜 游戏网关是 Web 应用
@ConditionalOnExpression SpEL 表达式为 true 满足条件才上菜 动态条件

自动配置的"约定大于配置"

SpringBoot 约定:
1. 引入 spring-boot-starter-web → 自动配置 Tomcat + SpringMVC
2. 引入 spring-boot-starter-data-redis → 自动配置 Redis 连接
3. 引入 mybatis-spring-boot-starter → 自动配置 SqlSessionFactory
4. application.yml 中的配置会覆盖默认值

这就是"约定大于配置"——你只需要引入依赖,SpringBoot 自动帮你配置好

三、自定义 Starter

// 自定义 Starter 的命名规范
// 官方:spring-boot-starter-xxx
// 第三方:xxx-spring-boot-starter

// 我们的示例:game-spring-boot-starter
game-spring-boot-starter/
├── pom.xml
├── src/main/java/
│   └── com/game/autoconfigure/
│       ├── GameAutoConfiguration.java  // 自动配置类
│       ├── GameProperties.java         // 配置属性
│       └── GameService.java            // 核心服务
└── src/main/resources/
    └── META-INF/
        └── spring.factories            // 注册自动配置

GameProperties.java

@ConfigurationProperties(prefix = "game.server")
public class GameProperties {
    private boolean enabled = true;
    private int maxRooms = 100;
    private int maxPlayersPerRoom = 10;
    private int tickRate = 20;  // 每秒逻辑帧数
    
    // getters and setters
}

GameAutoConfiguration.java

@AutoConfiguration
@ConditionalOnClass(GameService.class)
@ConditionalOnProperty(prefix = "game.server", name = "enabled", havingValue = "true", matchIfMissing = true)
@EnableConfigurationProperties(GameProperties.class)
public class GameAutoConfiguration {
    
    @Bean
    @ConditionalOnMissingBean
    public GameService gameService(GameProperties properties) {
        return new GameService(
            properties.getMaxRooms(),
            properties.getMaxPlayersPerRoom(),
            properties.getTickRate()
        );
    }
}

spring.factories

# Spring Boot 2.x
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.game.autoconfigure.GameAutoConfiguration

AutoConfiguration.imports(Spring Boot 3.x)

# Spring Boot 3.x
# 文件路径:META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
com.game.autoconfigure.GameAutoConfiguration

使用 Starter

# application.yml
game:
  server:
    enabled: true
    max-rooms: 200
    max-players-per-room: 8
    tick-rate: 30
@Service
public class BattleService {
    @Autowired
    private GameService gameService;  // 自动注入!
}

自问自答

Q:spring.factories 和 AutoConfiguration.imports 有什么区别? A:spring.factories 是 Spring Boot 2.x 的方式,一个文件注册所有类型的 SPI。AutoConfiguration.imports 是 Spring Boot 3.x 的新方式,专门用于自动配置,文件路径不同,更清晰。Spring Boot 3.x 两者都支持,但推荐新方式。

Q:自动配置的 Bean 和我手动定义的 Bean 冲突了怎么办? A:手动定义的 Bean 优先。自动配置类通常用 @ConditionalOnMissingBean,当容器中已有同名 Bean 时,自动配置不会创建。这就是 SpringBoot 的"用户定义优先"原则。

Q:为什么 Starter 要用 @ConditionalOnMissingBean? A:给用户留出覆盖默认配置的能力。用户可能需要自定义实现(如自定义的 GameService),@ConditionalOnMissingBean 保证用户的 Bean 不会被自动配置覆盖。

Q:如何排查自动配置是否生效? A:1)启动时加 --debug,打印自动配置报告;2)在 application.yml 中设置 debug: true;3)用 Actuator 的 /conditions 端点查看。


实践任务

  • 任务1:用 --debug 启动 SpringBoot 应用,查看自动配置报告(哪些生效,哪些不生效)
  • 任务2:开发一个简单的 game-spring-boot-starter,包含自动配置类和属性类
  • 任务3:在 Starter 中使用 @ConditionalOnProperty 控制功能开关
  • 任务4:同时提供 spring.factories 和 AutoConfiguration.imports 两种注册方式
  • 任务5:写一个自定义的 ApplicationContextInitializer,在容器刷新前打印环境信息

与其他章节的关联

本章内容 关联章节 关联点
启动流程 第04章 Spring 核心原理 SpringBoot 启动 = Spring refresh + 自动配置
自动配置 第11章 游戏服务器架构 游戏组件用 Starter 封装
条件注解 第07章 字节码与动态编程 条件判断依赖类加载和反射
Bean 注册 第04章 Spring 核心原理 BeanDefinition 注册流程
配置属性 第15章 云原生与容器化 配置外部化是云原生的核心

上一章:04-Spring核心原理 | 下一章:06-数据库深度进阶