秒杀项目
秒杀项目基本环境搭建
商品展示模块
请求执行秒杀模块
秒杀流程总结

商品列表展示模块

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>

 

全部教程