SpringBoot 自定义 Starter:从零开发一个私有 Starter

前面我们讲了 SpringBoot 自动配置原理、@Conditional 系列条件注解,这些都是底层基础。今天我们把这些知识点落地,实战开发一个「私有 Starter」。

用过 SpringBoot 的都知道,引入一个 starter(比如 spring-boot-starter-web、spring-boot-starter-redis),就能实现开箱即用,不用手动配置。而自定义 Starter,本质就是「封装自己的公共组件/业务逻辑」,让其他项目能直接引入、快速复用,减少重复开发。

结合上两章的自动配置、条件注解知识,能更轻松理解 Starter 的底层逻辑,建议先回顾一下~

一、什么是 Starter?核心原理是什么?

在开发自定义 Starter 之前,我们先明确两个核心问题,避免盲目动手:

1. Starter 的本质

Starter 不是什么神秘的东西,它本质是一个「Maven 依赖包」,里面包含了:

  • • 核心业务代码(公共组件、工具类、业务逻辑等);

  • • 自动配置类(基于 @EnableAutoConfiguration,实现 Bean 自动注入);

  • • META-INF/spring.factories 文件(指定自动配置类,让 SpringBoot 能扫描到)。

简单说:Starter = 公共代码 + 自动配置 + 配置清单,目的是「封装复用、开箱即用」。

2. 自定义 Starter 的核心原理

自定义 Starter 的底层逻辑,就是我们上两章讲的内容,串联起来就是:

    1. 将公共组件/业务逻辑封装成 Bean,通过 @Configuration 配置类定义;
    1. 用 @Conditional 系列注解,实现 Bean 的按需加载(比如根据配置开关控制是否生效);
    1. 在 META-INF/spring.factories 文件中,注册自动配置类,让 SpringBoot 启动时能加载到;
    1. 其他项目引入该 Starter 依赖后,SpringBoot 自动加载配置类,注入 Bean,实现开箱即用。

3. 自定义 Starter 的命名规范(约定大于配置)

SpringBoot 官方有明确的命名规范,建议遵循(避免和官方 Starter 冲突,也更规范):

  • • 官方 Starter:spring-boot-starter-xxx(比如 spring-boot-starter-web、spring-boot-starter-redis);

  • • 自定义 Starter:xxx-spring-boot-starter(比如 mybatis-spring-boot-starter、aliyun-sms-spring-boot-starter)。

我们今天实战开发的 Starter,命名为:custom-sms-spring-boot-starter(自定义短信 Starter,实现短信发送功能的封装复用)。

二、从零开发 custom-sms-spring-boot-starter

本次实战目标:开发一个短信发送的私有 Starter,实现以下功能:

  • • 支持通过配置文件配置短信平台的 appId、appSecret;

  • • 提供短信发送接口,支持阿里云、腾讯云两种短信平台(可通过配置切换);

  • • 支持通过配置开关,开启/关闭短信功能;

  • • 其他项目引入该 Starter 后,直接注入 Bean 就能使用,无需额外配置。

开发环境:JDK8 + Maven + SpringBoot 2.7.x(兼容主流版本),步骤分5步,每一步都有详细代码和说明。

第一步:创建 Maven 项目(Starter 核心工程)

Starter 本身是一个 Maven 项目,不需要是 SpringBoot 项目(因为它是被其他 SpringBoot 项目引入的依赖),具体操作:

1. 新建 Maven 项目

IDE 中新建 Maven Project,GroupId 自定义(比如 com.example),ArtifactId 按规范命名为 custom-sms-spring-boot-starter,Packaging 为 jar(默认即可)。

2. 配置 pom.xml 依赖

核心依赖:SpringBoot 自动配置核心依赖(spring-boot-autoconfigure)、配置处理器(用于配置文件提示),无需引入 spring-boot-starter(避免依赖冲突)。

