Spring Boot Starter机制与自定义详解
定位:第 03 篇,讲透 Starter 的双模块结构、命名约定、依赖版本管理,以及从零自定义一个 Starter 的完整流程
适用版本:Spring Boot 3.x(JDK 17+)
目录
- [一、Starter 的结构](#一、Starter 的结构)
- 二、命名约定
- 三、依赖管理
- [四、自定义 Starter 流程](#四、自定义 Starter 流程)
- 五、配置提示
- 六、总结
- 七、常见高频面试题
一、Starter 的结构
一个标准 Starter 由两个模块组成(可拆开也可合并):
xxx-spring-boot-starter ← 依赖聚合(空壳 + pom)
└── 依赖 xxx-spring-boot-autoconfigure ← 真正的逻辑
├── XxxAutoConfiguration.java 自动配置类
├── XxxProperties.java 配置属性类
└── META-INF/spring/...AutoConfiguration.imports 清单
| 模块 | 职责 |
|---|---|
xxx-spring-boot-starter |
只声明依赖:聚合 autoconfigure 模块 + 该功能需要的第三方库 |
xxx-spring-boot-autoconfigure |
自动配置类、属性类、清单文件 |
拆分的好处:用户只想要依赖聚合不想要自动配置(或反之)时可选择性引入;简单场景也可合成一个模块。
本质:Starter = "依赖清单 + 自动配置",把"引入依赖 + 写配置"两步压缩成"引一个坐标"。
二、命名约定
| 来源 | 格式 | 例 |
|---|---|---|
| 官方 | spring-boot-starter-{功能} |
spring-boot-starter-web |
| 第三方/自定义 | {功能}-spring-boot-starter |
mybatis-spring-boot-starter |
官方保留 spring-boot-starter- 前缀给自身,自定义不要用该前缀开头(社区约定,便于区分来源)。
三、依赖管理
3.1 版本仲裁的两种方式
方式一:继承
<parent>
<artifactId>spring-boot-starter-parent</artifactId>
</parent>
→ 获得:依赖版本仲裁 + 插件默认配置 + 资源过滤
方式二:BOM import(不继承时用)
<dependencyManagement>
<dependency>
<artifactId>spring-boot-dependencies</artifactId> ← 纯 BOM
<type>pom</type><scope>import</scope>
</dependency>
</dependencyManagement>
→ 只获得版本仲裁,不带 parent 的插件/资源默认
3.2 覆盖仲裁
BOM 里的版本是"推荐且经过联合测试"的默认;确需不同版本时,显式声明版本即可覆盖(parent 方式下改 properties 里的版本号)。
原则:默认跟 BOM,升级先改 BOM 版本而不是单点覆盖------单点覆盖容易引入未联合测试的组合。
四、自定义 Starter 流程
以"短信发送客户端"为例,五步:
4.1 定义配置属性类
java
@ConfigurationProperties(prefix = "demo.sms")
public class SmsProperties {
private String accessKey;
private int timeoutMs = 3000; // 默认值
// getter/setter
}
4.2 写自动配置类
java
@AutoConfiguration
@ConditionalOnClass(SmsClient.class) // 有核心库才装配
@EnableConfigurationProperties(SmsProperties.class)
public class SmsAutoConfiguration {
@Bean
@ConditionalOnMissingBean // 用户自定义则让位
public SmsClient smsClient(SmsProperties props) {
return new SmsClient(props.getAccessKey(), props.getTimeoutMs());
}
}
4.3 注册清单
# META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
com.demo.sms.SmsAutoConfiguration
4.4 打依赖聚合模块
demo-sms-spring-boot-starter 的 pom 依赖:autoconfigure 模块 + 短信核心库。
4.5 使用方
引入 demo-sms-spring-boot-starter
配置 demo.sms.access-key=xxx
@Autowired SmsClient 直接用
设计纪律:条件要齐(OnClass/OnMissingBean),属性有默认值与校验,提供排除手段;文档写明属性清单------好 Starter 的标准。
五、配置提示
引入注解处理器,构建时生成元数据,让 IDE 提示自定义属性:
依赖:spring-configuration-processor
产物:META-INF/spring-configuration-metadata.json
效果:application.yml 里输 demo.sms. 有属性名/类型/默认值提示
这是自定义 Starter 的"专业度"体现,成本低收益明显。
六、总结
- 结构:Starter = 依赖聚合模块 + 自动配置模块(配置类/属性类/清单);本质是"引一个坐标完成依赖+配置"。
- 命名 :官方
spring-boot-starter-x,第三方x-spring-boot-starter。 - 版本管理:parent 继承或 BOM import 二选一;默认跟 BOM,升级改 BOM 而非单点覆盖。
- 自定义五步:属性类 → 自动配置类(条件齐全)→ imports 清单 → 聚合模块 → 使用方引依赖配属性。
- 体验增强:configuration-processor 生成元数据供 IDE 提示。
七、常见高频面试题
1. Starter 的组成部分是什么?
要点:标准 Starter 含两个模块------依赖聚合模块(xxx-spring-boot-starter,主要是 pom,聚合 autoconfigure 与所需第三方库)和自动配置模块(xxx-spring-boot-autoconfigure,含 @AutoConfiguration 配置类、@ConfigurationProperties 属性类、AutoConfiguration.imports 清单)。简单场景可合并为一个模块。本质:把"引入一组协调依赖 + 自动装配默认 Bean"封装成单个坐标,开箱即用。
2. 官方和第三方 Starter 的命名约定?
要点:官方用 spring-boot-starter-{功能}(如 spring-boot-starter-web),Spring Boot 团队保留该前缀;第三方/自定义用 {功能}-spring-boot-starter(如 mybatis-spring-boot-starter)。约定便于区分来源与归属,自定义不要占用官方前缀。名字即文档:从坐标就能看出它提供什么能力。
3. spring-boot-starter-parent 和 spring-boot-dependencies 的区别?
要点:spring-boot-dependencies 是纯 BOM,只负责依赖版本仲裁;spring-boot-starter-parent 继承自它,额外提供 Maven 插件默认配置、资源过滤、编译参数等构建默认。使用方式:继承 parent 一步到位;不能/不想继承(如公司已有父 POM)时用 dependencyManagement import BOM 只拿版本仲裁。两者都保证依赖版本经过联合测试。
4. 如何自定义一个 Starter?
要点:五步。① @ConfigurationProperties 定义配置属性类(前缀绑定、默认值);② @AutoConfiguration 写自动配置类,配 @ConditionalOnClass/OnProperty/OnMissingBean 条件,@Bean 注册核心对象;③ 把类名写入 META-INF/spring/...AutoConfiguration.imports;④ 建依赖聚合模块引 autoconfigure 与核心库;⑤ 使用方引该 starter、配置属性即自动装配。可用 exclude 排除、自定义同类型 Bean 覆盖。
5. 为什么自定义 Starter 要用 @ConditionalOnMissingBean?
要点:保证"用户优先"------使用方若自定义了同类型 Bean(如定制化的客户端),自动配置检测到已存在即让位,不重复注册也不冲突。这是 Starter 可覆盖性的核心:默认开箱即用,但处处允许定制。不加该条件会导致与用户定义冲突或产生重复 Bean。同理 @ConditionalOnClass 保证缺核心依赖时不乱装。
6. 版本冲突时,BOM 仲裁和显式声明谁赢?
要点:就近原则下显式声明的版本覆盖 BOM 仲裁结果(parent 方式可通过 properties 覆盖 BOM 中的版本属性)。但应谨慎:BOM 的版本组合经过联合测试,单点覆盖可能引入不兼容组合。正确姿势:优先升级 BOM 整体版本获得协调的新版本;确需单点覆盖时充分回归测试。依赖树可用 mvn dependency:tree 核对实际生效版本。
7. spring-configuration-metadata.json 是干什么的?
要点:配置元数据文件,描述可配置属性(名称、类型、默认值、说明)。由 spring-configuration-processor 注解处理器在构建时从 @ConfigurationProperties 类生成。作用:IDE 在 application.yml 中提供属性自动补全、类型校验与文档提示,显著提升使用方体验。自定义 Starter 应引入该处理器,这是专业度的体现。
8. 引入一个 Starter 后,它是如何"自动生效"的?
要点:链路回顾(衔接 BT-02)------Starter 的 autoconfigure 模块带 AutoConfiguration.imports 清单;应用启动时 @EnableAutoConfiguration 的 ImportSelector 聚合所有 jar 的清单,把候选自动配置类注册;各类按 @Conditional 条件(OnClass 满足、属性具备、OnMissingBean 无用户定义)决定是否真正装配 @Bean。所以"引依赖即生效"= 清单被聚合 + 条件满足。
9. 一个自动配置模块能不能不拆 starter 直接给用户用?
要点:技术上可以(合并为一个模块),用户引该模块也能触发自动配置。拆分的价值:依赖聚合与自动配置关注点分离;用户可选择只要依赖不要自动配置(或反向);符合官方生态惯例,便于排除与替换。简单内部库合并即可;面向广泛使用、需要灵活组合的,建议按标准双模块拆分。
10. 设计一个好用的 Starter 有哪些要点?
要点:① 条件齐全:OnClass(依赖门禁)、OnMissingBean(可覆盖)、OnProperty(开关);② 属性友好:前缀清晰、有默认值、可校验(@Validated)、生成配置元数据供 IDE 提示;③ 可退出:支持 exclude 排除与自定义 Bean 覆盖,不强制接管;④ 文档完整:属性清单、默认行为、排除方式;⑤ 无副作用:不在装配期做重操作(建连接等延迟到使用时)。
