首页 > 代码库 > SSM实战——秒杀系统之Web层Restful url设计、SpringMVC整合、页面设计

SSM实战——秒杀系统之Web层Restful url设计、SpringMVC整合、页面设计

一:Spring整合SpringMVC

1:编写web.xml,配置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">
  
  <servlet>
      <servlet-name>seckill-dispatcher</servlet-name>
      <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
      <!-- 指明配置文件路径 -->
      <init-param>
          <param-name>contextConfigLocation</param-name>
          <param-value>classpath:spring/spring-*.xml</param-value>
      </init-param>
  </servlet>
  <servlet-mapping>
      <servlet-name>seckill-dispatcher</servlet-name>
      <!-- 默认匹配所有请求 -->
      <url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>

2:在resource/spring下,新建spring-web.xml,整合SpringMVC

<?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:mvc="http://www.springframework.org/schema/mvc"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd
        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-4.1.xsd">
   <!-- 1:开启注解模式 -->
   <mvc:annotation-driven/>
   <!-- 2:配置静态资源默认处理器-->
   <mvc:default-servlet-handler/>
   <!-- 3:配置JSP视图解析器 -->
   <bean
        class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
        <property name="prefix" value="/WEB-INF/jsp/" />
        <property name="suffix" value=".jsp" />
    </bean>
    <!-- 4:配置扫描controller所在包 -->
    <context:component-scan base-package="org.myseckill.web"/>
</beans>

 

 

二:SpringMVC controller开发并实现restful url设计

RestFul架构见博客:http://kb.cnblogs.com/page/512047/

Restful url 格式:/模块/资源/{id}/细分

 

1:在org.myseckill包下新建web包,用于存放所有controller。

2:web包下新建一个controller:SeckillController

package org.myseckill.web;

import java.util.Date;
import java.util.List;

import org.junit.runners.Parameterized.Parameter;
import org.myseckill.entity.Seckill;
import org.myseckill.exception.RepeatKillException;
import org.myseckill.service.SeckillService;
import org.myseckill.dto.Exposer;
import org.myseckill.dto.SeckillExecution;
import org.myseckill.dto.SeckillResult;
import org.myseckill.enums.SeckillStateEnum;
import org.myseckill.exception.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller//注册该bean
@RequestMapping("/seckill")//映射项目模块
public class SeckillController {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());
    @Autowired
    private SeckillService seckillService;
    //列表页请求
    @RequestMapping(value="http://www.mamicode.com/list",method=RequestMethod.GET)
    public String list(Model model){//model用于存放数据,字符串用于控制跳转
        List<Seckill> list=seckillService.getSeckillList();
        model.addAttribute("list",list);
        return "list";
    }
    
    //详情页请求
    @RequestMapping(value="http://www.mamicode.com/{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";
    }
    
    @RequestMapping(value="/{seckillId}/exposer",method=RequestMethod.POST,produces={"application/json;charset=UTF-8"})
    @ResponseBody//把返回数据转换成JSON格式,绑定到响应中
    //Ajax方法,返回JSON数据
    public SeckillResult<Exposer> exposer(@PathVariable 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);
            result = 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(@PathVariable Long seckillId, @PathVariable String md5,@CookieValue(value = "http://www.mamicode.com/killPhone", required = false) Long userPhone) {
        if(userPhone==null){
            return new SeckillResult<SeckillExecution>(false, "未注册");
        }
        try {
            SeckillExecution execution=seckillService.executeSeckill(seckillId, userPhone, md5);
            return new SeckillResult<SeckillExecution>(true, execution);
        } catch (RepeatKillException e) {
            SeckillExecution execution = new SeckillExecution(seckillId, SeckillStateEnum.REPEAT_KILL);
            return new SeckillResult<SeckillExecution>(true,execution);
        } catch (SeckillException e) {
            SeckillExecution execution = new SeckillExecution(seckillId, SeckillStateEnum.END);
            return new SeckillResult<SeckillExecution>(true,execution);
        } catch (Exception e){
            SeckillExecution execution = new SeckillExecution(seckillId, SeckillStateEnum.INNER_ERROR);
            return new SeckillResult<SeckillExecution>(false,execution);
        }
    }    
    
    @RequestMapping(value="/time/now",method=RequestMethod.GET)
    @ResponseBody
    public SeckillResult<Long> time(){
        Date now = new Date();
        return new SeckillResult<Long>(true,now.getTime());
    }    
}

3:新建一个dto:SeckillResult,封装controller的处理结果

package org.seckill.dto;

//所有ajax请求返回类型,封装json结果
public class SeckillResult<T> {
    private boolean success;//请求是否成功
    
    private T data;
    
    private String error;

    public SeckillResult(boolean success, T data) {
        super();
        this.success = success;
        this.data =http://www.mamicode.com/ data;
    }

    public SeckillResult(boolean success, String error) {
        super();
        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 =http://www.mamicode.com/ data;
    }

    public String getError() {
        return error;
    }

    public void setError(String error) {
        this.error = error;
    }        
}

 

三:页面开发

在WEB-INF下新建文件夹jsp,用于存放所有页面。

1:jsp文件夹下新建common文件夹,用于存放一些共用的jsp页面头/尾。

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]-->
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<base href="http://www.mamicode.com/">   

tag.jsp:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>

 

2:创建页面jsp

列表页list.jsp:

<%@ page language="java" contentType="text/html; charset=utf-8"
    pageEncoding="utf-8"%>
<%@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">
                   <h2>秒杀列表</h2>
               </div>
               <div class="panel-body">
                   <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></th>

 

