zhangrui.i
zhangrui.i
发布于 2025-07-10 / 8 阅读
0
0

Spring Boot 3.x 自定义封装 Starter 实战:对接多家第三方短信平台

一、引言

在现代的软件开发中,短信服务是一个常见的需求,比如验证码发送、通知提醒等。不同的业务场景可能会选择不同的第三方短信平台,如阿里云、腾讯云、亚马逊云等。为了方便开发者在 Spring Boot 项目中灵活使用这些短信服务,我们可以自定义一个 Spring Boot Starter 来封装短信服务的逻辑。

本文将详细介绍如何在 Spring Boot 3.x 中自定义封装一个 sms-spring-boot-starter,让开发者可以通过配置灵活选择短信服务提供商,并且默认使用腾讯云作为短信服务商。

二、核心架构设计

2.1 整体架构

我们的 Starter 采用工厂模式和策略模式,实现多服务商的动态切换:

image-zsvm.png
从客户端请求到短信发送的整个流程

image-wkte.png

2.2 项目结构

我们的项目包含两个主要模块:

ssm-spring-boot-starter
自定义的 Starter 模块,封装短信服务的核心逻辑。
starter-test
测试模块,用于验证 Starter 的功能。

ssm-spring-boot-starter 模块结构

ssm-spring-boot-starter
├── src
│   ├── main
│   │   ├── java
│   │   │   └── com
│   │   │       └── fox
│   │   │           └── ssmspringbootstarter
│   │   │               ├── config
│   │   │               │   ├── SmsAutoConfiguration.java
│   │   │               │   ├── SmsProperties.java
│   │   │               │   └── SmsTemplate.java
│   │   │               ├── constant
│   │   │               │   └── SmsTypeEnum.java
│   │   │               ├── factory
│   │   │               │   └── SmsHandleFactory.java
│   │   │               └── service
│   │   │                   ├── SmsService.java
│   │   │                   └── impl
│   │   │                       ├── AliCloudSmsServiceImpl.java
│   │   │                       ├── TxCloudSmsServiceImpl.java
│   │   │                       └── YmxCloudSmsServiceImpl.java
│   │   └── resources
│   │       └── META-INF
│   │           └── spring
│   │               └── org.springframework.boot.autoconfigure.AutoConfiguration.imports
└── pom.xml

starter-test 模块结构

starter-test
├── src
│   ├── main
│   │   ├── java
│   │   │   └── com
│   │   │       └── fox
│   │   │           └── startertest
│   │   │               ├── StarterTestApplication.java
│   │   │               └── controller
│   │   │                   └── SmsController.java
│   │   └── resources
│   │       └── application.properties
│   └── test
│       └── java
│           └── com
│               └── fox
│                   └── startertest
│                       └── StarterTestApplicationTests.java
└── pom.xml

三、代码实现

3.1 定义短信服务类型枚举

在 ssm-spring-boot-starter 模块中,我们首先定义一个枚举类 SmsTypeEnum 来表示不同的短信服务提供商:

package com.fox.ssmspringbootstarter.constant;
/**
 * @author Fox
 */
public enum SmsTypeEnum {
    // 阿里云
    ALI_CLOUD("ali"),
    // 腾讯云
    TX_CLOUD("tx"),
    // 亚马逊云
    YMX_CLOUD("ymx");
    private String type;
    SmsTypeEnum(String type) {
        this.type = type;
    }
    public String getType() {
        return type;
    }
}

3.2 定义短信服务接口和实现类

定义一个 SmsService 接口,包含发送短信的方法:

package com.fox.ssmspringbootstarter.service;
/**
 * @author Fox
 */
public interface SmsService {
    /**
     * 发送短信
     *
     * @param fromPhone 发送方手机号
     * @param toPhone   接收方手机号
     * @param content   短信内容
     * @return 发送结果
     */
    String send(String fromPhone, String toPhone, String content);
}

然后为每个短信服务提供商实现该接口:

// 阿里云短信服务实现类
package com.fox.ssmspringbootstarter.service.impl;
import com.fox.ssmspringbootstarter.service.SmsService;
import org.springframework.stereotype.Service;
/**
 * 阿里云 SMS 实现
 *
 * @author Fox
 */
