目录
- 一、自定义Starter
- [(一)、 自定义 Starter 的标准步骤](#(一)、 自定义 Starter 的标准步骤)
- [1. 创建工程与命名规范](#1. 创建工程与命名规范)
- [2. 编写自动配置类](#2. 编写自动配置类)
- [3. 定义配置属性类(@ConfigurationProperties)](#3. 定义配置属性类(@ConfigurationProperties))
- [4. 注册自动配置类(SPI 机制)](#4. 注册自动配置类(SPI 机制))
- [5. 打包与安装](#5. 打包与安装)
- [6. 使用方引入](#6. 使用方引入)
- [(二)、 核心要点总结](#(二)、 核心要点总结)
- [(三)、 避坑指南与最佳实践](#(三)、 避坑指南与最佳实践)
- [(四)、 架构演进视野](#(四)、 架构演进视野)
- [(一)、 自定义 Starter 的标准步骤](#(一)、 自定义 Starter 的标准步骤)
- 二、普通jar和starter区别
- (一)、核心差异对照
- (二)、用场景拆开看更直观
- [1. 普通 jar 的典型形态](#1. 普通 jar 的典型形态)
- [2. Starter 的典型形态](#2. Starter 的典型形态)
- (三)、结构上也有分工
- [(四)、什么时候打普通 jar,什么时候写 starter?](#(四)、什么时候打普通 jar,什么时候写 starter?)
- (五)、最容易混的坑
- 三、项目常用的自定义starter的场景有哪些
- [(一)、 中间件与基础设施集成(最常见)](#(一)、 中间件与基础设施集成(最常见))
- [(二)、 企业通用业务能力下沉](#(二)、 企业通用业务能力下沉)
- [(三)、 第三方开放平台 SDK 封装](#(三)、 第三方开放平台 SDK 封装)
- [(四)、 运维与监控增强](#(四)、 运维与监控增强)
- [(五)、 内部框架与规范约束](#(五)、 内部框架与规范约束)
- [(六)、 开发辅助与测试工具](#(六)、 开发辅助与测试工具)
- (七)、场景适用性判断表
- [(八)、视野拓展:Starter 与 Library 的界限](#(八)、视野拓展:Starter 与 Library 的界限)
一、自定义Starter
Spring Boot 自定义 Starter 的核心在于封装自动配置逻辑 + 定义依赖入口。其本质是遵循 Spring Boot 的 SPI 规范,将组件的配置类、条件注解和依赖管理打包,让使用者仅需引入一个依赖即可获得开箱即用的功能。
以下是详细步骤及核心要点总结。
(一)、 自定义 Starter 的标准步骤
1. 创建工程与命名规范
建议创建两个模块(或一个模块):
- 自动配置模块 :
xxx-spring-boot-autoconfigure(核心逻辑) - Starter 模块 :
xxx-spring-boot-starter(空壳,仅做依赖聚合)
命名规范(非常重要):
- 官方 Starter :
spring-boot-starter-{module}(如spring-boot-starter-web) - 第三方 Starter :
{module}-spring-boot-starter(如mybatis-spring-boot-starter)
2. 编写自动配置类
这是 Starter 的大脑,使用 @Configuration 配合条件注解。
java
// 示例:一个简单的短信服务自动配置
@Configuration
@ConditionalOnClass(SmsService.class) // 类路径存在该类时才生效
@EnableConfigurationProperties(SmsProperties.class) // 绑定配置属性
@ConditionalOnProperty(prefix = "app.sms", name = "enabled", havingValue = "true", matchIfMissing = true)
public class SmsAutoConfiguration {
private final SmsProperties properties;
public SmsAutoConfiguration(SmsProperties properties) {
this.properties = properties;
}
@Bean
@ConditionalOnMissingBean // 允许用户自定义覆盖
public SmsService smsService() {
return new SmsService(properties.getAccessKey(), properties.getSecretKey());
}
}
3. 定义配置属性类(@ConfigurationProperties)
用于映射 application.yml 中的配置。
java
@Data
@Component
@ConfigurationProperties(prefix = "app.sms")
public class SmsProperties {
private String accessKey;
private String secretKey;
private boolean enabled = true;
}
4. 注册自动配置类(SPI 机制)
这是让 Spring Boot 发现配置的关键一步。根据 Spring Boot 版本选择文件:
-
Spring Boot 2.7+ / 3.x(推荐) :
在
resources/META-INF/spring/下创建文件:org.springframework.boot.autoconfigure.AutoConfiguration.imports内容为自动配置类的全限定名:
textcom.example.autoconfigure.SmsAutoConfiguration -
Spring Boot 2.6 及以下(旧版) :
在
resources/META-INF/下创建spring.factories:propertiesorg.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.example.autoconfigure.SmsAutoConfiguration
5. 打包与安装
执行 mvn clean install,将 Starter 安装到本地仓库或私服。
6. 使用方引入
在其他项目中引入依赖即可:
xml
<dependency>
<groupId>com.example</groupId>
<artifactId>sms-spring-boot-starter</artifactId>
<version>1.0.0</version>
</dependency>
并在 application.yml 中配置:
yaml
app:
sms:
access-key: abc
secret-key: 123
(二)、 核心要点总结
| 核心点 | 说明 |
|---|---|
| SPI 注册 | 必须通过 AutoConfiguration.imports 或 spring.factories 注册,否则配置类不会被扫描。 |
| 条件注解 | 使用 @ConditionalOnClass、@ConditionalOnMissingBean 等,确保按需加载且不干扰用户自定义 Bean。 |
| 配置属性绑定 | 使用 @ConfigurationProperties 替代 @Value,提供类型安全的配置绑定。 |
| 用户优先原则 | 自动配置类加载顺序靠后,确保用户自定义的 @Bean 能通过 @ConditionalOnMissingBean 覆盖默认配置。 |
| 依赖隔离 | Starter 模块本身不应包含业务逻辑,仅声明依赖;逻辑应放在 autoconfigure 模块中。 |
(三)、 避坑指南与最佳实践
-
不要使用
@ComponentScan:自动配置类应该通过 SPI 机制加载,而不是包扫描。使用
@ComponentScan会导致配置在非预期的环境下被加载,破坏隔离性。 -
注意配置类的加载顺序 :
如果配置类之间有依赖关系(例如 A 配置需要 B 配置先存在),使用
@AutoConfigureAfter或@AutoConfigureBefore明确指定顺序。 -
区分 Starter 与 Library:
- Library :提供功能代码(如
SmsService)。 - Starter:提供自动配置和依赖管理,让用户"零配置"使用 Library。
- Library :提供功能代码(如
-
调试技巧 :
在使用方启动时添加
--debug参数,查看 Conditions Report ,确认你的自动配置类是Positive matches(已加载)还是Negative matches(未加载及原因)。
(四)、 架构演进视野
随着云原生和 GraalVM 的普及,Spring Boot 3.x 对 Starter 提出了更高要求:
- AOT 处理:确保自动配置类支持提前编译(AOT),避免在运行时使用反射。
- Imports 文件优势 :相比
spring.factories,新的AutoConfiguration.imports文件在 AOT 阶段能被静态分析,显著提升 Native Image 的构建速度和兼容性。
二、普通jar和starter区别
普通 jar 是"代码 + 依赖"的打包;Spring Boot starter 是"自动配置 + 依赖管理 + SPI 注册"的完整集成单元。 前者扔进 classpath 不会自己干任何事,后者引入就能跑。
(一)、核心差异对照
| 维度 | 普通 jar | Spring Boot starter |
|---|---|---|
| 本质 | 代码 + 第三方依赖的封装 | 自动配置模块 + 依赖聚合入口 |
| 引入后行为 | 啥也不做,得用户自己 @ComponentScan 或 @Bean 配 |
借助 @EnableAutoConfiguration + SPI 自动注册 Bean |
| 配置方式 | 用户手动写 @Configuration 或 XML 接 |
application.yml 配几个属性就行,@ConfigurationProperties 绑定 |
| 条件装配 | 无,类在就加载 | @ConditionalOnClass / @ConditionalOnMissingBean 等全套,按需装 |
| 用户覆盖 | 得自己改代码或继承重写 | 用户自己 @Bean 就能替掉默认(starter 用 @ConditionalOnMissingBean 让路) |
| 依赖管理 | 传递依赖裸曝,版本冲突自理 | 通常配 spring-boot-dependencies BOM,版本统一,引入方不用写 <version> |
| 命名规范 | 随意 | 官方 spring-boot-starter-* / 第三方 *-spring-boot-starter |
| SPI 注册 | 无要求 | META-INF/spring/*.imports(或老 spring.factories)必须挂类名 |
(二)、用场景拆开看更直观
1. 普通 jar 的典型形态
比如你自己封了个 common-utils.jar,里面放了些加密工具、@Component 打的工具 Bean、几个常量类。别人 mvn:import 进来后:
- 如果对方也是 Spring Boot,且包路径能被
@SpringBootApplication的扫包范围覆盖到 → Bean 能注册上 - 如果扫不到 → 得对方手动
@ComponentScan("com.yourcompany.common")或写@Import - 如果对方不是 Spring 项目(纯 Java / 其他框架)→ 只能当普通工具类调,跟 Spring 没关系
普通 jar 不感知 Spring Boot 的存在,它只是恰好能被 Spring 扫到而已。
2. Starter 的典型形态
比如 mybatis-spring-boot-starter,引入后:
spring.factories/AutoConfiguration.imports里声明了MybatisAutoConfiguration- Spring Boot 启动时被
AutoConfigurationImportSelector扫到 - 类路径有
SqlSessionFactory+ 用户没自己配 → 自动注册SqlSessionFactoryBean application.yml里mybatis.mapper-locations=classpath:mapper/*.xml被@ConfigurationProperties绑到MybatisProperties- 用户啥也不用写,直接
@Mapper就能用
starter 是"主动把自己接入 Spring Boot 生命周期"的 jar。
(三)、结构上也有分工
官方推荐的 starter 是拆成两个模块的(虽然小项目合成一个也行):
xxx-spring-boot-autoconfigure ← 业务逻辑 + @Configuration + 条件注解 + SPI 注册
xxx-spring-boot-starter ← 空壳,pom 里 dependencyManagement 引 autoconfigure + 相关传递依赖
普通 jar 一般就一个模块,没有这种"配置归配置、聚合归聚合"的拆分。
(四)、什么时候打普通 jar,什么时候写 starter?
- 打普通 jar :工具类库(
commons-lang那种)、SDK 客户端(aliyun-java-sdk)、公司内部common-utils、非 Spring 项目也能用的东西 - 写 starter :要让 Spring Boot 项目"引入即生效"的集成组件------ORM 封装、中间件客户端(Redis/ES/Kafka)、公司内部框架封装、需要读
application.yml自动初始化的场景
💡 一个判断标准:你的 jar 要不要用户在
application.yml里配几个属性就能跑?要 → starter;不要 → 普通 jar。
(五)、最容易混的坑
- "我 jar 里写了
@Configuration也算 starter 吗?" ------ 不算。没走 SPI 注册、没条件注解、没@ConfigurationProperties,只是能被扫到的普通配置类,换个项目扫不到包就废了。 - "starter 必须拆 autoconfigure + starter 两个模块?" ------ 不是必须,小项目合成一个也能跑,但官方推荐拆,原因是 autoconfigure 可以被其他 starter 复用(比如
spring-boot-starter-data-redis和spring-boot-starter-data-mongo各自独立,但都依赖spring-data-commons那套)。 - 普通 jar 也能被
@ComponentScan扫到啊,跟 starter 差在哪? ------ 差在"约定"。starter 是 Spring Boot 生态的标准契约:引入即装配 + 配置属性前缀 + 条件让路 + 可被--debug的 Conditions Report 看到。普通 jar 扫到了算运气,没扫到也没人觉得"它应该生效"。
三、项目常用的自定义starter的场景有哪些
Spring Boot 自定义 Starter 主要用于解决通用能力的复用 与基础设施的标准化接入。凡是需要在多个微服务中重复配置、且具备"引入即生效"特性的组件,都是自定义 Starter 的最佳应用场景。
以下是企业级开发中常见的六大类场景及其核心价值:
(一)、 中间件与基础设施集成(最常见)
这是 Starter 诞生的初衷,旨在屏蔽底层技术的复杂性,提供统一的接入方式。
| 场景 | 解决的问题 | 核心实现逻辑 |
|---|---|---|
| 数据库/ORM 封装 | 统一数据源、连接池(HikariCP)、MyBatis/JPA 配置,避免每个服务重复写配置。 | 自动配置 DataSource、SqlSessionFactory,并绑定 spring.datasource.* 属性。 |
| 消息队列 (MQ) | 统一管理 Kafka/RocketMQ 的生产者、消费者模板,处理序列化与重试机制。 | 自动配置 KafkaTemplate 或 DefaultMQProducer,并根据配置自动注册监听器容器。 |
| 缓存 (Redis) | 统一 Redis 序列化方式(如 JSON)、连接池配置及分布式锁工具类注入。 | 自动配置 RedisTemplate 和 StringRedisTemplate,并设置默认的 Key/Value 序列化器。 |
| 搜索引擎 (ES) | 封装 ES 高级客户端,处理连接认证与超时配置。 | 自动配置 RestHighLevelClient 或 ElasticsearchClient。 |
(二)、 企业通用业务能力下沉
将跨部门、跨项目的通用业务模块抽象为 Starter,实现业务逻辑的高度复用。
| 场景 | 解决的问题 | 核心实现逻辑 |
|---|---|---|
| 认证与鉴权 | 统一 OAuth2/JWT 校验逻辑、权限拦截器,避免每个服务重复写 Security 配置。 | 自动配置 SecurityFilterChain,注册全局过滤器,并通过 @ConditionalOnProperty 控制开关。 |
| 日志与审计 | 统一操作日志记录、日志脱敏、TraceId 透传。 | 自动注册 AOP 切面拦截 Controller 或 Service,自动配置 MDC 过滤器。 |
| 多租户隔离 | 统一数据源切换、ThreadLocal 上下文管理。 | 自动配置 TenantInterceptor 和 TenantDataSourceRouter。 |
| 防重与幂等 | 提供统一的接口防重注解和校验逻辑。 | 自动注册拦截器,结合 Redis 自动检查请求唯一 ID。 |
(三)、 第三方开放平台 SDK 封装
将外部供应商(如支付、短信、地图)的 SDK 包装成 Spring Boot 风格,方便调用。
- 场景示例:支付宝/微信支付、阿里云 OSS/SMS、百度地图 API。
- 实现逻辑 :
- 定义
xxxProperties绑定appid、secret等密钥。 - 自动初始化 SDK 的核心 Client(如
OSSClient)。 - 提供
XXXServiceBean,用户直接@Autowired即可调用。
- 定义
(四)、 运维与监控增强
通过 Starter 统一插桩,降低运维接入成本。
| 场景 | 解决的问题 | 核心实现逻辑 |
|---|---|---|
| 监控指标暴露 | 统一注册 JVM、线程池、业务指标到 Prometheus。 | 自动配置 MeterRegistryCustomizer,注册自定义 Collector。 |
| 动态配置刷新 | 对接 Apollo/Nacos,实现 Bean 属性的热更新。 | 自动配置 ConfigService 并监听配置变更事件,结合 @RefreshScope。 |
| 链路追踪 | 统一集成 SkyWalking/Zipkin,自动埋点。 | 自动配置 TracingFilter 和 RestTemplate 拦截器。 |
(五)、 内部框架与规范约束
强制团队遵守特定的开发规范,减少低级错误。
- 场景示例:API 响应体统一包装、全局异常处理、参数校验增强。
- 实现逻辑 :
- 自动注册
RestControllerAdvice统一返回Result<T>格式。 - 自动配置
MethodValidationPostProcessor开启方法级校验。
- 自动注册
(六)、 开发辅助与测试工具
提高开发效率,但不建议在生产环境中启用。
- 场景示例:Swagger/Knife4j 接口文档、Mock 数据生成器。
- 实现逻辑 :通过
@ConditionalOnProperty(name = "knife4j.enable", havingValue = "true")控制仅在开发环境加载。
(七)、场景适用性判断表
在决定是否编写自定义 Starter 时,可以参考下表进行判断:
| 特征 | 适合做 Starter | 不适合做 Starter |
|---|---|---|
| 复用频率 | 高频(超过 3 个服务使用) | 低频或一次性逻辑 |
| 配置复杂度 | 高(需要大量 Bean 初始化) | 低(仅几个工具类) |
| 侵入性 | 低(通过自动配置和注解解耦) | 高(需要硬编码依赖具体实现) |
| 环境相关性 | 强(不同环境配置不同) | 弱(纯算法或静态工具) |
(八)、视野拓展:Starter 与 Library 的界限
很多开发者容易混淆 Starter 和 Library 。关键在于 "自动配置" 这一层:
- Library 只提供代码(如
StringUtils、HttpUtils),需要用户自己管理对象的创建和生命周期。 - Starter 是 Library + AutoConfiguration 。它不仅包含代码,还告诉 Spring Boot:"只要你看到我在 Classpath 里,就帮我创建一个
HttpClient并配置好超时时间和拦截器。"
如果你的组件仅仅是几个静态方法,不需要 Spring 管理,那么普通的 Jar 包就足够了;如果需要 Spring 容器帮你管理对象及其依赖关系,才需要 Starter。