前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【Starter 】Spring Boot 3.x 自定义封装Starter 实战

【Starter 】Spring Boot 3.x 自定义封装Starter 实战

作者头像
后端码匠
发布2024-09-10 20:30:15
2460
发布2024-09-10 20:30:15
举报
文章被收录于专栏:后端码匠

【Starter 】Spring Boot 3.x 自定义封装Starter 实战

Starter 背景简介及作用

什么是 Starter

Starter 是 Spring Boot 中的一项创新发明,它的主要作用是降低项目开发中的复杂性,从而简化开发操作。通过使用 Starter,开发人员可以轻松地引入常用的库和配置,避免了繁琐的手动设置。

Starter 的理念:Starter 会将项目中所需的所有依赖一起打包,开发者不需要手动逐个引入依赖。这种设计减少了手动管理依赖的麻烦,确保了所需依赖版本的一致性。不同的 Starter 是为了解决不同的场景需求,比如 JPA 和 Redis 各有对应的 Starter,这种差异是由于 Starter 本质上是对逻辑层的封装和抽象。类似于 Docker 的概念,Starter 通过封装帮助开发者快速解决复杂问题。

例如,spring-boot-starter-data-jpa 就会自动配置与 JPA 相关的依赖和配置,而开发者只需将其引入即可使用 JPA 功能,而不必手动处理配置。

自定义 Starter 的背景

虽然在开发中我们经常使用官方的 Starter,例如数据库、缓存、消息队列等中间件的 Starter,但在实际开发中仍然会遇到一些情况:

  • 某些中间件没有官方 Starter 支持,开发者需要自行引入相关依赖并手动配置。
  • Spring Boot 版本更新后,部分官方 Starter 未能及时更新,导致无法兼容新版本。

在企业中,技术负责人或架构师通常会根据项目的实际需求,封装属于自己团队的自定义 Starter。这种自定义 Starter 通过统一的规范与配置,极大地简化了项目的开发流程,提升了开发效率。例如,封装数据库访问、认证授权、消息服务等,所有项目组成员都可以统一使用,提高了代码的规范性和一致性。

自定义 Starter 封装规范

  • 官方的 Starter 包命名规范为:spring-boot-starter-xxx,如 spring-boot-starter-webspring-boot-starter-jdbc
  • 自定义 Starter 的命名规范建议为:xxx-spring-boot-starter,如 mycompany-spring-boot-starter-email,这种命名方式便于团队识别自定义的 Starter,同时遵循 Spring 的命名风格。

自定义 Starter 通常包含以下内容:

  1. 自动配置类@Configuration):定义好依赖的自动配置逻辑。
  2. 配置属性类@ConfigurationProperties):支持使用外部化配置参数来简化使用。
  3. 必要的依赖管理:将常用的第三方库一并引入,避免手动添加依赖。

通过自定义 Starter,企业能够封装复杂的业务逻辑和配置,减少重复劳动,并确保项目开发中的一致性和可维护性。

代码语言:javascript
复制
spring-boot-starter
spring-boot-starter-data-jpa
spring-boot-starter-data-redis
spring-boot-starter-data-mongodb
spring-boot-starter-jdbc
mybatis-spring-boot-starter
mybatis-plus-boot-starter

新版Spring Boot3.X和旧版Spring Boot2.7之前自定义Starter区别

SpringBoot 2.7 之前

META-INF/spring.factories 文件里添加org.springframework.boot.autoconfigure.EnableAutoConfiguration=XXAutoConfiguration

Spring Boot2.7 推出新的自动配置

在META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

文件里添加配置类名称,每行包含一个配置类全限定名;

兼容META-INF/spring.factories方式;

Spring Boot3.x 移除spring.factories;

移除META-INF/spring.factories方式;

只支持META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 增加自动配置。

自定义starter的步骤

  • 创建项目 xx-spring-boot-starter
  • 添加依赖
代码语言:javascript
复制
 <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-autoconfigure</artifactId>
      <version>3.0.2</version>
</dependency>
  • 创建配置类
  • 创建XXAutoConfiguration类
  • 增加Condition条件注解
  • 配置AutoConfiguration.imports自动配置类