@Service("ali")
public class AliCloudSmsServiceImpl implements SmsService {
    @Override
    public String send(String fromPhone, String toPhone, String content) {
        System.out.println("------------------当前 SMS 厂商为阿里云------------------");
        System.out.println("----" + fromPhone + " 向 " + toPhone + " 发送了一条短信。" + "----");
        System.out.println("短信内容为:" + content);
        System.out.println("----------------------------------------------------");
        return "success";
    }
}
// 腾讯云短信服务实现类
package com.fox.ssmspringbootstarter.service.impl;
import com.fox.ssmspringbootstarter.service.SmsService;
import org.springframework.stereotype.Service;
/**
 * 腾讯云 SMS 实现
 *
 * @author Fox
 */
@Service("tx")
public class TxCloudSmsServiceImpl implements SmsService {
    @Override
    public String send(String fromPhone, String toPhone, String content) {
        System.out.println("------------------当前 SMS 厂商为腾讯云------------------");
        System.out.println("----" + fromPhone + " 向 " + toPhone + " 发送了一条短信。" + "----");
        System.out.println("短信内容为:" + content);
        System.out.println("----------------------------------------------------");
        return "success";
    }
}
// 亚马逊云短信服务实现类
package com.fox.ssmspringbootstarter.service.impl;
import com.fox.ssmspringbootstarter.service.SmsService;
import org.springframework.stereotype.Service;
/**
 * 亚马逊云 SMS 实现
 *
 * @author Fox
 */
@Service("ymx")
public class YmxCloudSmsServiceImpl implements SmsService {
    @Override
    public String send(String fromPhone, String toPhone, String content) {
        System.out.println("------------------当前 SMS 厂商为亚马逊云------------------");
        System.out.println("----" + fromPhone + " 向 " + toPhone + " 发送了一条短信。" + "----");
        System.out.println("短信内容为:" + content);
        System.out.println("----------------------------------------------------");
        return "success";
    }
}

3.3 定义短信服务工厂类

创建一个 SmsHandleFactory 类,用于根据配置动态创建短信服务实例:

package com.fox.ssmspringbootstarter.factory;
import com.fox.ssmspringbootstarter.service.SmsService;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Component
public class SmsHandleFactory {
    @Autowired
    private ListableBeanFactory beanFactory; // 注入 Spring 容器
    /**
     * 创建处理类对象
     *
     * @param code 短信服务提供商代码
     * @return SmsService 实例
     */
    public SmsService createSmsService(String code) {
        // 从 Spring 容器中按名称获取 Bean(code 需与 @Service("code") 一致)
        return beanFactory.getBean(code, SmsService.class);
    }
}

3.4 定义配置类和属性类

创建 SmsProperties 类来读取配置文件中的短信服务提供商配置:

package com.fox.ssmspringbootstarter.config;
import com.fox.ssmspringbootstarter.constant.SmsTypeEnum;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
 * @author Fox
 */
@ConfigurationProperties(prefix = "sms.server")
public class SmsProperties {
    /**
     * 发送短信类型
     */
    private String type;
    public String getType() {
        if (type == null || "".equals(type)) {
            type = SmsTypeEnum.TX_CLOUD.getType();
        }
        return type;
    }
    public void setType(String type) {
        this.type = type;
    }
}
创建 SmsAutoConfiguration 类来自动配置短信服务:

package com.fox.ssmspringbootstarter.config;
import com.fox.ssmspringbootstarter.factory.SmsHandleFactory;
import com.fox.ssmspringbootstarter.service.SmsService;
import com.fox.ssmspringbootstarter.constant.SmsTypeEnum;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import java.util.List;
/**
 * @author Fox
 */
@AutoConfiguration
@ConditionalOnClass({SmsTemplate.class})
@EnableConfigurationProperties(value = SmsProperties.class)
public class SmsAutoConfiguration {
    @Bean
    @ConditionalOnMissingBean
    public SmsTemplate smsTemplate() {
        return new SmsTemplate();
    }
}