go 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <!-- 自定义 GroupId 和 ArtifactId -->
    <groupId>com.example</groupId>
    <artifactId>custom-sms-spring-boot-starter</artifactId>
    <version>1.0.0</version>
    <name>custom-sms-spring-boot-starter</name>
    <description>自定义短信发送 Starter</description>

    <!-- 依赖管理 -->
    <dependencies><!-- SpringBoot 自动配置核心依赖(必须) -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
            <version>2.7.10</version>
            <scope>provided</scope>
        </dependency>

        <!-- 配置处理器:用于在 application.yml 中提供配置提示(可选,但推荐) -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <version>2.7.10</version>
            <scope>provided</scope>
        </dependency>

        <!--  lombok(简化代码,可选) -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.24</version>
           <scope>provided</scope>
        </dependency>
    </dependencies><!-- 打包配置(可选,确保打包后能被其他项目引入) -->
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <target>8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

说明:scope 设为 provided,是因为其他项目引入该 Starter 时,会自带 SpringBoot 相关依赖,避免重复引入导致版本冲突。

第二步:定义配置属性类(绑定配置文件)

用于绑定 application.yml 中的配置(比如短信平台的 appId、appSecret、平台类型、开启开关),通过 @ConfigurationProperties 注解实现。

go 复制代码
package com.example.sms.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

/**
 * 短信配置属性类:绑定配置文件中的 sms 前缀配置
 */
@Data
@ConfigurationProperties(prefix = "sms") // 绑定配置文件中 sms 前缀的属性
public class SmsProperties {

    /**
     * 短信功能开启开关(默认关闭)
     */
    private boolean enable = false;

    /**
     * 短信平台类型(aliyun:阿里云,tencent:腾讯云)
     */
    private String platform;

    /**
     * 短信平台 appId
     */
    private String appId;

    /**
     * 短信平台 appSecret
     */
    private String appSecret;

    /**
     * 短信签名(可选,模拟场景)
     */
    private String signName;
}

说明:@ConfigurationProperties(prefix = "sms") 表示该类会绑定配置文件中所有以「sms.」开头的属性,比如 sms.enable、sms.platform 等;添加 @Data 注解,自动生成 get/set 方法,简化代码。

第三步:开发核心业务逻辑(短信发送接口+实现)

封装短信发送的核心逻辑,提供统一接口,支持不同短信平台的实现(阿里云、腾讯云),便于扩展。

1. 定义短信发送接口
go 复制代码
package com.example.sms.service;

/**
 * 短信发送统一接口
 */
public interface SmsService {

    /**
     * 发送短信
     * @param phone 手机号
     * @param content 短信内容
     * @return 发送结果(成功/失败)
     */
    boolean sendSms(String phone, String content);
}
2. 实现阿里云短信发送
go 复制代码
package com.example.sms.service.impl;

import com.example.sms.service.SmsService;
import lombok.Data;

/**
 * 阿里云短信发送实现
 */
@Data
public class AliyunSmsServiceImpl implements SmsService {

    // 从配置文件中注入的 appId 和 appSecret
    private String appId;
    private String appSecret;
    private String signName;

    @Override
    public boolean sendSms(String phone, String content) {
        // 模拟阿里云短信发送逻辑(实际开发中替换为阿里云官方 SDK 调用)
        System.out.println("阿里云短信发送 -> appId:" + appId + ",appSecret:" + appSecret);
        System.out.println("向手机号:" + phone + ",发送短信:" + content + ",签名:" + signName);
        return true; // 模拟发送成功
    }
}
3. 实现腾讯云短信发送
go 复制代码
package com.example.sms.service.impl;

import com.example.sms.service.SmsService;
import lombok.Data;

/**
 * 腾讯云短信发送实现
 */
@Data
public class TencentSmsServiceImpl implements SmsService {

    private String appId;
    private String appSecret;
    private String signName;

    @Override
    public boolean sendSms(String phone, String content) {
        // 模拟腾讯云短信发送逻辑(实际开发中替换为腾讯云官方 SDK 调用)
        System.out.println("腾讯云短信发送 -> appId:" + appId + ",appSecret:" + appSecret);
        System.out.println("向手机号:" + phone + ",发送短信:" + content + ",签名:" + signName);
        return true;
    }
}

第四步:编写自动配置类(实现 Bean 自动注入)

这是 Starter 的核心,通过自动配置类,根据配置文件的属性,自动创建 SmsService 实例,注入到 Spring 容器中,同时结合 @Conditional 系列注解,实现按需加载。

go 复制代码
package com.example.sms.config;

