前面的 IoC 与 Bean 章节主要处理项目自己声明的对象:组件扫描发现 HealthController 和 HealthService,配置属性扫描创建 HealthProperties。但项目没有手动创建 Tomcat、DispatcherServlet、JSON 消息转换器等对象,它们仍然能够在启动后协作。
这些框架对象主要来自 Spring Boot 自动配置。本章沿着「依赖进入 classpath → 条件配置参与判断 → Bean 注册到容器」这条路径,解释 Starter、@SpringBootApplication、条件注解、默认配置退让和诊断报告。
1. Starter 与自动配置
Starter 和自动配置经常一起出现,但不是同一个概念:
| 名称 | 作用 |
|---|---|
| Starter | 提供一组经过版本协调的依赖 |
| 自动配置 | 根据 classpath、配置属性和现有 Bean 决定是否注册框架对象 |
心跳项目添加 spring-boot-starter-webmvc 后,Maven 把 Spring MVC、JSON 支持和内嵌服务器相关依赖放入 classpath。仅有这些类还不会自动组成应用,自动配置接着检查运行环境,并注册 Web 应用需要的 Bean。
1pom.xml 添加 spring-boot-starter-webmvc2-> Maven 解析并下载一组依赖3-> Spring Boot 在 classpath 中发现 Web MVC 类4-> Web 相关自动配置条件匹配5-> 容器注册 DispatcherServlet、消息转换器等 Bean6-> 内嵌服务器启动并接收请求
Starter 也不等于所有依赖都必须通过它添加。明确知道只需要一个独立库时,可以直接声明该依赖;Spring Boot Starter 的价值在于提供常见能力所需的一组协调版本,并触发相应自动配置条件。
后面加入 JDBC Starter 时会出现同一过程:依赖提供 Spring JDBC 与 HikariCP,spring.datasource.* 提供连接参数,自动配置再创建 DataSource、事务管理器和 JdbcClient 等对象。
2. 入口注解
应用入口使用的 @SpringBootApplication 组合了三个核心注解:
1@SpringBootConfiguration2@EnableAutoConfiguration3@ComponentScan4public @interface SpringBootApplication {}
它们分别承担不同职责:
| 注解 | 职责 |
|---|---|
@SpringBootConfiguration | 标识应用的主要配置类,本身建立在 @Configuration 上 |
@EnableAutoConfiguration | 导入符合当前环境的自动配置候选项 |
@ComponentScan | 从入口类所在包开始发现应用自己的组件 |
因此,「找到项目组件」与「提供框架默认组件」是两条不同路径。HealthService 因为 @Service 和组件扫描成为 Bean;DispatcherServlet 则由 Web 自动配置注册,不要求应用源码中存在同名组件类。
入口类应放在项目根包:
1cn.usehook.spring.Application2├── config3├── controller4└── health
默认组件扫描会覆盖这些子包。如果入口类被放入更深的 cn.usehook.spring.bootstrap,同级的 controller 和 health 就不再位于默认扫描范围内。可以显式配置扫描位置,但清晰的根包布局更容易维护。
3. 条件匹配
自动配置不是无条件创建所有对象。Spring Boot 提供一组条件注解,让配置只在环境满足要求时生效:
| 条件 | 判断内容 |
|---|---|
@ConditionalOnClass | classpath 中是否存在指定类 |
@ConditionalOnMissingClass | classpath 中是否缺少指定类 |
@ConditionalOnBean | 容器中是否已经存在指定 Bean |
@ConditionalOnMissingBean | 容器中是否还没有指定 Bean |
@ConditionalOnProperty | 配置属性是否存在或等于指定值 |
@ConditionalOnWebApplication | 当前是否为指定类型的 Web 应用 |
可以把一个库提供的自动配置简化成下面的形式。它用于理解机制,不需要放进当前心跳项目:
01@AutoConfiguration02@ConditionalOnClass(HealthClient.class)03@ConditionalOnProperty(04prefix = "health.client",05name = "enabled",06havingValue = "true",07matchIfMissing = true08)09public class HealthClientAutoConfiguration {1011@Bean12@ConditionalOnMissingBean13HealthClient healthClient() {14return new HealthClient();15}16}
只有 HealthClient 类存在并且配置没有关闭该功能时,这个配置类才参与注册;容器已经有用户提供的 HealthClient 时,默认 Bean 又会退让。
条件检查的是应用启动时已经收集到的环境与 Bean 定义。它不是每次 HTTP 请求都会重新执行的业务判断,也不应该用来代替方法中的业务分支。
4. 配置候选项
Spring Boot 不会通过普通组件扫描寻找依赖库中的所有自动配置。提供自动配置的库会在下面的文件中列出候选类:
1com.example.health.HealthClientAutoConfiguration
@EnableAutoConfiguration 读取这些候选项,再逐个评估条件。这样应用只需引入库,不必知道并手动导入每个内部配置类。
当前项目自己的 @Configuration、@Service 和 @RestController 仍然由组件扫描发现。两条路径最终都会产生 Bean 定义,但来源不同:
1应用源码2-> @ComponentScan3-> @Component、@Service、@Configuration45依赖库6-> @EnableAutoConfiguration7-> AutoConfiguration.imports 中的候选配置89两者共同注册到 ApplicationContext
如果以后开发供多个项目复用的 Starter,才需要创建 @AutoConfiguration 和 imports 文件。当前单体应用中的普通配置不要仅为了模仿框架而改造成自动配置。
5. 默认配置与自定义
自动配置的目标是提供合理默认值,同时允许应用覆盖。最常见的退让条件是 @ConditionalOnMissingBean:
1容器没有目标类型 Bean2-> 自动配置创建默认 Bean34容器已经有应用声明的目标类型 Bean5-> 默认自动配置退让
这不表示任意同名方法都能覆盖自动配置。条件通常检查 Bean 类型、名称或特定注解,具体规则要查看对应自动配置类和配置属性文档。
Web MVC 中,应用实现 WebMvcConfigurer 可以增加 CORS、拦截器和格式转换等配置,同时保留 Spring Boot 的 MVC 默认设置。直接使用 @EnableWebMvc 则表示应用要更全面地接管 MVC 配置,会让部分 Boot 默认行为退出,不应该把二者当成等价写法。
后面的数据库连接章节只提供 spring.datasource 属性,让 Boot 创建池化 DataSource。只有项目确实需要特殊数据源构造逻辑时,才自己声明 DataSource Bean;一旦自定义对象满足退让条件,连接池参数绑定、监控和事务配置也要重新确认是否仍按预期生效。
自动配置减少的是通用基础设施样板代码,不会替应用决定业务对象如何分层,也不会自动创建任意没有注解和配置来源的普通类。
6. 条件报告
遇到预期 Bean 没有创建或意外使用默认实现时,可以先让应用输出条件评估报告:
1./mvnw spring-boot:run -Dspring-boot.run.arguments=--debug
也可以在 JAR 启动时传入参数:
1java -jar target/spring-0.0.1-SNAPSHOT.jar --debug
报告会区分匹配与未匹配的自动配置,并说明具体条件。排查时可以按下面顺序定位:
- 确认需要的 Starter 或依赖确实位于运行时 classpath;
- 确认相关配置键名称、Profile 和最终值;
- 检查是否已有 Bean 让默认配置退让;
- 查看对应自动配置为什么匹配或未匹配;
- 再确认应用是否排除了某个自动配置类。
--debug 会产生较多启动日志,适合本地诊断,不必作为生产环境的永久默认值。后面的 Actuator 还会提供条件和 Bean 端点,但端点是否对外暴露需要单独控制。
到这里,项目自己的组件与 Spring Boot 提供的基础设施已经能放进同一张容器图中理解。下一篇继续学习 Spring AOP 与代理,解释框架怎样在不改写业务方法调用方式的前提下,在方法前后加入事务、监控等横切行为。