2:详情页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-default text-center">
               <div class="panel-heading">
                   <h1>${seckill.name}</h1>
               </div>
               <div class="panel-body">
                   <h2 class="text-danger">
                       <!-- 显示time图标 -->
                       <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 class="modal-body">
                    <div class="row">
                        <div class="col-xs-8 col-xs-offset-2">
                            <input type="text" name="killPhone" id="killPhoneKey"
                                    placeholder="填手机号^o^" 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>
                        Submit
                    </button>
                </div>
            </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>
   <!-- 使用cdn获取公共js http://www.bootcdn.cn -->
   <!-- jQuery cookie操作插件 -->
   <script src="http://cdn.bootcss.com/jquery-cookie/1.4.1/jquery.cookie.min.js"></script>
   <!-- jQuery countDown倒计时插件 -->
   <script src="http://cdn.bootcss.com/jquery.countdown/2.2.0/jquery.countdown.min.js"></script>
   <!-- 开始编写交互逻辑 -->
   <script src="/MySeckill/resources/script/seckill.js" type="text/javascript"></script>
   <script type="text/javascript">
           $(function(){
               //使用EL表达式传入参数
               seckill.detail.init({
                   seckillId : ${seckill.seckillId},
                   startTime : ${seckill.startTime.time},//毫秒
                   endTime : ${seckill.endTime.time}

               });
           });
   </script>
</html>

3:在WebRoot下新建一个文件夹resources,用于存放一些共用的资源,如js、images等。

resource下新建一个文件夹script,新建一个js文件 seckill.js,用于书写交互函数:

//存放主要交互逻辑js代码
//javascript模块化.json类型
var seckill={
        //封装秒杀相关ajax的url
        URL:{
            now : function(){
                return "seckill/time/now";
            },
            exposer : function(seckillId){
                return ‘seckill/‘+seckillId+‘/exposer‘;
            },
            execution : function(seckillId,md5){
                return ‘seckill/‘+seckillId+‘/‘+md5+‘/execution‘;
            }
        },
        handlerSeckill:function(seckillId, node){
            //获取秒杀地址,控制显示逻辑 执行秒杀
            node.hide().html(‘<button class="btn btn-primary btn-lg" id="killBtn">开始秒杀</button>‘);
            $.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);
                        console.log("killUrl:"+killUrl);
                        //绑定一次点击事件
                        $(‘#killBtn‘).one(‘click‘,function(){
                            //执行秒杀请求的操作
                            //1.先禁用按钮
                            $(this).addClass(‘disabled‘);
                            //2.发送秒杀请求
                            $.post(killUrl,{},function(result){
                                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){
            if(phone && phone.length==11 && !isNaN(phone)){//isNaN不是数字
                return true;
            } else {
                return false;
            }
        },
        countdown:function(seckillId, nowTime, startTime, endTime){
            var seckillBox = $(‘#seckill-box‘);
            if(nowTime > endTime){
                seckillBox.html(‘秒杀结束!‘);
            } else if(nowTime < startTime){
                //秒杀未开始,倒计时
                var killTime = new Date(startTime-0 + 1000);
                seckillBox.countdown(killTime,function(event){
                    //时间格式
                    var format = event.strftime(‘秒杀倒计时: %D天 %H时 %M分 %S秒‘);
                    seckillBox.html(format);
                    /*时间完成后回调函数*/
                }).on(‘finish.countdown‘,function(){
                    //获取秒杀地址,控制显示逻辑 执行秒杀
                    seckill.handlerSeckill(seckillId, seckillBox);
                });
            }else{
                //秒杀开始
                seckill.handlerSeckill(seckillId, seckillBox);
            }
        },
        //详情页秒杀逻辑
        detail:{
            //详情页初始化
            init : function(params){
                //用户手机验证和登陆,计时交互
                //在cookie中查找手机号
                var killPhone = $.cookie(‘killPhone‘);
                var startTime = params[‘startTime‘];
                var endTime = params[‘endTime‘];
                var seckillId = params[‘seckillId‘];
                //验证手机号
                if(!seckill.validatePhone(killPhone)){
                    //绑定phone
                    var killPhoneModal = $(‘#killPhoneModal‘);
                    killPhoneModal.modal({
                        show:true,//显示弹出层
                        backdrop:‘static‘,//禁止位置关闭
                        keyboard:false//关闭键盘事件
                    });
                    $(‘#killPhoneBtn‘).click(function(){
                        var inputPhone = $(‘#killPhoneKey‘).val();
                        if(seckill.validatePhone(inputPhone)){
                            //电话写入cookie
                            $.cookie(‘killPhone‘,inputPhone,{expires:7,path:‘/seckill‘});
                            //刷新页面
                            window.location.reload();
                        } else {
                            $(‘#killPhoneMessage‘).hide().html(‘<label class="label label-danger">手机号错误!</label>‘).show(300);
                        }
                    });
                }
                //已经登陆
                //计时交互
                $.get(seckill.URL.now(),{},function(result){
                    if(result && result[‘success‘]){
                        var nowTime = result[‘data‘];
                        seckill.countdown(seckillId, nowTime, startTime, endTime);
                    } else {
                        console.log(‘result:‘+result);
                    }
                });
            }
        }
}

 

 

4:部署测试记录

提示DispatcherServlet找不到,或者配置文件找不到,首先检查部署到tomcat的目录下有无对应文件。

如果没有,则检查是否Myeclipse中的关于项目编译文件输出目录与部署文件拉取目录不一致导致出错。

一般,Maven项目都会把项目编译文件输出到target目录下,而我们需要的是WebRoot/WEB-INF/classes目录,所以需要修改。

1)右键项目,Buildpath——configer buildPath

2)选择 source 选项卡

3)修改output folder:

技术分享

4)左侧,打开部署选项配置

技术分享

5)修改拉取路径

技术分享

SSM实战——秒杀系统之Web层Restful url设计、SpringMVC整合、页面设计