import com.example.sms.service.SmsService;
import com.example.sms.service.impl.AliyunSmsServiceImpl;
import com.example.sms.service.impl.TencentSmsServiceImpl;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 短信 Starter 自动配置类
 */
@Configuration // 标记为配置类
@EnableConfigurationProperties(SmsProperties.class) // 启用配置属性绑定(让 SmsProperties 生效)
@ConditionalOnClass(SmsService.class) // 类路径中存在 SmsService 时生效(确保核心类存在)
@ConditionalOnProperty( // 根据配置文件中的 sms.enable 决定是否生效
        prefix = "sms",
        name = "enable",
        havingValue = "true",
        matchIfMissing = false
)
public class SmsAutoConfiguration {

    // 注入配置属性类(从配置文件中读取的配置)
    private final SmsProperties smsProperties;

    // 构造方法注入(Spring 4.3+ 支持构造方法自动注入)
    public SmsAutoConfiguration(SmsProperties smsProperties) {
        this.smsProperties = smsProperties;
    }

    /**
     * 阿里云短信 Bean:当平台类型为 aliyun 时创建
     */
    @Bean
    @ConditionalOnProperty(
            prefix = "sms",
            name = "platform",
            havingValue = "aliyun"
    )
    @ConditionalOnMissingBean // 避免用户自定义 Bean 时被覆盖
    public SmsService aliyunSmsService() {
        AliyunSmsServiceImpl aliyunSms = new AliyunSmsServiceImpl();
        // 将配置文件中的属性注入到 Bean 中
        aliyunSms.setAppId(smsProperties.getAppId());
        aliyunSms.setAppSecret(smsProperties.getAppSecret());
        aliyunSms.setSignName(smsProperties.getSignName());
        return aliyunSms;
    }

    /**
     * 腾讯云短信 Bean:当平台类型为 tencent 时创建
     */
    @Bean
    @ConditionalOnProperty(
            prefix = "sms",
            name = "platform",
            havingValue = "tencent"
    )
    @ConditionalOnMissingBean
    public SmsService tencentSmsService() {
        TencentSmsServiceImpl tencentSms = new TencentSmsServiceImpl();
        tencentSms.setAppId(smsProperties.getAppId());
        tencentSms.setAppSecret(smsProperties.getAppSecret());
        tencentSms.setSignName(smsProperties.getSignName());
        return tencentSms;
    }
}
关键注解解读
  • @EnableConfigurationProperties(SmsProperties.class):启用配置属性绑定,让 Spring 能识别 SmsProperties 类,将配置文件中的属性注入到该类中;

  • @ConditionalOnClass(SmsService.class):确保类路径中存在 SmsService 接口(核心业务类),避免依赖缺失导致报错;

  • @ConditionalOnProperty:控制自动配置的开关(sms.enable=true 才生效),同时根据 sms.platform 切换不同的短信平台实现;

  • @ConditionalOnMissingBean:允许用户自定义 SmsService 实现,覆盖 Starter 中的默认实现,符合 SpringBoot 「约定大于配置」的理念。

第五步:创建 META-INF/spring.factories 文件(注册自动配置类)

这是最关键的一步------SpringBoot 启动时,会通过 SpringFactoriesLoader 读取 META-INF/spring.factories 文件,找到自动配置类并加载。如果没有这个文件,自动配置类不会被 Spring 扫描到,Starter 就会失效。

1. 创建目录

在 src/main/resources 目录下,新建 META-INF 文件夹(注意大写),然后在 META-INF 文件夹下,新建 spring.factories 文件。

2. 配置自动配置类

在 spring.factories 文件中,添加以下内容,指定我们编写的自动配置类:

go 复制代码
# SpringBoot 自动配置类清单
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.sms.config.SmsAutoConfiguration

说明:

  • • key 固定为 org.springframework.boot.autoconfigure.EnableAutoConfiguration(SpringBoot 自动配置的核心 key);

  • • value 为我们编写的自动配置类的全类名(com.example.sms.config.SmsAutoConfiguration);

  • • 如果有多个自动配置类,用逗号分隔,换行用反斜杠「\」连接。

第六步:打包 Starter(供其他项目引入)