自定义 Starter 案例实战

需求背景

目前公司所有的短信发送功能都已集成到消息中心。为了简化各个平台的开发流程,计划自定义封装一个 codingce-sms-spring-boot-starter,供各个平台统一使用。

这个 codingce-sms-spring-boot-starter 需要能够对接多家第三方短信平台,包括阿里云、腾讯云、亚马逊云等。开发者可以通过配置灵活选择短信服务提供商,默认情况下使用腾讯云作为短信服务商。如果 Spring 容器中不存在相应的 bean,则自动创建;如果已经存在,则不再创建。

创建SpringBoot3.x项目

codingce-sms-spring-boot-starter,添加autoconfigure依赖

代码语言:javascript
复制
<dependencies>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-autoconfigure</artifactId>
        <version>3.0.2</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>

</dependencies>

SmsAutoConfiguration

代码语言:javascript
复制
package cn.com.codingce.sms.config;

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;

/**
 * @author Will
 */
@AutoConfiguration
@ConditionalOnClass({SmsTemplate.class})
@EnableConfigurationProperties(value = SmsProperties.class)
public class SmsAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean
    public SmsTemplate smsTemplate() {
        return new SmsTemplate();
    }

}

SmsProperties

代码语言:javascript
复制
package cn.com.codingce.sms.config;

import cn.com.codingce.sms.constant.SmsTypeEnum;
import org.springframework.boot.context.properties.ConfigurationProperties;

/**
 * @author Will
 */
@ConfigurationProperties(prefix = "sms.server.achieve")
public class SmsProperties {

    /**
     * 发送短信类型
     */
    private String type;

    public String getType() {
        if (type == null || "".equals(type)) {
            type = SmsTypeEnum.TX_CLOUD.name();
        }
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }
    
}

SmsTemplate

代码语言:javascript
复制
package cn.com.codingce.sms.config;

import cn.com.codingce.sms.factory.SmsHandleFactory;
import cn.com.codingce.sms.service.SmsService;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * @author Will
 */
public class SmsTemplate {

    @Autowired
    private SmsProperties smsProperties;

    @Resource
    SmsHandleFactory smsHandleFactory;

    public String send(String fromPhone, String toPhone, String content) {
        //获取云厂商的业务实现类
        String type = smsProperties.getType();
        SmsService smsService = smsHandleFactory.createSmsService(type);
        return smsService.send(fromPhone, toPhone, content);
    }

}

SmsTypeEnum

代码语言:javascript
复制
package cn.com.codingce.sms.constant;

/**
 * @author Will
 */
public enum SmsTypeEnum {

    //阿里云
    ALI_CLOUD("ali"),
    //腾讯云
    TX_CLOUD("tx"),
    //亚马逊云
    YMX_CLOUD("ymx");

    private String type;

    SmsTypeEnum(String ymx) {
    }

}

SmsHandleFactory

代码语言:javascript
复制
package cn.com.codingce.sms.factory;

import cn.com.codingce.sms.service.SmsService;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author Will
 */
@Component
public class SmsHandleFactory {

    public Map<String, SmsService> handlerMap = new ConcurrentHashMap<>();

    /**
     * 创建处理类对象
     *
     * @param code code
     * @return ISmsService
     */
    public SmsService createSmsService(String code) {
        return handlerMap.get(code);
    }

}

SmsService

代码语言:javascript
复制
package cn.com.codingce.sms.service;

/**
 * @author Will
 */
public interface SmsService {

    /**
     * 发送短信
     *
     * @param fromPhone fromPhone
     * @param toPhone   toPhone
     * @param content   content
     * @return String
     */
    String send(String fromPhone, String toPhone, String content);

}

AliCloudSmsServiceImpl

代码语言:javascript
复制
package cn.com.codingce.sms.service.impl;

import cn.com.codingce.sms.factory.SmsHandleFactory;
import cn.com.codingce.sms.service.SmsService;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;

/**
 * 阿里云SMS实现
 *
 * @author Will
 */
@Service("ali")
public class AliCloudSmsServiceImpl implements SmsService {

    @Resource
    SmsHandleFactory smsHandleFactory;

