1. 在15-seckill-web 的pom.xml文件添加Thymeleaf依賴
我們創(chuàng)建SpringBoot項(xiàng)目的時(shí)候,已經(jīng)勾選,會(huì)自動(dòng)生成
2. 在15-seckill-web的resources/templates下創(chuàng)建goods.html,用于商品列表展示
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>秒殺商品列表頁(yè)</title>
</head>
<body th:inline="text">
<div th:each="goods:${goodsList}">
<img th:src="@{${goods.imageurl}}"><br>
[[${goods.name}]] [[${goods.namedesc}]]<br>
[[${goods.price}]] 剩余:[[${goods.store}]]件
</div>
</body>
</html>
3. 后端獲取所有商品列表傳遞給goods.html頁(yè)面
? 分析1:直接從數(shù)據(jù)庫(kù)中查詢獲取所有商品合適嗎?
• 查詢所有秒殺商品,商品在數(shù)據(jù)庫(kù)中存儲(chǔ)
• 因?yàn)槭敲霘?chǎng)景,屬于高并發(fā)大流量訪問(wèn)
• 如果我們?cè)贑ontroller層中直接查詢數(shù)據(jù)庫(kù),會(huì)給數(shù)據(jù)庫(kù)造成很大的壓力,甚至?xí)寯?shù)據(jù)庫(kù)沒(méi)有響應(yīng)或者崩潰
• 優(yōu)化:所以,我們這里不直接查詢數(shù)據(jù)庫(kù),而是先將所有商品查詢出來(lái),放到Redis中,采用Redis抗住巨大的訪問(wèn)流量,這種提前將數(shù)據(jù)查詢出來(lái)放到Redis的操作叫做“緩存預(yù)熱”
? 分析2:從Redis取數(shù)據(jù)寫在 15-seckill-web項(xiàng)目中還是在15-seckill- service項(xiàng)目中
• 直接寫在15-seckill-web項(xiàng)目中,查詢Redis獲取商品列表
• 如果寫在15-seckill-service項(xiàng)目中,需要采用RPC調(diào)用,會(huì)通過(guò)網(wǎng)絡(luò)進(jìn)行Socket連接,會(huì)有性能消耗,
• 這也是一個(gè)優(yōu)化
? 分析3:什么時(shí)候向Redis中寫入商品列表
在服務(wù)器啟動(dòng)之后,不間斷的從數(shù)據(jù)庫(kù)中查詢商品信息,寫入到Redis中
? 分析4:通過(guò)什么方式,不間斷的查詢數(shù)據(jù)庫(kù)商品信息,寫到Redis中可以在15-seckill-service中創(chuàng)建一個(gè)定時(shí)任務(wù),通過(guò)定時(shí)任務(wù)實(shí)現(xiàn)
? 在15-seckill-service中完成定時(shí)查詢數(shù)據(jù)庫(kù)向Redis放商品
• 在15-seckill-service的pom.xml文件中添加SpringBoot對(duì)Redis的支持依賴
<!-- 加載spring boot redis包 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
• 在15-seckill-service的SpringBoot核心配置文件application.properties中添加Redis連接信息
#配置redis連接信息
spring.redis.host=192.168.235.128
spring.redis.port=6379
spring.redis.password=123456
• 向15-seckill-service的pom.xml文件中添加SpringBoot整合Mybatis依賴和數(shù)據(jù)庫(kù)驅(qū)動(dòng)依賴
<!-- 加載mybatis整合springboot -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<!--在springboot的父工程中沒(méi)有指定版本,我們需要手動(dòng)指定-->
<version>1.3.2</version>
</dependency>
<!-- MySQL的jdbc驅(qū)動(dòng)包 -->
<dependency>
<groupId>mysql</groupId>
<!--在springboot的父工程中指定了版本,我們就不需要手動(dòng)指定了-->
<artifactId>mysql-connector-java</artifactId>
</dependency>
• 在15-seckill-service的pom.xml文件中指定將Mybatis映射文件編譯到classpath下
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
• 在15-seckill-service的核心配置文件application.properties中配置MySQL數(shù)據(jù)庫(kù)連接信息
#數(shù)據(jù)庫(kù)的連接配置信息
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://192.168.235.128:3306/seckill?useUnicode=true&characterEncoding=utf8&useSSL=false
• 在15-seckill-service的com.bjpowernode.seckill.task包下創(chuàng)建定時(shí)任務(wù)類RedisTask,緩存商品信息到Redis中Redis Key的格式 redis:store:商品id Value的值:商品對(duì)象的json格式
@Configuration
@EnableScheduling
public class RedisTask {
@Autowired
private GoodsMapper goodsMapper;
@Autowired
private RedisTemplate<String,String> redisTemplate;
/**
* 每5秒執(zhí)行一次定時(shí)任務(wù),初始化一遍秒殺商品信息
* 緩存預(yù)熱
*/
@Scheduled(cron = "0/5 * * * * *")
public void initRedisGoods(){
System.out.println("緩存預(yù)熱...........");
//查詢所有秒殺商品數(shù)據(jù)
List<Goods> goodsList = goodsMapper.selectAllGoods();
//放入到Redis中
for(Goods goods:goodsList){
//因?yàn)楹罄m(xù)還需要查詢商品的詳情,如果將整個(gè)List放進(jìn)去,后續(xù)查詢?cè)斍槁闊┬? String goodsJSON = JSONObject.toJSONString(goods);
redisTemplate.opsForValue().set(Constants.REDIS_GOODS +goods.getId(),goodsJSON);
}
}
}
• 在15-seckill-service中GoodsMapper接口中添加selectAllGoods方法
/**
* 查詢所有商品
* @return
*/
List<Goods> selectAllGoods();
• 在GoodsMapper和OrderMapper接口上加@Mapper注解
@Mapper
public interface GoodsMapper {
OrderMapper我們也提前加上,避免后面忘了
• 在15-seckill-service中GoodsMappe.xml中實(shí)現(xiàn)selectAllGoods
目前我們表中所有商品都是秒殺商品,實(shí)際項(xiàng)目中,如果還有非秒殺商品,需要進(jìn)行過(guò)濾
<select id="selectAllGoods" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from goods
</select>
• 在15-seckill-service的pom.xml文件中添加對(duì)fastjson的支持
為了操作商品數(shù)據(jù)方便,后續(xù)我們需要改商品庫(kù)存,我們向Redis中放數(shù)據(jù)的時(shí)候,是以json字符串的形式存放的,所以導(dǎo)入對(duì)json的支持依賴
<!--fastjson對(duì)json處理的依賴-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.36</version>
</dependency>
• 在15-seckill-interface中的com.bjpowernode.seckill.constants包下定義常量類Constants,存放秒殺系統(tǒng)的常量
public class Constants {
/**
* 定義Redis中商品信息的key的前綴
* Redis中存放商品的格式:redis:goods:商品id
*/
public static final String REDIS_GOODS = "redis:goods:";
}
• 運(yùn)行15-seckill-service的Application,單獨(dú)對(duì)定時(shí)任務(wù)進(jìn)行測(cè)試
通過(guò)RedisDestopManager客戶端查看效果
? 在15-seckill-web中處理頁(yè)面的請(qǐng)求,從Redis中取商品數(shù)據(jù)響應(yīng)給前端頁(yè)面
• 在15-seckill-web的pom.xml文件中添加SpringBoot對(duì)Redis的支持依賴
<!-- 加載spring boot redis包 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
• 在15-seckill-web的核心配置文件application.properties中配置Redis連接信息
#配置redis連接信息
spring.redis.host=192.168.235.128
spring.redis.port=6379
spring.redis.password=123456
• 在15-seckill-web的pom.xml文件中添加對(duì)fastjson的支持,因?yàn)榫彺骖A(yù)熱向Redis中存放的商品是以json的形式存放的,所以我們這里要將json轉(zhuǎn)為商品對(duì)象
<!--fastjson對(duì)json處理的依賴-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.36</version>
</dependency>
• 在15-seckill-web 的com.bjpowernode.seckill.controller包下創(chuàng)建GoodsController類
@Controller
public class GoodsController {
@Autowired
private RedisTemplate<String,String> redisTemplate;
@GetMapping("/seckill/goods")
public String goods(Model model){
//從Redis獲取商品集合
//獲取所有存在Redis中的商品的key
Set<String> keys = redisTemplate.keys(Constants.REDIS_GOODS +"*");
List<String> goodsJSONList = redisTemplate.opsForValue().multiGet(keys);
List<Goods> goodsList = new ArrayList<Goods>();
for (String goodsJSON : goodsJSONList) {
Goods goods = JSONObject.parseObject(goodsJSON,Goods.class);
goodsList.add(goods);
}
model.addAttribute("goodsList",goodsList);
return "goods";
}
}
? 運(yùn)行15-seckill-web的Application,瀏覽器輸入http://localhost:8080/seckill/goods查看效果
? 數(shù)據(jù)可以正常顯示,但是圖片顯示不出來(lái)問(wèn)題解決
• 確認(rèn)數(shù)據(jù)庫(kù)商品表中的圖片路徑是否和當(dāng)前項(xiàng)目上下文及端口一致
• 將07-SecKill\resources下的image拷貝到15-seckill-web模塊的static目錄下
? 修改goods.html頁(yè)面樣式
• 商品div的寬度過(guò)寬:width: 285px;
• 讓商品排列展開:float:left;
• 每個(gè)商品間設(shè)置內(nèi)邊距:margin:18px;
• 整個(gè)頁(yè)面設(shè)置內(nèi)邊距: margin:18px;
• 每行展示4個(gè)商品:
th:style="${goodsStat.count % 5 eq 0}?'clear:both; float: left;width: 285px;':'width: 285px;float: left;'"
• 商品價(jià)錢調(diào)大,顏色為紅色
<span style="color: red;font-size:22px;font-weight: bold;">[[${goods.price}]]</span>
• 給商品圖片和商品名稱加超鏈接,點(diǎn)擊跳到詳情頁(yè),并在新窗口打開加超鏈接,并指定target=”_black”
!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>秒殺商品列表頁(yè)</title>
</head>
<body th:inline="text" style="margin: 20px;">
<div th:each="goods:${goodsList}"
th:style="${goodsStat.count % 5 eq 0}?
'clear:both; float: left;margin:20px;width: 285px;':'width: 285px;margin:20px;float: left;'">
<a th:href="@{'/seckill/goods/' + ${goods.getId()}}" target="_blank">
<img th:src="@{${goods.imageurl}}"><br>
</a>
<a th:href="@{'/seckill/goods/' + ${goods.getId()}}" target="_blank">
[[${goods.name}]] [[${goods.namedesc}]]<br>
</a>
<span style="color: red;font-size:22px;font-weight: bold;">[[${goods.price}]]</span>
剩余:[[${goods.store}]]件
</div>
</body>
</html>