前言:
java语言实现一个简单的策略模式进行多方短信发送的架子,可以在多个第三方进行切换,防止一方突然不可用等突发情况,以阿里云、互亿、创蓝为例。
1.注册第三方用以发送短信,以阿里云为例
在搜索框搜索找到短信服务控制台,这里会有快速教程,按步骤做即可,我这里是开通完成的样子,大概步骤如下,不清楚的可以百度教程。
1.登录阿里云账号。
2.搜索框搜索短信服务
3.开通短信服务并进入短信服务控制台,点击左边的栏目的国内消息
4.添加签名,等待审核
5.添加模版,等待审核
6.点击套餐包咨询栏目,购买一定的短信套餐
7.获取Access Key,需要将鼠标移动到右上角头像位置,不需要点击,显示Access Key管理并点击
8.进入管理台,点击创建Access Key即可获得
2.application.yml配置
2.1.signName需和这里名称保持一致
2.2.templateCode需和这里模板code保持一致
2.3.accessKey和secret从这里获取
没有的话就先创建
获取到各方配置文件需要的密钥后,在对应位置进行配置,如果后续某个第三方发送失败,一定检查配置文件是否正确。
lyy:
sms:
# 默认启用
enable: huyi
aliyun:
# 签名
signName: 1111
# 模板CODE
templateCode: 11111
# 个人accessKey
accessKey: 11111
# 个人secret
secret: 11111
huyi:
signName:
templateCode:
accessKey:
secret:
chuanglan:
signName:
templateCode:
accessKey:
secret:
3.创建各方实体类
prefix 值注意和配置文件层级保持一致
3.1.互亿
@Data
@Component
@ConfigurationProperties(prefix = "lyy.sms.huyi")
public class AliyunProperties {
private String signName;
private String templateCode;
private String accessKey;
private String secret;
}
3.2.创蓝
@Data
@Component
@ConfigurationProperties(prefix = "lyy.sms.chuanglan")
public class ChuanglanProperties {
private String signName;
private String templateCode;
private String accessKey;
private String secret;
}
3.3.阿里云
@Data
@Component
@ConfigurationProperties(prefix = "lyy.sms.aliyun")
public class HuyiProperties {
private String signName;
private String templateCode;
private String accessKey;
private String secret;
}
4.自定义一个注解
这个注解的是为了给各方实现类起一个别名,后续用这个别名与配置文件中enable属性值对应。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface SmsTypeName {
String value();
}
5.发送短信方法接口和实现类
5.1.接口
发送短信的方法,参数一:接收方的电话号码;参数二:发送的验证码
public interface TemplateService {
void sendMsg(String mobile, String code);
}
5.2.实现类
具体的发送代码,百度都有现成的,或者阅读官方文档。
这里使用@ConfigurationProperties注解,在启动时,springBoot会自动注入属性值,
然后用@Autowired注解自动装配对应的第三方的实体类。
@SmsTypeName(“aliyun”),别名需与配置文件的一致。不然影响到第6步策略类的加载存储的key与配置文件的enable属性对应不上,就无法获取到对应的实现类。
这里第三方的实现类的代码都是使用的阿里云的,只需要验证是否能通过配置文件改变发送短信具体的实现类即可,大家也可以替换为对应第三方的代码。
5.2.1.阿里云
@Component
@SmsTypeName("aliyun")
public class AliyunTemplate implements TemplateService {
@Autowired
private AliyunProperties properties;
@Override
public void sendMsg(String mobile, String code) {
try {
Config config = new Config()
// 必填,您的 AccessKey ID
.setAccessKeyId(properties.getAccessKey())
// 必填,您的 AccessKey Secret
.setAccessKeySecret(properties.getSecret());
// 访问的域名
config.endpoint = "dysmsapi.aliyuncs.com";
Client client = new Client(config);
SendSmsRequest sendSmsRequest = new SendSmsRequest()
.setPhoneNumbers(mobile)
.setSignName(properties.getSignName())
.setTemplateCode(properties.getTemplateCode())
.setTemplateParam("{\"code\":\"" + code + "\"}");
// 复制代码运行请自行打印 API 的返回值
SendSmsResponse response = client.sendSms(sendSmsRequest);
if ("OK".equals(response.getBody().getCode())) {
System.out.println("aliyun验证码发送成功");
}
System.out.println(response.getBody().getMessage());
} catch (Exception e) {
e.printStackTrace();
}
}
}
5.2.2.创蓝
@Component
@SmsTypeName("chuanglan")
public class ChuanglanTemplate implements TemplateService {
@Autowired
private ChuanglanProperties properties;
@Override
public void sendMsg(String mobile, String code) {
try {
Config config = new Config()
// 必填,您的 AccessKey ID
.setAccessKeyId(properties.getAccessKey())
// 必填,您的 AccessKey Secret
.setAccessKeySecret(properties.getSecret());
// 访问的域名
config.endpoint = "dysmsapi.aliyuncs.com";
Client client = new Client(config);
SendSmsRequest sendSmsRequest = new SendSmsRequest()
.setPhoneNumbers(mobile)
.setSignName(properties.getSignName())
.setTemplateCode(properties.getTemplateCode())
.setTemplateParam("{\"code\":\"" + code + "\"}");
// 复制代码运行请自行打印 API 的返回值
SendSmsResponse response = client.sendSms(sendSmsRequest);
if ("OK".equals(response.getBody().getCode())) {
System.out.println("chuanglan验证码发送成功");
}
System.out.println(response.getBody().getMessage());
} catch (Exception e) {
e.printStackTrace();
}
}
}
5.2.3.互亿
@Component
@SmsTypeName("huyi")
public class HuyiTemplate implements TemplateService {
@Autowired
private HuyiProperties properties;
@Override
public void sendMsg(String mobile, String code) {
try {
Config config = new Config()
// 必填,您的 AccessKey ID
.setAccessKeyId(properties.getAccessKey())
// 必填,您的 AccessKey Secret
.setAccessKeySecret(properties.getSecret());
// 访问的域名
config.endpoint = "dysmsapi.aliyuncs.com";
Client client = new Client(config);
SendSmsRequest sendSmsRequest = new SendSmsRequest()
.setPhoneNumbers(mobile)
.setSignName(properties.getSignName())
.setTemplateCode(properties.getTemplateCode())
.setTemplateParam("{\"code\":\"" + code + "\"}");
// 复制代码运行请自行打印 API 的返回值
SendSmsResponse response = client.sendSms(sendSmsRequest);
if ("OK".equals(response.getBody().getCode())) {
System.out.println("huyi验证码发送成功");
}
System.out.println(response.getBody().getMessage());
} catch (Exception e) {
e.printStackTrace();
}
}
}
6.策略类
启动时加载,存储带有SmsTypeName注解的类,以SmsTypeName的值为key,以对应的实现类为value存入一个map并提供一个get方法根据key获取value。
@Component
public class SmsTypeHandler implements ApplicationContextAware {
private static HashMap<String, TemplateService> TEMPLATE_MAP = new HashMap<>();
/**
* 启动时加载发送短信策略类
*
* @param applicationContext
* @throws BeansException
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
applicationContext.getBeansOfType(TemplateService.class).values().forEach(temp -> {
String type = temp.getClass().getAnnotation(SmsTypeName.class).value();
TEMPLATE_MAP.put(type, temp);
});
}
public static TemplateService getTemplateByType(String type) {
TemplateService templateService = TEMPLATE_MAP.get(type);
return templateService;
}
}
7.测试类
获取配置文件enable的属性值,然后调用SmsTypeHandler类的getTemplateByType方法即可。110换成自己的手机号码,检查有没有收到自己设置的验证码即可。
@RunWith(SpringRunner.class)
@SpringBootTest(classes = AppServerApplication.class)
public class TemplateTest {
@Autowired
private SmsTypeHandler smsTypeHandler;
@Value("${tanhua.sms.enable}")
private String smsType;
//阿里云发短信测试
@Test
public void testSendSms() {
smsTypeHandler.getTemplateByType(smsType).sendMsg("110", "123456");
}
}
这是一个简单的架子,大家可以尽情扩展。