原本一个很简单的短信推送,被自己作死搞的好复杂,不过主要是在加强java的学习和aliyun的一些服务学习吧。
这次也是用了自定义注解,并且利用了fastjson过滤器。
!!!!!!注意注意⚠️,新版阿里云短信mns版本博客已经更新,本博客只做技术分享,如果你是想看阿里云短信相关内容,可以查看
这篇博客
如果你是想学习反射,自定义注解,fastjson的过滤器,可以看本文博客。
使用的jar包:
aliyun-java-sdk-core-3.0.8.jar
aliyun-java-sdk-sms-3.0.0-rc1.jar
fastjson-1.2.4.jar
com.againfly.util.aliyun ├─annotation │ └SmsParame.java --一个短信注解 ├─sms │ ├filter │ │ └SmsParameFilter.java --fastjson短信模版字段过滤器 │ │ │ ├template │ │ ├SmsTemplate.java --模版接口,定义的短信模版需要实现该接口 │ │ └VerificationTemplate.java--验证码模版 │ ├SmsResponse.java --短信推送的返回值 │ └SmsUtil.java --外部调用的工具类 └AliyunConst.java --阿里云服务配置变量值
首先我们要先知道阿里的短信推送,他们需要什么:
- 自己在阿里云控制台开通短信推送服务。
- 知道自己阿里云的ACCESS_KEY和ACCESS_SECRET
- 申请签名
- 申请短信模版
只有当签名和短信模版都审核通过后才能使用,短信模版有一个唯一码,SMS_CODE,短信模版当中会设置若干参数,例如${time},${name}。比如我这里的短信模版内容是:
尊敬的${name},您的验证码为:${code},请在${time}内使用。
官方实例很简单,但是也够用了,如果你只是想发送短信,那就直接看官方的文档吧。
本文纯粹是瞎操心,简单的东西复杂化,旨在提高java的了解。
上一篇文章已经说过了自定义注解,对fastjson也做了一个过滤器的说明,如果是想了解fastjson的话可以访问这篇文章。
注解代码:里面什么都没有,纯粹只是为了标记某个字段是不是被本注解标记了。
package com.againfly.util.aliyun.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @Target(ElementType.FIELD):该注解只能注解在字段上 * @Retention(RetentionPolicy.RUNTIME):该注解可以保留到运行期,可以通过反射找到本注解值 * @Documented:表明这个注解应该被 javadoc工具记录 * @author Jecced * 本注解用在阿里短信模版参数注释,被标记则为模版参数 */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface SmsParame { }
fastjson过滤器类:实现了静态内部类的单例模式,以及利用反射,来确定一个字段是否拥有上面的SmsParame注解,如果有则返回true。
package com.againfly.util.aliyun.sms.filter; import java.lang.reflect.Field; import com.againfly.util.aliyun.annotation.SmsParame; import com.alibaba.fastjson.serializer.PropertyFilter; public class SmsParameFilter implements PropertyFilter{ private static class SingletonHolder{ private static final PropertyFilter INSTANCE = new SmsParameFilter(); } public static PropertyFilter getInstance(){ return SingletonHolder.INSTANCE; } @Override public boolean apply(Object object, String name, Object value) { Class<? extends Object> clazz = object.getClass(); try { Field field = clazz.getDeclaredField(name); SmsParame annotation = field.getAnnotation(SmsParame.class); if(null != annotation) return true; } catch (NoSuchFieldException | SecurityException e) { return false; } return false; } }
短信模版抽象类:具体实现了一个parameJson的方法,并且禁止让子类重写。并且定义了两个方法,分别是获取这个短信模版使用的SMS_CODE和应用签名。这两个基本上在申请短信模版的时候就固定了,所以让子类实现这两个方法,让他们分别返回这两个字段即可。
package com.againfly.util.aliyun.sms.template; import com.againfly.util.aliyun.sms.filter.SmsParameFilter; import com.alibaba.fastjson.JSON; public abstract class SmsTemplate { public final String paramJson() { return JSON.toJSONString(this, SmsParameFilter.getInstance()); } public abstract String getTemplateCode(); public abstract String getSmsSignName(); }
现在我们申请了一个短信验证码的短信模版:让他继承短信模版抽象类,并实现抽象类中的两个方法。
在这个模版中我们可以定义若干个自定义字段,这些字段分别代表着短信模版中定义的字段名称,并且这些字段是需要使用@SmsParmae注解标注,这些标注可以让抽象类中那个具体实现的方法获取他们,并且返回json字符串。
我申请的短信模版内容是:尊敬的${name},您的验证码为:${code},请在${time}内使用。
所以验证码模版中有name字段,code字段,time字段三个,并且使用了@SmsParmae注解
package com.againfly.util.aliyun.sms.template; import com.againfly.util.aliyun.annotation.SmsParame; public class VerificationTemplate extends SmsTemplate{ public static final String SMS_TEMPLATE_CODE = "/****you sms code****/"; public static final String SMS_SIGN_NAME = "/****you sign name****/"; @SmsParame private String name; @SmsParame private String code; @SmsParame private String time; public VerificationTemplate(String name, String code) { this.name = name; this.code = code; this.time = "30分钟"; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getCode() { return code; } public void setCode(String code) { this.code = code; } public String getTime() { return time; } public void setTime(String time) { this.time = time; } @Override public String toString() { return "VerificationTemplate [name=" + name + ", code=" + code + ", time=" + time + ", paramJson()=" + paramJson() + "]"; } public static void main(String[] args) { SmsTemplate template = new VerificationTemplate("ankaang", "12312"); System.out.println(template); } @Override public String getTemplateCode() { return VerificationTemplate.SMS_TEMPLATE_CODE; } @Override public String getSmsSignName() { return VerificationTemplate.SMS_SIGN_NAME; } }
短信内容返回类:
package com.againfly.util.aliyun.sms; import com.aliyuncs.exceptions.ClientException; import com.aliyuncs.sms.model.v20160927.SingleSendSmsResponse; public class SmsResponse { private boolean success; private String requestId; private String errCode; private String errMsg; private String model; public SmsResponse(ClientException e) { requestId = e.getRequestId(); errCode = e.getErrCode(); errMsg = e.getErrMsg(); success = false; } public SmsResponse(SingleSendSmsResponse r) { requestId = r.getRequestId(); model = r.getModel(); success = true; } /**此处省略get和set方法**/ @Override public String toString() { return "SmsResponse [success=" + success + ", requestId=" + requestId + ", errCode=" + errCode + ", errMsg=" + errMsg + ", model=" + model + "]"; } }
真正被外部使用的SMSUtil
package com.againfly.util.aliyun.sms; import com.againfly.util.aliyun.AliyunConst; import com.againfly.util.aliyun.sms.template.SmsTemplate; import com.aliyuncs.DefaultAcsClient; import com.aliyuncs.IAcsClient; import com.aliyuncs.exceptions.ClientException; import com.aliyuncs.exceptions.ServerException; import com.aliyuncs.profile.DefaultProfile; import com.aliyuncs.profile.IClientProfile; import com.aliyuncs.sms.model.v20160927.SingleSendSmsRequest; import com.aliyuncs.sms.model.v20160927.SingleSendSmsResponse; public class SmsUtil { /** * 发送短信 * @param template 短信模版,模版code,签名,模版参数都在模版中,模版需要继承抽象类:SmsTemplate * @param mobile 手机号码,可以是多个手机号码,但是手机号码需要使用英文半角逗号隔开;手机号数量不能超过100,需要控制 * @author Jecced * @return */ public static SmsResponse SendSms(SmsTemplate template, String mobile) { IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", AliyunConst.ACCESS_KEY, AliyunConst.ACCESS_SECRET); try { DefaultProfile.addEndpoint("cn-hangzhou", "cn-hangzhou", "Sms", "sms.aliyuncs.com"); IAcsClient client = new DefaultAcsClient(profile); SingleSendSmsRequest request = new SingleSendSmsRequest(); request.setSignName(template.getSmsSignName()); request.setTemplateCode(template.getTemplateCode()); request.setParamString(template.paramJson()); request.setRecNum(mobile); SingleSendSmsResponse httpResponse = client.getAcsResponse(request); return new SmsResponse(httpResponse); } catch (ServerException e) { return new SmsResponse(e); } catch (ClientException e) { return new SmsResponse(e); } } }
阿里云服务需要的一些权限KEY存放的静态字段类:
package com.againfly.util.aliyun; public class AliyunConst { public static final String ACCESS_KEY = "/****you access key****/"; public static final String ACCESS_SECRET = "/****you access secret****/"; }
==============================================
最后当然是要来看使用效果的。。。对吧,这个不能忘。
SmsUtil.SendSms(new VerificationTemplate("ankang", "1212"), "155xxxxxxx");
最新评论