Starter 开发完成后,需要打包成 jar 包,才能被其他 SpringBoot 项目引入使用,打包步骤:

    1. 打开 IDEA 的 Maven 面板,找到当前 Starter 项目;
    1. 点击「clean」(清理旧的打包文件),再点击「package」(打包);
    1. 打包完成后,jar 包会生成在 target 目录下(比如 custom-sms-spring-boot-starter-1.0.0.jar)。

补充:如果需要在公司内部共享该 Starter,可将其上传到公司的 Maven 私有仓库(如 Nexus),其他项目通过 pom.xml 引入即可;如果只是本地测试,可将 jar 包安装到本地 Maven 仓库(点击 Maven 面板的「install」)。

三、测试验证:新建 SpringBoot 项目,引入自定义 Starter

开发完 Starter 后,必须测试验证,确保能正常使用,步骤如下:

1. 新建 SpringBoot 测试项目

新建一个 SpringBoot 项目(比如 test-sms-starter),引入 web 依赖(方便测试接口)。

2. 引入自定义 Starter 依赖

在测试项目的 pom.xml 中,引入我们打包好的 Starter 依赖(本地测试需先 install 到本地仓库):

go 复制代码
<!-- 引入自定义短信 Starter -->
<dependency>
    <groupId>com.example</groupId>
    <artifactId>custom-sms-spring-boot-starter</artifactId>
    <version>1.0.0</version>
</dependency>

<!-- web 依赖(用于测试接口) -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

3. 配置 application.yml

在测试项目的 application.yml 中,添加短信相关配置,开启短信功能,指定阿里云平台:

go 复制代码
server:
  port: 8080

# 自定义短信 Starter 配置
sms:
  enable: true # 开启短信功能
  platform: aliyun # 选择阿里云短信平台
  app-id: 123456 # 模拟 appId
  app-secret: abcdef123456 # 模拟 appSecret
  sign-name: 我的测试签名 # 短信签名

4. 编写测试接口

创建一个测试 Controller,注入 SmsService,调用短信发送方法:

go 复制代码
package com.example.test.controller;

import com.example.sms.service.SmsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class SmsTestController {

    // 直接注入自定义 Starter 中的 SmsService(自动配置,无需手动创建)
    @Autowired
    private SmsService smsService;

    @GetMapping("/sendSms")
    public String sendSms(@RequestParam String phone, @RequestParam String content) {
        boolean result = smsService.sendSms(phone, content);
        return result ? "短信发送成功" : "短信发送失败";
    }
}

5. 启动项目,测试接口

    1. 启动测试项目,SpringBoot 会自动加载 SmsAutoConfiguration 配置类,创建 AliyunSmsServiceImpl Bean;
    1. 访问接口:http://localhost:8080/sendSms?phone=13800138000\&content=测试自定义Starter,查看控制台输出;
    1. 控制台会打印阿里云短信发送的相关信息,接口返回「短信发送成功」,说明 Starter 正常生效。

额外测试:切换短信平台

将 application.yml 中的 sms.platform 改为 tencent,重启项目,再次访问接口,控制台会打印腾讯云短信发送信息,说明条件注解生效,能根据配置切换 Bean。

四、注意事项

开发自定义 Starter 时,有很多细节需要注意,避免出现依赖冲突、配置不生效等问题,下面这些最佳实践,一定要遵循:

1. 依赖管理:避免引入多余依赖

Starter 是公共依赖,尽量只引入核心依赖(如 spring-boot-autoconfigure),避免引入 web、redis 等具体依赖------如果需要依赖其他组件,建议用 @ConditionalOnClass 判断,让用户自行引入相关依赖,避免依赖冲突。

2. 配置属性:提供默认值,增加容错性

在 SmsProperties 中,给非必填属性提供默认值(比如 sms.enable 默认 false),避免用户未配置时出现空指针异常;同时,通过 @ConfigurationProperties 的 validate 功能,对必填属性进行校验(比如 appId、appSecret 不能为空)。

3. 条件注解:合理使用,避免无效加载

结合 @ConditionalOnProperty、@ConditionalOnClass 等注解,实现按需加载,避免无用 Bean 占用资源;比如只有开启 sms.enable=true 时,才加载短信相关 Bean。

4. 命名规范:遵循官方约定

