楼层: 首页/ 软件技术/ Spring Boot 3.5/ 自动配置原理:@SpringBootApplication 拆开看
3

自动配置原理:@SpringBootApplication 拆开看

Auto-Configuration Magic

为什么加个 spring-boot-starter-web,Tomcat 就自动起来了?为什么连数据库都不配就能连 H2?这一节把这个魔法拆开给你看——它一点都不神秘,就是一堆"如果类路径有 X,就自动配 Y"的条件判断。

论@SpringBootApplication 的三件套

① @Configuration:告诉 Spring 这是一个配置类,里面可以写 @Bean 方法。

② @ComponentScan:扫描当前包及子包,把 @Component/@Service/@RestController 这些注解的类收编为 Bean。

③ @EnableAutoConfiguration:核心中的核心。它会去读所有 jar 包里 META-INF/spring/...AutoConfiguration.imports 文件,把里面列的自动配置类加载进来。

自动配置类长什么样(以 DispatcherServlet 为例)

package org.springframework.boot.autoconfigure.web.servlet; // 这是 Spring Boot 源码里的一个自动配置类,简化版 @AutoConfiguration // 条件:类路径下存在 DispatcherServlet 才生效 @ConditionalOnClass(DispatcherServlet.class) // 条件:容器里还没有这个 Bean 才配置(你自己配了就用你的) @ConditionalOnMissingBean public class DispatcherServletAutoConfiguration { @Bean public DispatcherServlet dispatcherServlet() { return new DispatcherServlet(); // 自动给你 new 一个 } }
怎么看"为什么这个 Bean 被自动配了"

启动时加参数 --debug,Spring Boot 会打印一份条件评估报告:哪些自动配置类匹配了(positive matches)、哪些没匹配(negative matches)。排查"为什么我的 Bean 没生效"时,先看这份报告。

自定义 Starter:把自动配置封装成自己的轮子

当你团队里有一套通用配置(比如统一的日志格式、统一的异常处理、统一的监控上报),别让每个项目复制粘贴——把它做成一个 starter,别的项目引入依赖就自动生效。结构很简单:一个自动配置类 + 一个 imports 文件。

@ConditionalOnMissingBean:你配了就用你的

这是 Spring Boot 自动配置的核心套路:如果你自己没定义某个 Bean,它就给你配一个默认的;如果你自己定义了,它就闭嘴用你的。这就是"覆盖默认配置"的原理。

// Spring Boot 源码里的套路: // 你没自己配 DataSource,就给你一个内嵌 H2; // 你自己配了,就用你的。 @Bean @ConditionalOnMissingBean public DataSource dataSource() { return new EmbeddedDatabaseBuilder() .setType(EmbeddedDatabaseType.H2) .build(); }

自定义 starter 三步

// 1. 写自动配置类 @AutoConfiguration @ConditionalOnClass(MySdkClient.class) @EnableConfigurationProperties(MySdkProperties.class) public class MySdkAutoConfiguration { @Bean @ConditionalOnMissingBean public MySdkClient mySdkClient(MySdkProperties props) { return new MySdkClient(props.getEndpoint()); } } // 2. 在 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports // 文件里写一行: com.example.autoconfigure.MySdkAutoConfiguration // 3. 别的项目引你的 starter: <dependency> <groupId>com.example</groupId> <artifactId>my-sdk-spring-boot-starter</artifactId> <version>1.0.0</version> </dependency> // 不用任何 @Import,MySdkClient 自动出现在容器里

@Conditional 全家桶:四个最常用的条件注解

自动配置的精髓全在 @Conditional 家族。下面四个是高频中的高频,看懂它们,你就看懂了 Boot 一半的魔法。

条件注解大白话:满足什么条件才装配这个 Bean
@ConditionalOnClass(X.class)类路径下能找到类 X,才装配。加了 starter 才有这个类,没加就跳过。
@ConditionalOnMissingBean容器里还没有同类型 Bean,才装默认的。你自己配了就用你的。
@ConditionalOnProperty(prefix="x", name="y", havingValue="true")配置文件里 x.y=true 才装配。常用于开关某个功能。
@ConditionalOnWebApplication这是个 Web 应用(有 Servlet 环境)才装 MVC 相关 Bean。

