Java高并发之秒杀系统Web层开发

基于SpringMVC的Java高并发之秒杀系统Web层开发。

一、整合配置SpringMVC框架

配置SpringMVC中央控制器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
<display-name>Archetype Created Web Application</display-name>
<!--配置DispatcherServlet-->
<servlet>
<servlet-name>seckill-dispacher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--配置springMVC需要加载的配置文件-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/spring-*.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>seckill-dispacher</servlet-name>
<!--默认匹配所有的请求-->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>

配置SpringMVC 框架

  • 开启SpringMVC注解
  • Selvlet配置静态资源
  • 配置jsp
  • 扫描web相关的bean
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    <?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/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">
    <!-- 配置SpringMVC -->
    <!-- 1:开启springMVC注解模式 -->
    <!--简化配置:
    (1)自动注册DefaultAnnotationHandlerMapping,AnnotationMethodHandlerAdapter
    (2)默认提供一系列的功能:数据绑定,数字和日期的format,xml,json默认读写支持
    -->
    <mvc:annotation-driven/>
    <!--servlet-mapping 映射路径:"/" -->
    <!-- 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/jsp/"/>
    <property name="suffix" value=".jsp"/>
    </bean>
    <!-- 4:扫描web相关的bean-->
    <context:component-scan base-package="szy.seckill.web"/>
    </beans>

二、实现秒杀相关的Reatful接口

在 REST 中,资源通过 URL进行识别和定位,然后通过行为(即 HTTP 方法)来定义 REST 来完成怎样的功能。

注解说明

  • @Controller 声明控制类
  • @Autowired 扫描并自动装配
  • @RequestMapping(value = “ “, method = RequestMethod.GET) 跳转地址
  • @PathVariable(“ “) 定义路径中的变量
  • @CookieValue(value = “ “, required = false) 从cookie中取值,非必要。

功能需求

访问列表时显示商品列表。
访问商品细节时,显示细节。
返回值为页面的名称。

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
@Controller
@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);//jsp页面可通过${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 "redirect:/seckill/list";
}
Seckill seckill = seckillService.getSeckillById(seckillId);
if (seckill == null) {
return "forward:/seckill/list";
}
model.addAttribute("seckill", seckill);
return "detail";// /WEB-INF/jsp/"detail".jsp
}

//produce设置,防止json乱码
@RequestMapping(value = "/{seckillId}/exposer", method = RequestMethod.POST, produces = {"application/json;charset=UTF-8"})
//返回json
@ResponseBody
public SeckillResult<Exposer> exposer(Long seckillId) {
SeckillResult<Exposer> result;
try {
Exposer exposer = seckillService.exportSeckillUrl(seckillId);
result = new SeckillResult<>(true, exposer);
} catch (Exception e) {
logger.error(e.getMessage());
result = new SeckillResult<>(false, e.getMessage());
}
return result;
}

@RequestMapping(value = "/{seckillId}/{md5}/execution")
@ResponseBody
public SeckillResult<SeckillExecution> execute(@PathVariable("seckillId") Long seckillId, @PathVariable("md5") String md5, @CookieValue(value = "seckillPhone", required = false) Long userPhone) {
//spring valid
if (userPhone == null) {
return new SeckillResult<>(false, "未注册");
}
SeckillResult<SeckillExecution> result;
try {
SeckillExecution execution = seckillService.executeSeckill(seckillId, userPhone, md5);
return new SeckillResult<SeckillExecution>(true, execution);
} catch (RepeatKillException e) {
SeckillExecution seckillExecution = new SeckillExecution(seckillId, SeckillStatEnum.REPEAT_KILL);
return new SeckillResult<SeckillExecution>(false, seckillExecution);
} catch (SeckillCloseException e) {
SeckillExecution seckillExecution = new SeckillExecution(seckillId, SeckillStatEnum.END);
return new SeckillResult<SeckillExecution>(false, seckillExecution);
} catch (Exception e) {
logger.error(e.getMessage());
SeckillExecution seckillExecution = new SeckillExecution(seckillId, SeckillStatEnum.INNER_ERROR);
return new SeckillResult<SeckillExecution>(false, seckillExecution);
}
}

@RequestMapping(value = "/time/now", method = RequestMethod.GET)
public SeckillResult time() {
Date nowTime = new Date();
return new SeckillResult(true, nowTime.getTime());
}

}

基于bootstrap开发页面结构

定义标签页(tag.jsp)

jstl标签是jsp的标准标签库。设置变量的作用域,格式化系统时间。

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

定义公共页面头部(head.jsp)

定义bootstrap的格式规范。

1
2
3
4
5
6
7
8
9
10
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- 引入 Bootstrap -->
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">

<!-- HTML5 Shiv 和 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)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!--引入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">
<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>
<th>${sk.name}</th>
<th>${sk.number}</th>
<th><fmt:formatDate value="${sk.startTime}" pattern="yyyy-MM-dd HH:mm:ss"/></th>
<th><fmt:formatDate value="${sk.endTime}" pattern="yyyy-MM-dd HH:mm:ss"/></th>
<th><fmt:formatDate value="${sk.createTime}" pattern="yyyy-MM-dd HH:mm:ss"/></th>
<th><a class="btn btn-info" href="/seckill/${sk.seckillId}/detail" target="_blank">link</a></th>
</tr>
</c:forEach>
</tbody>

</table>

</div>
</div>

</div>
</body>
<!-- jQuery文件。务必在bootstrap.min.js 之前引入 -->
<script src="https://cdn.staticfile.org/jquery/2.1.1/jquery.min.js"></script>

<!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
<script src="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
</html>

页面显示

Markdown