严格按照「xxx-spring-boot-starter」的命名规范,避免和官方 Starter 冲突;同时,配置属性的前缀(如 sms)尽量唯一,避免和其他 Starter 的配置冲突。

5. 文档说明:添加 README 文件

给 Starter 项目添加 README 文件,说明 Starter 的功能、配置属性、使用方法、依赖版本要求等,方便其他开发者使用。

五、面试高频考点(自定义 Starter 必背)

自定义 Starter 是 SpringBoot 面试的高频考点:

自定义 Starter 的核心组成部分有哪些?

答:核心有3部分:① 配置属性类(@ConfigurationProperties):绑定配置文件中的属性;② 自动配置类(@Configuration + 条件注解):实现 Bean 自动注入;③ META-INF/spring.factories 文件:注册自动配置类,让 SpringBoot 能扫描到。

SpringBoot 如何识别自定义 Starter 中的自动配置类?

答:SpringBoot 启动时,会通过 SpringFactoriesLoader 工具类,读取 classpath 下 META-INF/spring.factories 文件,该文件中配置了自动配置类的全类名,SpringBoot 会加载这些自动配置类,结合条件注解筛选后,注入对应的 Bean。

如何实现 Starter 的按需加载?

答:通过 @Conditional 系列注解实现按需加载,比如:① @ConditionalOnProperty:根据配置文件的开关控制是否生效;② @ConditionalOnClass:根据是否引入依赖控制是否生效;③ @ConditionalOnMissingBean:允许用户自定义 Bean 覆盖默认实现。

自定义 Starter 和官方 Starter 的区别是什么?

答:① 命名规范不同:官方 Starter 是 spring-boot-starter-xxx,自定义 Starter 是 xxx-spring-boot-starter;② 功能不同:官方 Starter 是通用组件(如 web、redis),自定义 Starter 是业务相关的公共组件(如短信、支付);③ 维护主体不同:官方 Starter 由 Spring 团队维护,自定义 Starter 由开发者自己维护。

六、总结

    1. 自定义 Starter 本质是「封装公共组件的 Maven 依赖包」,核心是自动配置 + 配置清单;
      1. 开发步骤:创建 Maven 项目 → 定义配置属性类 → 开发核心业务逻辑 → 编写自动配置类 → 配置 spring.factories → 打包;
      1. 关键注解:@ConfigurationProperties(绑定配置)、@Configuration(配置类)、@EnableConfigurationProperties(启用配置绑定)、@Conditional 系列(按需加载);
      1. 核心价值:实现公共组件复用,减少重复开发,让其他项目能开箱即用;
      1. 避坑关键:合理管理依赖、提供默认配置、遵循命名规范、使用条件注解实现按需加载。

    到这里,我们就从零开发完成了一个私有 Starter,从底层原理到实战操作,每一步都清晰可落地。其实自定义 Starter 并不复杂,核心就是把我们之前学的自动配置、条件注解知识,串联起来,应用到实际开发中。

    以后工作中,遇到需要复用的公共组件(比如短信、支付、日志、权限等),就可以按照这个模板,开发自己的 Starter,提升开发效率,也能体现你的技术功底。

相关推荐
悟空码字2 小时前
别再System.out了!这份SpringBoot日志优雅指南,让你告别日志混乱
java·spring boot·后端
一 乐2 小时前
工会管理|基于springboot + vue工会管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·论文·毕设·工会管理系统
callJJ2 小时前
Spring AI ETL 数据处理管道实战指南:从原始文档到向量索引
java·人工智能·spring·ai·etl·spring ai
暗暗别做白日梦2 小时前
Maven 内部 Jar 包私服部署 + 多模块父工程核心配置
java·maven·jar
从零开始的-CodeNinja之路2 小时前
【Redis】Redis 缓存应用、淘汰机制—(四)
java·redis·缓存
程序员张33 小时前
自定义跨字段校验必填注解
java·后端
weixin_704266053 小时前
手机体检预约系统开发解析
java·开发语言
白露与泡影3 小时前
Java八股文大全(2026最新版)大厂面试题附答案详解
java·开发语言
那个失眠的夜3 小时前
Spring 的纯注解配置
xml·java·数据库·后端·spring·junit