实战:写一个带开关的自定义 HTTP 客户端 starter

@AutoConfiguration // ① 类路径下有 OkHttpClient 这个类才生效(用户引了 okhttp 依赖才装) @ConditionalOnClass(OkHttpClient.class) // ② 必须是 Web 应用才装(命令行工具项目不需要) @ConditionalOnWebApplication public class HttpAutoConfig { @Bean // ③ 配置 http.client.enabled=true 才装;默认 false 不装 @ConditionalOnProperty(prefix = "http.client", name = "enabled", havingValue = "true") // ④ 用户自己没定义 MyHttpClient 时,才给默认实现;用户定义了就用用户的 @ConditionalOnMissingBean public MyHttpClient myHttpClient() { return new MyHttpClient(new OkHttpClient()); } }
application.yml 控制开关: http: client: enabled: true # 改成 false,MyHttpClient 就不会被装配 # 启动加 --debug 看条件评估报告: # Positive matches: HttpAutoConfig matched # @ConditionalOnClass ... (okhttp 存在) # @ConditionalOnProperty ... (http.client.enabled=true) # Negative matches: 你引了依赖但开关没开时,这里会告诉你为什么没装。

导入自动配置类的幕后:AutoConfigurationImportSelector

上面说"@EnableAutoConfiguration 去读 imports 文件",背后干这个活的是一个叫 AutoConfigurationImportSelector 的类。它实现了 Spring 的 ImportSelector 接口,把一堆配置类的全限定名告诉容器。知道这条链路,面试就能答出完整原理。

Boot 版本自动配置类登记在哪
Boot 2.x所有 jar 的 META-INF/spring.factories 文件里,用一个 org.springframework.boot.autoconfigure.EnableAutoConfiguration key,把所有自动配置类名写在一行逗号分隔。缺点:启动要解析整个文件,慢。
Boot 3.x改成 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports,一行一个配置类,加载更快、更清晰。新项目按这个写。
# Boot 3.x:resources/META-INF/spring/...AutoConfiguration.imports 的内容 # 一行一个自动配置类全限定名 com.example.http.HttpAutoConfig com.example.redis.RedisAutoConfig

论一句话串起整条链路

@SpringBootApplication → @EnableAutoConfiguration → 通过 AutoConfigurationImportSelector 读取所有 jar 的 AutoConfiguration.imports(Boot 2.x 是 spring.factories)→ 加载这些配置类 → 每个类上的 @ConditionalOnXxx 条件不满足就跳过 → 满足的才注册 Bean。这就是"约定优于配置"的全部秘密。

本章面试题 · 第 3 章(自动配置原理)

1.(必考题)说说 Spring Boot 自动配置的完整原理。

查看答案

答案:三层:① @SpringBootApplication 包含 @EnableAutoConfiguration;② @EnableAutoConfiguration 去读所有 jar 包 META-INF/spring/...AutoConfiguration.imports 文件,加载列出的自动配置类;③ 每个自动配置类都带 @ConditionalOnXxx,满足条件才注册 Bean。解析:这是标准答案,能讲出"imports 文件 + 条件注解"两个关键词,面试官就知道你真懂。

2.(理解题)@ConditionalOnMissingBean 为什么能实现"你配了就用你的"?

查看答案

答案:自动配置类是在你自己写的 @Bean 之后处理的。它注册 Bean 之前先检查容器里有没有同类型 Bean——有就跳过,没有才注册默认的。解析:这就是为什么"覆盖 Boot 默认配置只要自己定义一个同类型 Bean"。

3.(排错题)我写了个 @Bean 想覆盖 Boot 的默认 DataSource,但没生效。可能什么原因?

查看答案

答案:常见原因:① 你的 @Bean 所在的配置类没被扫描到;② 自动配置优先级问题,顺序不对;③ Bean 类型对不上。解析:加 --debug 看条件评估报告,确认你的 Bean 和默认 Bean 是不是同类型、你的配置类有没有进容器。