修改web.xml
,引入SpringMvc
的DispatcherServlet
:
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0" metadata-complete="true"> <!--用maven创建的web-app需要修改servlet的版本为3.0--> <servlet> <servlet-name>seckill-dispatchServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!--配置springmvc的配置文件--> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/applicationContext-*.xml</param-value> </init-param> <load-on-startup> 1 </load-on-startup> </servlet> <servlet-mapping> <servlet-name>seckill-dispatchServlet</servlet-name> <!--直接拦截所有请求,不再采用spring2.0的/*或者*.do方式--> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
在这里的话如果你不配置这一段代码的:
<!--配置springmvc的配置文件--> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/applicationContext-*.xml</param-value> </init-param>
SpringMvc默认就会默认去WEB-INF
下查找默认规范的配置文件,像我这里配置的servlet-name
是seckill-dispatchServlet
的话,则默认会寻找WEB-INF
一个名为seckill-dispatchServlet-Servlet.xml
的配置文件
SeckillController
首先在com.suny
下建立包为Controller
的包,然后在里面新建一个类SeckillController
:
package com.suny.controller;/** * Created by 孙建荣 on 17-5-24.下午10:11 */@Controller@RequestMapping("/seckill")public class SeckillController { private final SeckillService seckillService; @Autowired public SeckillController(SeckillService seckillService) { this.seckillService = seckillService; } /** * 进入秒杀列表. * * @param model 模型数据,里面放置有秒杀商品的信息 * @return 秒杀列表详情页面 */ @RequestMapping(value = "/list", method = RequestMethod.GET) public String list(Model model) { List<Seckill> seckillList = seckillService.getSeckillList(); model.addAttribute("list", seckillList); return "list"; } @RequestMapping(value = "/{seckillId}/detail", method = RequestMethod.GET) public String detail(@PathVariable("seckillId") Long seckillId, Model model) { if (seckillId == null) { return "redirect:/seckill/list"; } Seckill seckill = seckillService.getById(seckillId); if (seckill == null) { return "forward:/seckill/list"; } model.addAttribute("seckill", seckill); return "detail"; } /** * 暴露秒杀接口的方法. * * @param seckillId 秒杀商品的id * @return 根据用户秒杀的商品id进行业务逻辑判断,返回不同的json实体结果 */ @RequestMapping(value = "/{seckillId}/exposer", method = RequestMethod.GET) @ResponseBody public SeckillResult<Exposer> exposer(@PathVariable("seckillId") Long seckillId) { // 查询秒杀商品的结果 SeckillResult<Exposer> result; try { Exposer exposer = seckillService.exportSeckillUrl(seckillId); result = new SeckillResult<>(true, exposer); } catch (Exception e) { e.printStackTrace(); result = new SeckillResult<>(false, e.getMessage()); } return result; } /** * 用户执行秒杀,在页面点击相应的秒杀连接,进入后获取对应的参数进行判断,返回相对应的json实体结果,前端再进行处理. * * @param seckillId 秒杀的商品,对应的时秒杀的id * @param md5 一个被混淆的md5加密值 * @param userPhone 参与秒杀用户的额手机号码,当做账号密码使用 * @return 参与秒杀的结果,为json数据 */ @RequestMapping(value = "/{seckillId}/{md5}/execution", method = RequestMethod.POST) @ResponseBody public SeckillResult<SeckillExecution> execute(@PathVariable("seckillId") long seckillId, @PathVariable("md5") String md5, @CookieValue(value = "userPhone", required = false) Long userPhone) { // 如果用户的手机号码为空的说明没有填写手机号码进行秒杀 if (userPhone == null) { return new SeckillResult<>(false, "没有注册"); } // 根据用户的手机号码,秒杀商品的id跟md5进行秒杀商品,没异常就是秒杀成功 try { // 这里换成储存过程 SeckillExecution execution = seckillService.executeSeckill(seckillId, userPhone, md5); return new SeckillResult<>(true, execution); } catch (RepeatKillException e1) { // 重复秒杀 SeckillExecution execution = new SeckillExecution(seckillId, SeckillStatEnum.REPEAT_KILL); return new SeckillResult<>(false, execution); } catch (SeckillCloseException e2) { // 秒杀关闭 SeckillExecution execution = new SeckillExecution(seckillId, SeckillStatEnum.END); return new SeckillResult<>(false, execution); } catch (SeckillException e) { // 不能判断的异常 SeckillExecution execution = new SeckillExecution(seckillId, SeckillStatEnum.INNER_ERROR); return new SeckillResult<>(false, execution); } // 如果有异常就是秒杀失败 } /** * 获取服务器端时间,防止用户篡改客户端时间提前参与秒杀 * * @return 时间的json数据 */ @RequestMapping(value = "/time/now", method = RequestMethod.GET) @ResponseBody public SeckillResult<LocalDateTime> time() { LocalDateTime localDateTime = LocalDateTime.now(); return new SeckillResult<>(true, localDateTime); } }
SeckillResult
:
package com.suny.dto;/** * 封装所有的ajax请求返回类型,方便返回json * Created by 孙建荣 on 17-5-24.下午10:18 */public class SeckillResult<T> { private boolean success; private T data; private String error; public SeckillResult() { } public SeckillResult(boolean success, T data) { this.success = success; this.data = data; } public SeckillResult(boolean success, String error) { this.success = success; this.error = error; } public boolean isSuccess() { return success; } public void setSuccess(boolean success) { this.success = success; } public T getData() { return data; } public void setData(T data) { this.data = data; } public String getError() { return error; } public void setError(String error) { this.error = error; } @Override public String toString() { return "SeckillResult{" + "状态=" + success + ", 数据=" + data + ", 错误消息='" + error + ''' + '}'; } }
因为项目的前端页面都是由Bootstrap
开发的,所以我们要先去下载Bootstrap
或者是使用在线的CDN.
-Bootstrap中文官网
-Bootstrap中文文档 使用在线CDN引入的方法:
<!-- 最新版本的 Bootstrap 核心 CSS 文件 --><link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"><!-- 可选的 Bootstrap 主题文件(一般不用引入) --><link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous"><!-- 最新的 Bootstrap 核心 JavaScript 文件 --><script src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
文档里面写的很详细,然后我这里是使用离线版本的,方便我们本地调试,避免出现什么别的因素干扰我们:
首先下载JQuery
,因为Bootstrap
就是依赖JQuery
的
然后下载Bootstrap
然后下载一个倒计时插件jquery.countdown.min.js
-再下载一个操作Cookie
插件jquery.cookie.min.js
如图放置:
首先编写一个公共的头部jsp
文件,位于WEB-INF
下common
中的head.jsp
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta charset="utf-8"> <link rel="stylesheet" href="${pageContext.request.contextPath}/resources/plugins/bootstrap-3.3.0/css/bootstrap.min.css" type="text/css"> <link rel="stylesheet" href="${pageContext.request.contextPath}/resources/plugins/bootstrap-3.3.0/css/bootstrap-theme.min.css" type="text/css">
然后编写一个公共的jstl
标签库文件,位于WEB-INF
下common
中的tag.jsp
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> <%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
编写列表页面,位于WEB-INF
下common
中的list.jsp
<%@page contentType="text/html; charset=UTF-8" language="java" %> <%@include file="common/tag.jsp" %> <!DOCTYPE html> <html lang="zh-CN"> <head> <title>秒杀列表</title> <%@include file="common/head.jsp" %> </head> <body> <div class="container"> <div class="panel panel-default"> <div class="panel-heading text-center"> <h2>秒杀列表</h2> </div> <div class="panel-body"> <table class="table table-hover"> <thead> <tr> <td>名称</td> <td>库存</td> <td>开始时间</td> <td>结束时间</td> <td>创建时间</td> <td>详情页</td> </tr> </thead> <tbody> <c:forEach items="${list}" var="sk"> <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">详情</a></td> </tr> </c:forEach> </tbody> </table> </div> </div> </div> </body> <script src="${pageContext.request.contextPath}/resources/plugins/jquery.js"></script> <script src="${pageContext.request.contextPath}/resources/plugins/bootstrap-3.3.0/js/bootstrap.min.js"></script> </html>
编写列表页面,位于WEB-INF
下common
中的detail.jsp
,秒杀详情页面
<%-- Created by IntelliJ IDEA. User: jianrongsun Date: 17-5-25 Time: 下午5:03 To change this template use File | Settings | File Templates.--%><%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@include file="common/tag.jsp" %> <html> <head> <title>秒杀商品详情页面</title> <%@include file="common/head.jsp" %> </head> <body> <div class="container"> <div class="panel panel-default"> <div class="panel-heading"> <h1>${seckill.name}</h1> </div> <div class="panel-body"> <h2 class="text-danger"> <span class="glyphicon glyphicon-time"></span> <span class="glyphicon" id="seckill-box"></span> </h2> </div> </div> </div> <div id="killPhoneModal" class="modal fade"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <h3 class="modal-title text-center"> <span class="glyphicon glyphicon-phone"></span>秒杀电话: </h3> </div> </div> <div class="modal-body"> <div class="row"> <div class="col-xs-8 col-xs-offset-2"> <input type="text" name="killPhone" id="killPhoneKey" placeholder="填写手机号码" class="form-control"> </div> </div> </div> <div class="modal-footer"> <span id="killPhoneMessage" class="glyphicon"></span> <button type="button" id="killPhoneBtn" class="btn btn-success"> <span class="glyphicon glyphicon-phone"></span> 提交 </button> </div> </div> </div> </body> <script src="${pageContext.request.contextPath}/resources/plugins/jquery.js"></script> <script src="${pageContext.request.contextPath}/resources/plugins/bootstrap-3.3.0/js/bootstrap.min.js"></script> <script src="${pageContext.request.contextPath}/resources/plugins/jquery.cookie.min.js"></script> <script src="${pageContext.request.contextPath}/resources/plugins/jquery.countdown.min.js"></script> <script src="${pageContext.request.contextPath}/resources/script/seckill.js"></script> <script type="text/javascript"> $(function () { var startTimeVal = "${seckill.startTime.toLocalDate()} " + seckill.cloneZero("${seckill.startTime.toLocalTime()}"); var endTimeVal = "${seckill.endTime.toLocalDate()} " + seckill.cloneZero("${seckill.endTime.toLocalTime()}"); console.log("startTimeVal========" + startTimeVal); console.log("endTimeVal========" + endTimeVal); // 传入参数 seckill.detail.init({ seckillId:${seckill.seckillId}, startTime: startTimeVal, endTime: endTimeVal }) })</script> </html>
然后把项目运行一下我们又会碰到一个错误就是jstl
中的fmt
标签格式化时间只能格式化java.Util.Date
类型的日期跟时间,而在我们这里我么使用了java8
的LocalDateTIme
,所以解析时间会出异常,这时我们应该想到自己去实现jstl
标签来自定义解析这个时间日期 自定义标签步骤如下:
在/WEB-INF
创建目录 tags
然后创建一个文件 localDateTime.tag
在tags
目录下
localData.tag
用来格式化日期
localDataTime.tag
用来格式化日期跟时间的组合,也就是数据库中的Timestamp
类型 -然后在localDataTime.tag
中写自己自定义的格式化流程
<%--格式化java8的LocalDatime,解决jstl不支持java8时间的问题--%><%@ tag body-content="empty" pageEncoding="UTF-8" trimDirectiveWhitespaces="true" %><%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %><%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %><%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %><%-- 这里是定义页面使用标签中的属性设置,<tags:localDataTime dateTime="${sk.createTIme}"/> --%><%@ attribute name="dateTime" required="true" type="java.time.LocalDateTime" %><%@ attribute name="pattern" required="false" type="java.lang.String" %><%--首选判断日期时间转换规则是否存在,不存在给出默认的规则--%><c:if test="${empty pattern}"> <c:set var="pattern" value="yyyy-MM-dd HH:mm:ss"/> </c:if> <c:set var="datetime" value="${dateTime}"/> <%-- 获取jsp页面传入的【 日期时间 】,格式为【 2017-5-26T13:59:12 】 --%><c:set var="time" value="${fn:substringAfter(datetime, 'T')}"/> <%-- 获取页面传过来的【时间T】后面的 【 时:分:秒 】的值 --%><c:set var="timeLength" value="${fn:length(time)}"/> <%-- 获取页面传来的 【 时:分:秒 的长度 】 --%><c:set var="generalLength" value="${fn:length('123456')}"/> <%-- 这里定义了一个【Integer】类型的值,值为字符串 【123456 】的长度 --%><c:set var="cloneZero" value=":00"/> <%-- 这里设置一个值为【String】的字符串, --%><%-- 当 时:分:秒 不足6位的时候就说明缺少秒,我们给它自动补充 :00 --%><c:if test="${timeLength lt generalLength}"> <c:set var="datetimeCloneZero" value="${datetime}${cloneZero}"/> <%-- 拼接页面传过来的 【 时:分 】 ,补充一个【秒数】,EL中 + 为相加,非拼接字符串 --%> <c:set var="cleandDateTime" value="${fn:rep