目标
- 掌握spring boot框架的搭建方法
- 你能够使用阿里大于发送短信
- 运用springboot、阿里大于和activemq开发短信微服务
- 完成品优购用户注册功能(短信验证码认证)
用户中心–> 用户服务–>activeMQ–> 短信微服务–> 阿里大于
1. Spring Boot入门
1. 什么是Spring Boot
spring的配置是重量级的,另外项目依赖管理麻烦。
spring boot目的是帮助开发者更容易的创建基于spring的应用程序和服务,更快入门体验。
特性:
- 为基于spring的开发提供更快的入门体验
- 开箱即用
- 提供一些大型项目常见的非功能性特性,如嵌入式服务器、安全、指标,健康监测、外部配置等。
- springboot只是提供快速使用spring的方式。
2. 入门demo
1. 起步依赖
创建maven工程springboot_demo(打包方式为jar包)
在pom.xml添加依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.0.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
2. 变更jdk版本
默认版本为1.6,如果想用1.8,pom.xml中添加配置
<properties>
<java.version>1.8</java.version>
</properties>
3. 引导类
只需要创建一个引导类
package cn.itcast.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class,args);
}
}
简单解释:@SpringBootApplication
其实就是以下三个注解的总和
@Configuration
:用于定义一个java配置类application-solr.xml@EnableAutoConfiguration
:SpringBoot会自动根据jar包的依赖自动配置项目@ComponentScan
:告诉spring哪个package是用注解标识的类,会被spring自动扫描并且装入bean容器
4. Spring MVC实现Hello world的输出
现在开始使用spring MVC框架,实现json数据的输出。直接写Controller类
@RestController
public class HelloWorldController {
@RequestMapping("/info")
public String info(){
return "HelloWorld!!";
}
}
测试结果
5. 修改tomcat启动端口
在src/main/resources下创建application.properties
server.port=8088
6. 读取配置文件信息
在src/main/resources下的application.properties增加配置
url=http://www.baidu.com
要在类中读取这个配置信息
import org.springframework.core.env.Environment;
...
@RestController
public class HelloWorldController {
@Autowired
private Environment env;
@RequestMapping("/info")
public String info(){
return "HelloWorld!! "+env.getProperty("url");
}
}
7. 热部署
能否在修改代码后不重启就能生效?在pom.xml中添加如下配置即可,称为热部署
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
3. Spring Boot和ActiveMQ整合
1. 使用内嵌服务
- 在pom.xml中引入ActiveMQ起步依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-activemq</artifactId>
</dependency>
- 创建消息生产者
@RestController
public class QueueController {
@Autowired
private JmsMessagingTemplate jmsMessagingTemplate;
@RequestMapping("/send")
public void send(String text){
jmsMessagingTemplate.convertAndSend("itcast",text);
}
}
- 创建消息消费者
@Component
public class Consumer {
@JmsListener(destination = "itcast")
public void readMessage(String text){
System.out.println("接收到的消息:"+text);
}
}
测试:启动服务后,浏览器执行
http://localhost:8088/send?text=abc
可以在控制台输出消息提示,内置了ActiveMQ的服务,不用单独启动也可以执行
2. 使用外部服务
在src/main/resources下的application.properties增加配置,指定ActiveMQ的地址
spring.activemq.broker-url=tcp://192.168.25.135:61616
运行后,会在activeMQ看到发送的queue
3. 发送Map消息
- 修改QueueController.java
@RequestMapping("/sendMap")
public void sendMap(){
Map map = new HashMap();
map.put("mobile","13510733521");
map.put("content","恭喜您被彩蛋砸中了");
jmsMessagingTemplate.convertAndSend("itcast_map",map);
}
- 修改Consumer.java
@JmsListener(destination = "itcast_map")
public void readMap(Map map){
System.out.println(map);
}
2. 短信发送平台-阿里大于
1. 简介
阿里云旗下产品,融合三大运营商的通信能力,提供通信和数据相关服务
2. 准备工作
- 注册账户
- 登录
- 使用短信服务
- 申请签名(一般两个小时审核时间)
- 创建accessKey
3. SDK安装
从阿里云官网下载demo工程
两个依赖jar包的maven安装
mvn install:install-file -DgroupId=com.aliyun -DartifactId=aliyun-java-sdk-dysmsa pi -Dversion=1.0.0-SANPSHOT -Dpackaging=jar -Dfile=e:\aliyun-java-sdk-dysmsapi-1.0.0-SANPSHOT.jar
mvn install:install-file -DgroupId=com.aliyun -DartifactId=aliyun-java-sdk-core - Dversion=3.2.3 -Dpackaging=jar -Dfile=e:\aliyun-java-sdk-core-3.2.3.jar
本地jar包安装后,alicom-dysms-api工程引入依赖
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-dysmsapi</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
<version>3.2.5</version>
</dependency>
4. 发送短信测试
- 打开SmsDemo
替换下列几处代码
// TODO 此处需要替换成开发者自己的AK(在阿里云访问控制台寻找)
static final String accessKeyId = "xxxx";
static final String accessKeySecret = "xxxx";
手机号、短信签名和模板好
//必填:待发送手机号
request.setPhoneNumbers("xxxxx");
//必填:短信签名-可在短信控制台中找到
request.setSignName("xxxx");
//必填:短信模板-可在短信控制台中找到
request.setTemplateCode("xxxx");
//可选:模板中的变量替换JSON串,如模板内容为"亲爱的${name},您的验证码为${code}"时,此处的值为
request.setTemplateParam("{ \"code\":\"xxx\"}");
执行main方法,接收短信
3. 短信微服务
1. 需求分析
构建一个通用的短信发送服务(独立于品优购),接收activeMQ的消息(Map类型),消息包括手机号(mobile)、短信模板号(template_code)、签名(sign_name)、参数字符串(param)。
2. 代码实现
1. 工程搭建
- 创建工程itcast_sms(jar工程),POM文件引入依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.0.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-activemq</artifactId>
</dependency>
<!--阿里大于-->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-dysmsapi</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
<version>3.2.5</version>
</dependency>
</dependencies>
- 创建引导类
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class,args);
}
}
- 创建配置文件application.properties
server.port=9003
spring.activemq.broker-url=tcp://192.168.25.128:61616
accessKeyId=xxxx
accessKeySecret=xxxx
2. 短信工具类
参照之前的短信demo创建短信工具类
@Component
public class SmsUtil {
//产品名称:云通信短信API产品,开发者无需替换
static final String product = "Dysmsapi";
//产品域名,开发者无需替换
static final String domain = "dysmsapi.aliyuncs.com";
@Autowired
private Environment environment;
// TODO 此处需要替换成开发者自己的AK(在阿里云访问控制台寻找)
// static final String accessKeyId = "LTAIAVy4oL7adI4W";
// static final String accessKeySecret = "pF0zNhQQQxYAtxbEYuRbACdvLCz47j";
public SendSmsResponse sendSms(String mobile,String template_code,String sign_name,String param) throws ClientException {
String accessKeyId = environment.getProperty("accessKeyId");
String accessKeySecret = environment.getProperty("accessKeySecret");
//可自助调整超时时间
System.setProperty("sun.net.client.defaultConnectTimeout", "10000");
System.setProperty("sun.net.client.defaultReadTimeout", "10000");
//初始化acsClient,暂不支持region化
IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", accessKeyId, accessKeySecret);
DefaultProfile.addEndpoint("cn-hangzhou", "cn-hangzhou", product, domain);
IAcsClient acsClient = new DefaultAcsClient(profile);
//组装请求对象-具体描述见控制台-文档部分内容
SendSmsRequest request = new SendSmsRequest();
//必填:待发送手机号
request.setPhoneNumbers(mobile);
//必填:短信签名-可在短信控制台中找到
request.setSignName(sign_name);
//必填:短信模板-可在短信控制台中找到
request.setTemplateCode(template_code);
//可选:模板中的变量替换JSON串,如模板内容为"亲爱的${name},您的验证码为${code}"时,此处的值为
request.setTemplateParam(param);
//选填-上行短信扩展码(无特殊需求用户请忽略此字段)
//request.setSmsUpExtendCode("90997");
//可选:outId为提供给业务方扩展字段,最终在短信回执消息中将此值带回给调用者
request.setOutId("yourOutId");
//hint 此处可能会抛出异常,注意catch
SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request);
return sendSmsResponse;
}
public QuerySendDetailsResponse querySendDetails(String mobile,String bizId) throws ClientException {
String accessKeyId = environment.getProperty("accessKeyId");
String accessKeySecret = environment.getProperty("accessKeySecret");
//可自助调整超时时间
System.setProperty("sun.net.client.defaultConnectTimeout", "10000");
System.setProperty("sun.net.client.defaultReadTimeout", "10000");
//初始化acsClient,暂不支持region化
IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", accessKeyId, accessKeySecret);
DefaultProfile.addEndpoint("cn-hangzhou", "cn-hangzhou", product, domain);
IAcsClient acsClient = new DefaultAcsClient(profile);
//组装请求对象
QuerySendDetailsRequest request = new QuerySendDetailsRequest();
//必填-号码
request.setPhoneNumber(mobile);
//可选-流水号
request.setBizId(bizId);
//必填-发送日期 支持30天内记录查询,格式yyyyMMdd
SimpleDateFormat ft = new SimpleDateFormat("yyyyMMdd");
request.setSendDate(ft.format(new Date()));
//必填-页大小
request.setPageSize(10L);
//必填-当前页码从1开始计数
request.setCurrentPage(1L);
//hint 此处可能会抛出异常,注意catch
QuerySendDetailsResponse querySendDetailsResponse = acsClient.getAcsResponse(request);
return querySendDetailsResponse;
}
}
3. 消息监听类
创建SmsListener.java
@Component
public class SmsListener {
@Autowired
private SmsUtil smsUtil;
@JmsListener(destination = "sms")
public void sendMsg(Map<String,String> map){
try {
SendSmsResponse response = smsUtil.sendSms(map.get("mobile"),
map.get("template_code"),
map.get("sign_name"),
map.get("param"));
System.out.println("code: "+response.getCode());
System.out.println("message: "+response.getMessage());
} catch (ClientException e) {
e.printStackTrace();
}
}
}
3. 代码测试
修改springboot-demo的QueueController.java
@RequestMapping("/sendsms")
public void sendsms(){
Map map = new HashMap();
map.put("mobile","17816872186");
map.put("template_code","SMS_141616649");
map.put("sign_name","八零大蔬");
map.put("param","{ \"code\":\"123\"}");
jmsMessagingTemplate.convertAndSend("sms",map);
}
启动itcast_sms
启动springboot-demo
地址栏输入localhost:8088/sendsms.do
观察控制台输出,短信也成功发送到手机上。
4. 用户注册
1. 需求分析
完成用户注册功能
2. 工程搭建
1. 用户服务接口层
- 创建user-interface(jar)
- 引入pojo依赖
2. 用户服务实现层
- 创建pinyougou-user-service(war)
- 引入spring dubbox activeMQ相关依赖,引入依赖(pinyougou-user-interface pinyougou-dao pinyougou-common),运行端口为9006
- 添加web.xml
- 创建spring配置文件applicationContext-service.xml和applicationContext-tx.xml
<dubbo:protocol name="dubbo" port="20886"></dubbo:protocol>
<dubbo:application name="pinyougou-user-service"/>
<dubbo:registry address="zookeeper://192.168.25.128:2181"/>
<dubbo:annotation package="com.pinyougou.user.service.impl" />
3. 用户中心web层
创建war工程 pinyougou_user_web 我们将注册功能放入此工程
- 添加web.xml
- 引入依赖user-interface、spring相关依赖,tomcat运行端口为9106
- 添加spring配置文件
- 拷贝静态原型页面register.html以及相关资源
3. 基本注册功能实现
1. 生成和拷贝代码
2. 后端服务实现层
@Override
public void add(TbUser user) {
user.setCreated(new Date());
user.setUpdated(new Date());
user.setPassword(DigestUtils.md5DigestAsHex(user.getPassword().getBytes()));
userMapper.insert(user);
}
3. 前端控制层
修改userController.js
app.controller('userController' ,function($scope,$controller ,userService){
// 注册用户
$scope.reg = function () {
// 比较两次输入的密码是否一致
if($scope.password!=$scope.entity.password){
alert("两次输入密码不一致,请重新输入");
$scope.entity.password = "";
$scope.password = "";
return;
}
// 新增
userService.add($scope.entity).success(
function (response) {
alert(response.message);
}
)
}
});
4. 修改页面
修改页面register.html,引入js
<script src="plugins/angularjs/angular.min.js"></script>
<script type="text/javascript" src="js/base.js"></script>
<script type="text/javascript" src="js/service/userService.js"></script>
<script type="text/javascript" src="js/controller/userController.js"></script>
指令
<body ng-app="pinyougou" ng-controller="userController">
绑定表单
<form class="sui-form form-horizontal">
<div class="control-group">
<label class="control-label">用户名:</label>
<div class="controls">
<input type="text" ng-model="entity.username" placeholder="请输入你的用户名" class="input-xfat input-xlarge">
</div>
</div>
<div class="control-group">
<label for="inputPassword" class="control-label">登录密码:</label>
<div class="controls">
<input type="password" ng-model="entity.password" placeholder="设置登录密码" class="input-xfat input-xlarge">
</div>
</div>
<div class="control-group">
<label for="inputPassword" class="control-label">确认密码:</label>
<div class="controls">
<input type="password" ng-model="password" placeholder="再次确认密码" class="input-xfat input-xlarge">
</div>
</div>
<div class="control-group">
<label class="control-label">手机号:</label>
<div class="controls">
<input type="text" ng-model="entity.phone" placeholder="请输入你的手机号" class="input-xfat input-xlarge">
</div>
</div>
<div class="control-group">
<label for="inputPassword" class="control-label">短信验证码:</label>
<div class="controls">
<input type="text" placeholder="短信验证码" class="input-xfat input-xlarge"> <a href="#">获取短信验证码</a>
</div>
</div>
<div class="control-group">
<label for="inputPassword" class="control-label"> </label>
<div class="controls">
<input name="m1" type="checkbox" value="2" checked=""><span>同意协议并注册《品优购用户协议》</span>
</div>
</div>
<div class="control-group">
<label class="control-label"></label>
<div class="controls btn-reg">
<a class="sui-btn btn-block btn-xlarge btn-danger" ng-click="reg()" target="_blank">完成注册</a>
</div>
</div>
</form>
4. 注册判断短信验证码
1. 实现思路
-
点击发送验证码的逻辑:
- 生成一个6位随机数字
- 将生成的验证码存入redis,以手机号为key,以验证码为值
- 将短信内容发送到activeMQ
-
校验验证码是否正确
用户注册前要进行校验
获取用户填写的验证码,与redis中的验证码进行比较
2. 生成验证码
- 修改user-interface工程UserService.java,增加方法
- 修改user-service的UserServiceImpl.java
@Override
public void createSmsCode(String phone) {
// 1. 生成一个6位随机数(验证码)
String smscode = (long)(Math.random()*1000000)+"";
System.out.println("验证码:"+smscode);
// 2. 验证码放入redis
redisTemplate.boundHashOps("smscode").put(phone,smscode);
// 3. 将短信内容发送给activeMQ
// TODO
}
- 在pinyougou-common添加工具类PhoneFormatCheckUtils.java,用于验证手机号
- 修改pinyougou-user-web的UserController.java
public Result sendCode(String phone){
if(!PhoneFormatCheckUtils.isPhoneLegal(phone)){
return new Result(false,"手机格式不正确");
}
try {
userService.createSmsCode(phone);
return new Result(true,"验证码发送成功");
} catch (Exception e) {
e.printStackTrace();
return new Result(false,"验证码发送失败");
}
}
- pinyougou-user-web的userService.js
// 发送验证码
this.sendCode=function (phone) {
return $http.get('../user/sendCode.do?phone='+phone);
}
- user-web的userController.js
// 发送验证码
$scope.sendCode = function () {
if($scope.entity.phone==null || $scope.entity.phone===""){
alert("请填写手机号!");
return;
}
userService.sendCode($scope.entity.phone).success(
function (response) {
alert(response.message);
}
)
}
- 修改页面register.html
<a href="#" ng-click="sendCode()">获取短信验证码</a>
3. 用户注册判断验证码
- 修改user-interface的UserService.java
// 校验验证码
public boolean checkSmsCode(String phone,String code);
- 修改user-service的UserServiceImpl.java
@Override
public boolean checkSmsCode(String phone, String code) {
String systomcode = (String) redisTemplate.boundHashOps("smscode").get(phone);
if(systomcode==null){
return false;
}
if(!code.equals(systomcode)){
return false;
}
return true;
}
- 修改user-web的UserController.java
@RequestMapping("/add")
public Result add(@RequestBody TbUser user,String smscode){
boolean checkSmsCode = userService.checkSmsCode(user.getPhone(), smscode);
if(!checkSmsCode){
return new Result(false, "验证码输入错误!");
}
try {
userService.add(user);
return new Result(true, "增加成功");
} catch (Exception e) {
e.printStackTrace();
return new Result(false, "增加失败");
}
}
- 修改userService.js
//增加
this.add=function(entity,smscode){
return $http.post('../user/add.do?smscode='+smscode,entity );
}
- 修改UserController.js
userService.add($scope.entity,$scope.smscode).success(
function (response) {
alert(response.message);
}
)
- 修改页面,绑定变量
<input type="text" ng-model="smscode" placeholder="短信验证码" class="input-xfat input-xlarge"> <a href="#" ng-click="sendCode()">获取短信验证码</a>
4. 短信验证码发送到手机
- 在user-service添加配置文件applicationContext-activemq.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jms="http://www.springframework.org/schema/jms"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/jms
http://www.springframework.org/schema/jms/spring-jms.xsd">
<!-- 真正可以产生Connection的ConnectionFactory,由对应的 JMS服务厂商提供-->
<bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://192.168.25.128:61616"/>
</bean>
<!-- Spring用于管理真正的ConnectionFactory的ConnectionFactory -->
<bean id="connectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory">
<!-- 目标ConnectionFactory对应真实的可以产生JMS Connection的ConnectionFactory -->
<property name="targetConnectionFactory" ref="targetConnectionFactory"/>
</bean>
<!-- Spring提供的JMS工具类,它可以进行消息发送、接收等 -->
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<!-- 这个connectionFactory对应的是我们定义的Spring提供的那个ConnectionFactory对象 -->
<property name="connectionFactory" ref="connectionFactory"/>
</bean>
<!--这个是队列目的地,点对点的 文本信息-->
<bean id="smsDestination" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg value="sms"/>
</bean>
</beans>
- 修改user-service的UserServiceImpl.java
@Autowired
private JmsTemplate jmsTemplate;
@Autowired
private Destination smsDestination;
@Value("${template_code}")
private String template_code;
@Value("${sign_name}")
private String sign_name;
@Override
public void createSmsCode(String phone) {
// 1. 生成一个6位随机数(验证码)
String smscode = (long)(Math.random()*1000000)+"";
System.out.println("验证码:"+smscode);
// 2. 验证码放入redis
redisTemplate.boundHashOps("smscode").put(phone,smscode);
// 3. 将短信内容发送给activeMQ
jmsTemplate.send(smsDestination, new MessageCreator() {
@Override
public Message createMessage(Session session) throws JMSException {
MapMessage message = session.createMapMessage();
message.setString("mobile",phone);// 手机号
message.setString("template_code",template_code);
message.setString("sign_name",sign_name);
Map map = new HashMap();
map.put("number",smscode);
message.setString("param", JSON.toJSONString(map));
return message;
}
});
}
- 在pinyougou-common的properties目录创建配置文件sms.properties
template_code=SMS_141616649
sign_name=\u516b\u96f6\u5927\u852c