Spring,SpringMVC,Mybatis之Web---30
秒杀详情页流程逻辑: ①获取系统的标准时间 ②根据已经设置好的秒杀开始时间和秒杀结束时间判断当前系统时间是不是在设定好的时间范围之内, 2.1如果不在,秒杀结束,否则秒杀开始倒计时; 2.2如果刚好在秒杀时间范围之内则直接开始秒杀 ③推送秒杀地址,以及倒计时时间 ④点击秒杀,触发秒杀操作 ⑤返回秒杀结果。
Restful规范:一种优雅的URI的表达方式(资源的状态和状态转移)
Restful规范
(1)GET -- 查询操作 (2)POST -- 添加、修改操作 (3)PUT -- 修改操作 (4)DELETE -- 删除操作
POST PUT 区别是:POST非幂等操作 PUT是幂等操作 ,两者差别不是很大url设计:/模块/资源/{标示}/集合1/....
例子:/user/{uid}/frends->好友列表 /user/{uid}/followes->关注者列表。url可读性比较好
秒杀API的URL设计
====================================================================================================================================
SpringMVC
Spring运行流程:
注解映射技巧
SpringMVC案例:
返回Json数据:
Cookie访问:
===================================================================================================================
整合配置SpringMVC
配置web.xml
@web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.1" xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_3_1.xsd">
<!-- 修改servlet版本为3.1 -->
<!-- 配置DispatcherSerlet -->
<servlet>
<servlet-name>seckill-dispatcher</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<!-- 配置springMVC需要加载的配置文件
spring-dao.xml,spring-service.xml,spring-web.xml
整合顺序:Mybatis -> spring -> springMVC
-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/spring-*.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>seckill-dispatcher</servlet-name>
<!-- 默认匹配所有的请求 -->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
配置spring-web.xml
@spring-web.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util" xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.2.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd ">
<!--配置SpringMVC -->
<!-- 1:开启S配额ringMVC注解模式 -->
<!-- 简化配置:
(1) 自动注册 DefaultAnnotationHandlerMapping,AnnotationMethodHandlerAdapter;
(2) 提供一系列功能:数据绑定,数字和日期的format @NumberFormat,@DataTimeFormat
,xml,json默认读写支持. -->
<mvc:annotation-driven />
<!-- 2:静态资源默认servlet配置
1):加入对静态资源的处理:js,gif,png
2):允许使用"/"做整体影射 -->
<mvc:default-servlet-handler />
<!-- 3:配置jsp 显示ViewResolver -->
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass"
value="org.springframework.web.servlet.view.JstlView" />
<property name="prefix" value="/WEB-INF/jsps/" />
<property name="suffix" value=".jsp" />
</bean>
<!-- 4:扫描web相关的bean-->
<context:component-scan base-package="org.seckill.web"/>
</beans>
=====================================================================================================================
Controller代码
@SeckillController.java
@Controller//@Service @Component,放入springMVC容器中
@RequestMapping("/seckill")//url:/模块/资源/{id}/细分, /seckill/list
public class SeckillController {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private SeckillService seckillService;
@RequestMapping(name="/list", method=RequestMethod.GET)
public String list(Model model){
//获取列表页
List<Seckill> list = seckillService.getSeckillList();
model.addAttribute("list", list);
//list.jsp+model=ModelAndView
return "list";///WEB-INF/jsp/list.jsp
}
@RequestMapping(value = "/{seckillId}/detail",method = RequestMethod.GET)
public String detail(@PathVariable("seckillId")Long seckillId,Model model){
if (seckillId == null){
return "redirec:/seckill/list";//请求重定向
}
Seckill seckill = seckillService.getSeckillById(seckillId);
if (seckill==null){
return "forward:/seckill/list";
}
model.addAttribute("seckill",seckill);
return "detail";
}
//ajax json
@RequestMapping(value="/{seckillId}/exposer",
method = RequestMethod.POST,
produces = {"application/json;charset=utf-8"})
@ResponseBody
public SeckillResult<Exposer> exposer (@PathVariable("seckillId")Long seckillId){
SeckillResult<Exposer> result;
try {
Exposer exposer= seckillService.exportSeckillUrl(seckillId);
result = new SeckillResult<Exposer>(true, exposer);
} catch (Exception e) {
logger.error(e.getMessage(),e);
return new SeckillResult<Exposer>(false ,e.getMessage());
}
return result;
}
@RequestMapping(value="/{seckillId}/{md5}/execution",
method = RequestMethod.POST,
produces = {"application/json;charset=utf-8"})
@ResponseBody
public SeckillResult<SeckillExecution> execute (Long seckillId, String md5 ,
@CookieValue(value="userPhone",required=false)Long userPhone){
//@CookieValue(value="userPhone",required=false)Long userPhone
//从Cookie中获取用户手机号,required=false:没有也可以执行,不报错。
SeckillResult<SeckillExecution> result;
// springMVC valid
if (userPhone == null) {
return new SeckillResult<SeckillExecution>(false, "未注册");
}
try {
SeckillExecution seckillExecution = seckillService.executeSeckill(seckillId, userPhone, md5);
return new SeckillResult<SeckillExecution>(true, seckillExecution);
} catch (RepeatKillException e) {
SeckillExecution execution = new SeckillExecution(seckillId, SeckillStatEnum.REPEAT_KILL);
return new SeckillResult<SeckillExecution>(false, execution);
} catch (SeckillCloseException e) {
SeckillExecution execution = new SeckillExecution(seckillId, SeckillStatEnum.END);
return new SeckillResult<SeckillExecution>(false, execution);
} catch (SeckillException e) {
SeckillExecution execution = new SeckillExecution(seckillId, SeckillStatEnum.INNER_ERRPR);
return new SeckillResult<SeckillExecution>(false, execution);
}
}
@RequestMapping(value = "/time/now",method = RequestMethod.GET)
public SeckillResult<Long> time(){
Date now = new Date();
return new SeckillResult<Long>(true,now.getTime());
}
}
===================================================================================================================================
前端页面框架Boostrap
Boostrap网址:
http://www.runoob.com/bootstrap/bootstrap-environment-setup.html
JSP包含:
<%@include...%> 静态包含:
会将引用的源代码原封不动的附加过来,合并过来成一个jsp,对应一个servlet。<jsp:include...> 动态包含:
分别编译,被包含的jsp独立编译成servlet,然后和包涵的jsp页面编译生成的静态文档html做合并;
总是会检查所包含文件的变化,适合包含动态文件。
@tag.jsp
<%@taglib prefix= "c" uri="http://java.sun.com/jsp/jstl/core"%> <%@taglib prefix= "fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
@head.jsp
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- 引入 Bootstrap --> <link href="http://cdn.static.runoob.com/libs/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"> <!-- HTML5 Shim 和 Respond.js 用于让 IE8 支持 HTML5元素和媒体查询 --> <!-- 注意: 如果通过 file:// 引入 Respond.js 文件,则该文件无法起效果 --> <!--[if lt IE 9]> <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script> <script src="https://oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script> <![endif] -->
@list.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!-- 引入jstl -->
<%@include file = "common/tag.jsp" %>
<!DOCTYPE html>
<html>
<head>
<title>秒杀列表页</title>
<%@include file="common/head.jsp" %>
</head>
<body>
<!-- 页面显示部分 -->
<div class="container">
<div class="panel panel-default">
<div class="panel-heading text-center">
</div>
<div class="panel-body text-center">
<table class="table table-hover">
<thead>
<tr>
<th>名称</th>
<th>库存</th>
<th>开始时间</th>
<th>结束时间</th>
<th>创建时间</th>
<th>详情页</th>
</tr>
</thead>
<tbody>
<c:forEach var="sk" items="${list}">
<tr>
<td>${sk.name}</td>
<td>${sk.number}</td>
<td>
<fmt:formatDate value="${sk.startTime}" pattern="yyyy-MM-dd HH:mm:ss"/>
</td>
<td>
<fmt:formatDate value="${sk.endTime}" pattern="yyyy-MM-dd HH:mm:ss"/>
</td>
<td>
<fmt:formatDate value="${sk.createTime}" pattern="yyyy-MM-dd HH:mm:ss"/>
</td>
<td>
<a class="btn btn-info" href="/seckill/${sk.seckillId}/detail" target="_blank">link</a>
</td>
</tr>
</c:forEach>
</tbody>
</table>
</div>
</div>
</div>
</body>
<!-- jQuery文件。务必在bootstrap.min.js 之前引入 -->
<script src="http://cdn.static.runoob.com/libs/jquery/2.1.1/jquery.min.js"></script>
<!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
<script src="http://cdn.static.runoob.com/libs/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</html>
@detail.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<title>秒杀详情页</title>
<%@include file="common/head.jsp" %>
</head>
<body>
<div class="container">
<div class="panel panel-deault text-center">
<div class="panel-heading">
<h1>${seckill.name}</h1>
</div>
</div>
<div class="panel-body">
</div>
</div>
</body>
<!-- jQuery文件。务必在bootstrap.min.js 之前引入 -->
<script src="http://cdn.static.runoob.com/libs/jquery/2.1.1/jquery.min.js"></script>
<!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
<script src="http://cdn.static.runoob.com/libs/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</html>
=================================================================================================================================
BootCDN:Content Delivery Network,即内容分发网络
网址:http://www.bootcdn.cn/?
Cookie登录交互:
Javascript 模块化
// 存放主要交换逻辑js代码
// javascript 模块化
var seckill = {
// 封装秒杀相关ajax的url
URL : {
basePath : function() {
return $('#basePath').val();
},
now : function() {
return '/seckill/time/now';
},
exposer : function(seckillId) {
return '/seckill/' + seckillId + '/exposer';
},
execution : function(seckillId, md5) {
return '/seckill/' + seckillId + '/' + md5 + '/execution';
}
},
// 处理秒杀逻辑
handleSeckill : function(seckillId, node) {
// 获取秒杀地址,控制显示逻辑,执行秒杀
node.hide().html('<button class="btn btn-primary btn-lg" id="killBtn">开始秒杀</button>');
console.log('exposerUrl=' + seckill.URL.exposer(seckillId));//TODO
$.post(seckill.URL.exposer(seckillId), {}, function(result) {
// 在回调函数中,执行交互流程
if (result && result['success']) {
var exposer = result['data'];
if (exposer['exposed']) {
// 开启秒杀
var md5 = exposer['md5'];
var killUrl = seckill.URL.execution(seckillId, md5);
$('#killBtn').one('click', function() {//one.绑定一次点击事件
// 执行秒杀请求
// 1.先禁用按钮
$(this).addClass('disabled');
// 2.发送秒杀请求
console.log('killUrl=' + killUrl);//TODO
$.post(killUrl,{},function(result) {
console.log('执行Post请求');//TODO
if (result && result['success']) {
var killResult = result['data'];
var state = killResult['state'];
var stateInfo = killResult['stateInfo'];
// 3.显示秒杀结果
node.html('<span class="label label-success">' + stateInfo + '</span>');
}
});
});
node.show();
} else {
// 未开启秒杀
var now = exposer['now'];
var start = exposer['start'];
var end = exposer['end'];
// 重新计算计时逻辑
seckill.countdown(seckillId, now, start, end);
}
} else {
console.log('result=' + result);
}
});
},
// 验证手机号
validatePhone : function(phone) {
//正则表达式
var pattern = /0?(13|14|15|18)[0-9]{9}/;
console.log("手机号验证"+pattern.test(phone));
if (pattern.test(phone)) {
return true;
} else {
return false;
}
},
// 倒计时
countdown : function(seckillId, nowTime, startTime, endTime) {
// 时间判断
var seckillBox = $('#seckillBox');
if (nowTime > endTime) {
// 秒杀结束
seckillBox.html('秒杀结束!');
} else if (nowTime < startTime) {
// 秒杀未开始,计时事件绑定
var killTime = new Date(startTime + 1000);
seckillBox.countdown(killTime, function(event) {
// 时间格式
var format = event.strftime('秒杀倒计时:%D天 %H时 %M分 %S秒');
seckillBox.html(format);
// 时间完成后回调事件
}).on('finish.countdown', function() {
// 获取秒杀地址,控制显示逻辑,执行秒杀
seckill.handleSeckill(seckillId, seckillBox);
});
} else {
// 秒杀开始
seckill.handleSeckill(seckillId ,seckillBox);
}
},
// 详情页秒杀逻辑
detail : {
// 详情页初始化
init : function(params) {
// 用户手机验证和登录,计时交互
// 规划我们的交互流程
// 在cookie中查找手机号
var killPhone = $.cookie('userPhone');
// 验证手机号
if (!seckill.validatePhone(killPhone)) {
// 绑定phone
// 控制输出
console.log('显示输入手机号界面' + killPhone);//TODO
var killPhoneModal = $('#killPhoneModal');
killPhoneModal.modal({
show : true,// 显示弹出层
backdrop : 'static',// 禁止位置关闭
keyboard : false// 关闭键盘事件
})
$('#killPhoneBtn').click(function() {
var inputPhone = $('#killphoneKey').val();
console.log('inputPhone='+inputPhone);//TODO
if (seckill.validatePhone(inputPhone)) {
// 电话写入cookie,设置cookie有效期和范围
$.cookie('userPhone', inputPhone, {
expires : 7,
path : '/seckill'
});
// 刷新页面
window.location.reload();
} else {
$('#killphoneMessage').hide().html('<label class="label label-danger">手机号错误!</label>').show(300);
}
});
}
// 已经登录
// 计时交互
var startTime = params['startTime'];
var endTime = params['endTime'];
var seckillId = params['seckillId'];
console.log("seckill.URL.now=" + seckill.URL.now());
$.get(seckill.URL.now(), {}, function(result) {//ajax交互,(url,参数,回调函数)
if (result && result['success']) {
var nowTime = result['data'];
// 时间判断,计时交互
seckill.countdown(seckillId, nowTime, startTime, endTime);
} else {
console.log(result['reult:'] + result);
}
});
}
}
}
=================================================================================================================================