    static final String ALI = "ali";

    @PostConstruct
    public void init() {
        smsHandleFactory.handlerMap.put(ALI, this);
    }

    @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";
    }
}

TxCloudSmsServiceImpl

代码语言:javascript
复制
package cn.com.codingce.sms.service.impl;

import cn.com.codingce.sms.factory.SmsHandleFactory;
import cn.com.codingce.sms.service.SmsService;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;

/**
 * 腾讯云SMS实现
 *
 * @author ma
 */
@Service("tx")
public class TxCloudSmsServiceImpl implements SmsService {

    @Resource
    SmsHandleFactory smsHandleFactory;

    static final String ALI = "tx";

    @PostConstruct
    public void init() {
        smsHandleFactory.handlerMap.put(ALI, this);
    }

    @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";
    }

}

YmxCloudSmsServiceImpl

代码语言:javascript
复制
package cn.com.codingce.sms.service.impl;

import cn.com.codingce.sms.factory.SmsHandleFactory;
import cn.com.codingce.sms.service.SmsService;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;

/**
 * 亚马逊云SMS实现
 *
 * @author Will
 */
@Service("ymx")
public class YmxCloudSmsServiceImpl implements SmsService {

    @Resource
    SmsHandleFactory smsHandleFactory;

    static final String ALI = "ymx";

    @PostConstruct
    public void init() {
        smsHandleFactory.handlerMap.put(ALI, this);
    }

    @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";
    }

}

mvn clean install 打入到本地仓库

使用方

新建项目

注意一定要是spring boot3.x版本以上的。

代码语言:javascript
复制
<dependency>
    <groupId>cn.com.codingce</groupId>
    <artifactId>codingce-sms-spring-boot-starter</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</dependency>

创建测试代码

代码语言:javascript
复制
package com.example.usersdkdemo.controller;

import cn.com.codingce.sms.config.SmsTemplate;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author Will
 */
@RestController
@RequestMapping("/codingce")
public class SmsController {

    @Resource
    private SmsTemplate smsTemplate;

    @RequestMapping("/sms")
    public String sms() {
        String fromPhone = "19922579993";
        String toPhone = "17694917213";
        String content = "志国,志国,今晚英雄联盟 十点 五缺一,收到请回复,over!";
        return smsTemplate.send(fromPhone, toPhone, content);
    }

}

当配置文件不进行配置时,默认走的是腾讯云的厂商

代码语言:javascript
复制
http://localhost:8080/codingce/sms

当配置好云厂商时,就会走配置的云厂商

代码语言:javascript
复制
http://localhost:8080/codingce/sms
代码语言:javascript
复制
sms:
  server:
    achieve:
      type: ali
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-09-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 后端码匠 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 【Starter 】Spring Boot 3.x 自定义封装Starter 实战
    • Starter 背景简介及作用
      • 什么是 Starter
      • 自定义 Starter 的背景
      • 自定义 Starter 封装规范
      • 新版Spring Boot3.X和旧版Spring Boot2.7之前自定义Starter区别
    • 自定义starter的步骤
      • 自定义 Starter 案例实战
        • 需求背景
        • 创建SpringBoot3.x项目
        • SmsAutoConfiguration
        • SmsProperties
        • SmsTemplate
        • SmsTypeEnum
        • SmsHandleFactory
        • SmsService
        • AliCloudSmsServiceImpl
        • TxCloudSmsServiceImpl
        • YmxCloudSmsServiceImpl
        • mvn clean install 打入到本地仓库
      • 使用方
        • 新建项目
        • 创建测试代码
        • 当配置文件不进行配置时,默认走的是腾讯云的厂商
        • 当配置好云厂商时,就会走配置的云厂商
    相关产品与服务
    短信
    腾讯云短信(Short Message Service,SMS)可为广大企业级用户提供稳定可靠,安全合规的短信触达服务。用户可快速接入,调用 API / SDK 或者通过控制台即可发送,支持发送验证码、通知类短信和营销短信。国内验证短信秒级触达,99%到达率;国际/港澳台短信覆盖全球200+国家/地区,全球多服务站点,稳定可靠。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档