1. 在15-seckill-web 的pom.xml文件添加Thymeleaf依赖
我们创建SpringBoot项目的时候,已经勾选,会自动生成
2. 在15-seckill-web的resources/templates下创建goods.html,用于商品列表展示
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>秒杀商品列表页</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页面
❶ 分析1:直接从数据库中查询获取所有商品合适吗?
• 查询所有秒杀商品,商品在数据库中存储
• 因为是秒杀场景,属于高并发大流量访问
• 如果我们在Controller层中直接查询数据库,会给数据库造成很大的压力,甚至会让数据库没有响应或者崩溃
• 优化:所以,我们这里不直接查询数据库,而是先将所有商品查询出来,放到Redis中,采用Redis抗住巨大的访问流量,这种提前将数据查询出来放到Redis的操作叫做“缓存预热”
❷ 分析2:从Redis取数据写在 15-seckill-web项目中还是在15-seckill- service项目中
• 直接写在15-seckill-web项目中,查询Redis获取商品列表
• 如果写在15-seckill-service项目中,需要采用RPC调用,会通过网络进行Socket连接,会有性能消耗,
• 这也是一个优化
❸ 分析3:什么时候向Redis中写入商品列表
在服务器启动之后,不间断的从数据库中查询商品信息,写入到Redis中
❹ 分析4:通过什么方式,不间断的查询数据库商品信息,写到Redis中可以在15-seckill-service中创建一个定时任务,通过定时任务实现
❺ 在15-seckill-service中完成定时查询数据库向Redis放商品
• 在15-seckill-service的pom.xml文件中添加SpringBoot对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依赖和数据库驱动依赖
<!-- 加载mybatis整合springboot -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<!--在springboot的父工程中没有指定版本,我们需要手动指定-->
<version>1.3.2</version>
</dependency>
<!-- MySQL的jdbc驱动包 -->
<dependency>
<groupId>mysql</groupId>
<!--在springboot的父工程中指定了版本,我们就不需要手动指定了-->
<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数据库连接信息
#数据库的连接配置信息
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包下创建定时任务类RedisTask,缓存商品信息到Redis中Redis Key的格式 redis:store:商品id Value的值:商品对象的json格式
@Configuration
@EnableScheduling
public class RedisTask {
@Autowired
private GoodsMapper goodsMapper;
@Autowired
private RedisTemplate<String,String> redisTemplate;
/**
* 每5秒执行一次定时任务,初始化一遍秒杀商品信息
* 缓存预热
*/
@Scheduled(cron = "0/5 * * * * *")
public void initRedisGoods(){
System.out.println("缓存预热...........");
//查询所有秒杀商品数据
List<Goods> goodsList = goodsMapper.selectAllGoods();
//放入到Redis中
for(Goods goods:goodsList){
//因为后续还需要查询商品的详情,如果将整个List放进去,后续查询详情麻烦些
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中实现selectAllGoods
目前我们表中所有商品都是秒杀商品,实际项目中,如果还有非秒杀商品,需要进行过滤
<select id="selectAllGoods" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from goods
</select>
• 在15-seckill-service的pom.xml文件中添加对fastjson的支持
为了操作商品数据方便,后续我们需要改商品库存,我们向Redis中放数据的时候,是以json字符串的形式存放的,所以导入对json的支持依赖
<!--fastjson对json处理的依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.36</version>
</dependency>
• 在15-seckill-interface中的com.bjpowernode.seckill.constants包下定义常量类Constants,存放秒杀系统的常量
public class Constants {
/**
* 定义Redis中商品信息的key的前缀
* Redis中存放商品的格式:redis:goods:商品id
*/
public static final String REDIS_GOODS = "redis:goods:";
}
• 运行15-seckill-service的Application,单独对定时任务进行测试
通过RedisDestopManager客户端查看效果
❻ 在15-seckill-web中处理页面的请求,从Redis中取商品数据响应给前端页面
• 在15-seckill-web的pom.xml文件中添加SpringBoot对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文件中添加对fastjson的支持,因为缓存预热向Redis中存放的商品是以json的形式存放的,所以我们这里要将json转为商品对象
<!--fastjson对json处理的依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.36</version>
</dependency>
• 在15-seckill-web 的com.bjpowernode.seckill.controller包下创建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";
}
}
❼ 运行15-seckill-web的Application,浏览器输入http://localhost:8080/seckill/goods查看效果
❽ 数据可以正常显示,但是图片显示不出来问题解决
• 确认数据库商品表中的图片路径是否和当前项目上下文及端口一致
• 将07-SecKill\resources下的image拷贝到15-seckill-web模块的static目录下
❾ 修改goods.html页面样式
• 商品div的宽度过宽:width: 285px;
• 让商品排列展开:float:left;
• 每个商品间设置内边距:margin:18px;
• 整个页面设置内边距: margin:18px;
• 每行展示4个商品:
th:style="${goodsStat.count % 5 eq 0}?'clear:both; float: left;width: 285px;':'width: 285px;float: left;'"
• 商品价钱调大,颜色为红色
<span style="color: red;font-size:22px;font-weight: bold;">[[${goods.price}]]</span>
• 给商品图片和商品名称加超链接,点击跳到详情页,并在新窗口打开加超链接,并指定target=”_black”
!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>秒杀商品列表页</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>