springboot 自定义 starter 及使用场景

目录

  • 一、自定义Starter
    • [(一)、 自定义 Starter 的标准步骤](#(一)、 自定义 Starter 的标准步骤)
      • [1. 创建工程与命名规范](#1. 创建工程与命名规范)
      • [2. 编写自动配置类](#2. 编写自动配置类)
      • [3. 定义配置属性类(@ConfigurationProperties)](#3. 定义配置属性类(@ConfigurationProperties))
      • [4. 注册自动配置类(SPI 机制)](#4. 注册自动配置类(SPI 机制))
      • [5. 打包与安装](#5. 打包与安装)
      • [6. 使用方引入](#6. 使用方引入)
    • [(二)、 核心要点总结](#(二)、 核心要点总结)
    • [(三)、 避坑指南与最佳实践](#(三)、 避坑指南与最佳实践)
    • [(四)、 架构演进视野](#(四)、 架构演进视野)
  • 二、普通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(空壳,仅做依赖聚合)

命名规范(非常重要):

  • 官方 Starterspring-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

    内容为自动配置类的全限定名:

    text 复制代码
    com.example.autoconfigure.SmsAutoConfiguration
  • Spring Boot 2.6 及以下(旧版)

    resources/META-INF/ 下创建 spring.factories

    properties 复制代码
    org.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.importsspring.factories 注册,否则配置类不会被扫描。
条件注解 使用 @ConditionalOnClass@ConditionalOnMissingBean 等,确保按需加载且不干扰用户自定义 Bean。
配置属性绑定 使用 @ConfigurationProperties 替代 @Value,提供类型安全的配置绑定。
用户优先原则 自动配置类加载顺序靠后,确保用户自定义的 @Bean 能通过 @ConditionalOnMissingBean 覆盖默认配置。
依赖隔离 Starter 模块本身不应包含业务逻辑,仅声明依赖;逻辑应放在 autoconfigure 模块中。

(三)、 避坑指南与最佳实践

  1. 不要使用 @ComponentScan

    自动配置类应该通过 SPI 机制加载,而不是包扫描。使用 @ComponentScan 会导致配置在非预期的环境下被加载,破坏隔离性。

  2. 注意配置类的加载顺序

    如果配置类之间有依赖关系(例如 A 配置需要 B 配置先存在),使用 @AutoConfigureAfter@AutoConfigureBefore 明确指定顺序。

  3. 区分 Starter 与 Library

    • Library :提供功能代码(如 SmsService)。
    • Starter:提供自动配置和依赖管理,让用户"零配置"使用 Library。
  4. 调试技巧

    在使用方启动时添加 --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,引入后:

  1. spring.factories / AutoConfiguration.imports 里声明了 MybatisAutoConfiguration
  2. Spring Boot 启动时被 AutoConfigurationImportSelector 扫到
  3. 类路径有 SqlSessionFactory + 用户没自己配 → 自动注册 SqlSessionFactoryBean
  4. application.ymlmybatis.mapper-locations=classpath:mapper/*.xml@ConfigurationProperties 绑到 MybatisProperties
  5. 用户啥也不用写,直接 @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-redisspring-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 配置,避免每个服务重复写配置。 自动配置 DataSourceSqlSessionFactory,并绑定 spring.datasource.* 属性。
消息队列 (MQ) 统一管理 Kafka/RocketMQ 的生产者、消费者模板,处理序列化与重试机制。 自动配置 KafkaTemplateDefaultMQProducer,并根据配置自动注册监听器容器。
缓存 (Redis) 统一 Redis 序列化方式(如 JSON)、连接池配置及分布式锁工具类注入。 自动配置 RedisTemplateStringRedisTemplate,并设置默认的 Key/Value 序列化器。
搜索引擎 (ES) 封装 ES 高级客户端,处理连接认证与超时配置。 自动配置 RestHighLevelClientElasticsearchClient

(二)、 企业通用业务能力下沉

将跨部门、跨项目的通用业务模块抽象为 Starter,实现业务逻辑的高度复用。

场景 解决的问题 核心实现逻辑
认证与鉴权 统一 OAuth2/JWT 校验逻辑、权限拦截器,避免每个服务重复写 Security 配置。 自动配置 SecurityFilterChain,注册全局过滤器,并通过 @ConditionalOnProperty 控制开关。
日志与审计 统一操作日志记录、日志脱敏、TraceId 透传。 自动注册 AOP 切面拦截 Controller 或 Service,自动配置 MDC 过滤器。
多租户隔离 统一数据源切换、ThreadLocal 上下文管理。 自动配置 TenantInterceptorTenantDataSourceRouter
防重与幂等 提供统一的接口防重注解和校验逻辑。 自动注册拦截器,结合 Redis 自动检查请求唯一 ID。

(三)、 第三方开放平台 SDK 封装

将外部供应商(如支付、短信、地图)的 SDK 包装成 Spring Boot 风格,方便调用。

  • 场景示例:支付宝/微信支付、阿里云 OSS/SMS、百度地图 API。
  • 实现逻辑
    1. 定义 xxxProperties 绑定 appidsecret 等密钥。
    2. 自动初始化 SDK 的核心 Client(如 OSSClient)。
    3. 提供 XXXService Bean,用户直接 @Autowired 即可调用。

(四)、 运维与监控增强

通过 Starter 统一插桩,降低运维接入成本。

场景 解决的问题 核心实现逻辑
监控指标暴露 统一注册 JVM、线程池、业务指标到 Prometheus。 自动配置 MeterRegistryCustomizer,注册自定义 Collector
动态配置刷新 对接 Apollo/Nacos,实现 Bean 属性的热更新。 自动配置 ConfigService 并监听配置变更事件,结合 @RefreshScope
链路追踪 统一集成 SkyWalking/Zipkin,自动埋点。 自动配置 TracingFilterRestTemplate 拦截器。

(五)、 内部框架与规范约束

强制团队遵守特定的开发规范,减少低级错误。

  • 场景示例:API 响应体统一包装、全局异常处理、参数校验增强。
  • 实现逻辑
    • 自动注册 RestControllerAdvice 统一返回 Result<T> 格式。
    • 自动配置 MethodValidationPostProcessor 开启方法级校验。

(六)、 开发辅助与测试工具

提高开发效率,但不建议在生产环境中启用。

  • 场景示例:Swagger/Knife4j 接口文档、Mock 数据生成器。
  • 实现逻辑 :通过 @ConditionalOnProperty(name = "knife4j.enable", havingValue = "true") 控制仅在开发环境加载。

(七)、场景适用性判断表

在决定是否编写自定义 Starter 时,可以参考下表进行判断:

特征 适合做 Starter 不适合做 Starter
复用频率 高频(超过 3 个服务使用) 低频或一次性逻辑
配置复杂度 高(需要大量 Bean 初始化) 低(仅几个工具类)
侵入性 低(通过自动配置和注解解耦) 高(需要硬编码依赖具体实现)
环境相关性 强(不同环境配置不同) 弱(纯算法或静态工具)

(八)、视野拓展:Starter 与 Library 的界限

很多开发者容易混淆 StarterLibrary 。关键在于 "自动配置" 这一层:

  • Library 只提供代码(如 StringUtilsHttpUtils),需要用户自己管理对象的创建和生命周期。
  • StarterLibrary + AutoConfiguration 。它不仅包含代码,还告诉 Spring Boot:"只要你看到我在 Classpath 里,就帮我创建一个 HttpClient 并配置好超时时间和拦截器。"

如果你的组件仅仅是几个静态方法,不需要 Spring 管理,那么普通的 Jar 包就足够了;如果需要 Spring 容器帮你管理对象及其依赖关系,才需要 Starter。