自动配置类上使用了几个关键的条件注解:

  1. @AutoConfiguration
    这是 Spring Boot 3.x 新引入的注解,专门用于标记自动配置类。它替代了 Spring Boot 2.x 中的 @Configuration 注解,使自动配置类的语义更加明确。

  2. @ConditionalOnClass({SmsTemplate.class})
    这个注解表示只有当类路径中存在 SmsTemplate 类时,才会加载这个自动配置类。这确保了只有在引入了我们的 Starter 依赖时,自动配置才会生效。

  3. @EnableConfigurationProperties(value = SmsProperties.class)
    这个注解启用了 SmsProperties 类的配置属性绑定功能,允许我们通过 application.properties 或 application.yml 配置短信服务参数。

创建Spring Boot 3.x 自动配置文件

# META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
com.fox.ssmspringbootstarter.config.SmsAutoConfiguration

如果需要同时兼容 Spring Boot 2.x 和 3.x,可以保留两种配置文件:

ssm-spring-boot-starter
└── src
    └── main
        └── resources
            ├── META-INF
            │   ├── spring
            │   │   └── org.springframework.boot.autoconfigure.AutoConfiguration.imports  # Spring Boot 3.x
            │   └── spring.factories  # Spring Boot 2.x 兼容

其中,spring.factories 内容为:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.fox.ssmspringbootstarter.config.SmsAutoConfiguration

创建 SmsTemplate 类来封装短信发送逻辑:

package com.fox.ssmspringbootstarter.config;
import com.fox.ssmspringbootstarter.factory.SmsHandleFactory;
import com.fox.ssmspringbootstarter.service.SmsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
 * @author Fox
 */
@Component
public class SmsTemplate {
    @Autowired
    private SmsProperties smsProperties;
    @Autowired
    private SmsHandleFactory smsHandleFactory;
    public String send(String fromPhone, String toPhone, String content) {
        // 获取云厂商的业务实现类
        String type = smsProperties.getType();
        SmsService smsService = smsHandleFactory.createSmsService(type);
        if (smsService == null) {
            throw new IllegalArgumentException("No SmsService implementation found for type: " + type);
        }
        return smsService.send(fromPhone, toPhone, content);
    }
}

当开发者在应用中引入我们的 sms-spring-boot-starter 依赖后,整个自动配置流程如下:

  1. 应用启动:Spring Boot 应用启动时,会自动扫描类路径下的 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件。
  2. 加载自动配置类:发现并加载我们的 SmsAutoConfiguration 类。
  3. 条件检查:检查是否存在 SmsTemplate 类(通过 @ConditionalOnClass),确保 Starter 被正确引入。
  4. 配置属性绑定:将配置文件中以 sms.server 为前缀的属性绑定到 SmsProperties 类的字段上。
  5. 创建 Bean:创建 SmsTemplate 和 SmsHandleFactory Bean。
  6. 服务发现:SmsHandleFactory 从 Spring 容器中获取所有实现了 SmsService 接口的 Bean。
  7. 使用服务:开发者注入 SmsTemplate 并调用 send 方法时,SmsTemplate 根据配置选择合适的服务实现并调用其发送方法。

3.5 测试模块

在 starter-test 模块中,创建一个 SmsController 来测试短信发送功能:

package com.fox.startertest.controller;
import com.fox.ssmspringbootstarter.config.SmsTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
 * @author Fox
 */
@RestController
@RequestMapping("/sms")
public class SmsController {
    @Resource
    private SmsTemplate smsTemplate;
    @RequestMapping("/send")
    public String send() {
        String fromPhone = "186xxxxxxxx";
        String toPhone = "156xxxxxxxx";
        String content = "Fox,今晚十点王者 五缺一,收到请回复,over!";
        return smsTemplate.send(fromPhone, toPhone, content);
    }
}

在 application.properties 中配置短信服务提供商:

spring.application.name=starter-test
sms.server.type=tx

启动服务后测试:http://localhost:8080/sms/send,控制台输出

image-zsor.png

四、总结

通过自定义 Spring Boot Starter,我们实现了一个灵活的短信服务封装,开发者可以通过配置轻松切换不同的短信服务提供商。这种方式提高了代码的复用性和可维护性,使得项目在不同的业务场景下能够快速适配不同的短信服务。同时,利用 Spring Boot 的自动配置功能,简化了开发者的使用步骤,让开发者可以更专注于业务逻辑的实现。


评论