day14_用户前台项目环境搭建(首页接口开发,分类接口开发,网关服务搭建,Redis缓存,Spring Cache)

文章目录

  • 1 尚品甄选H5介绍
    • 1.1 业务功能介绍
    • 1.2 系统架构
    • 1.3 前端H5开发说明
  • 2 搭建项目环境
    • 2.1 项目结构说明
    • 2.2 模块依赖说明
    • 2.3 环境说明
    • 2.4 项目模块创建
      • 2.4.1 spzx-parent
      • 2.4.2 spzx-service
      • 2.4.43 service-product
    • 2.5 导入接口文档
  • 3 首页接口开发
    • 3.1 需求分析
    • 3.3 接口开发
      • 3.3.1 IndexVo
      • 3.3.2 IndexController
      • 3.3.3 商品一级分类接口
        • CategoryService
        • CategoryMapper
        • CategoryMapper.xml
      • 3.3.4 畅销商品列表接口
        • ProductService
        • ProductSkuMapper
        • ProductSkuMapper.xml
      • 3.3.5 Knife4j调试接口
        • 配置前端H5接口
        • Knife4j调试首页接口
        • H5对接本地接口
      • 3.3.6 接口测试
  • 4 分类接口开发
    • 4.1 需求分析
    • 4.2 接口开发
      • 4.2.1 CategoryController
      • 4.2.2 CategoryService
      • 4.2.3 CategoryMapper
      • 4.2.4 CategoryMapper.xml
      • 4.2.5 接口测试
  • 5 网关服务搭建
    • 5.1 网关服务说明
    • 5.2 服务网关搭建
    • 5.3 服务网关测试
  • 6 Redis缓存
    • 6.1 使用缓存
    • 6.2 Spring Cache
      • 6.2.1 介绍
      • 6.2.2 注解
      • 6.2.3 入门程序
      • 6.2.4 其他注解
        • @CachePut
        • @CacheEvict
    • 6.3 缓存所有分类数据

1 尚品甄选H5介绍

本章讲解尚品甄选H5端介绍。

1.1 业务功能介绍

首页、商品分类查询、关键字查询、商品详情、注册、登录、购物车模块、用户收货地址管理、订单模块、支付…

项目演示地址:http://spzx.atguigu.cn/

1.2 系统架构

服务器端架构:SpringBoot3 + Spring Cloud + Spring Cloud Alibaba(Nacos/Sentinel) + MyBatis + Redis + Mysql

前端架构:Uni-App

1.3 前端H5开发说明

前端H5部分我们不需要开发,我们只需要根据接口文档开发微服务接口,然后对接到写好的前端H5即可

导入课程资料:spzx-h5

部署spzx-h5:

1、docker部署nignx

#拉取镜像
docker pull nginx:latest

#运行nginx镜像
docker run -d -p 80:80 \
--name nginx-spzx \
-v nginx_html:/usr/share/nginx/html \
-v nginx_conf:/etc/nginx/ \
-v nginx_logs:/var/log/nginx  nginx

# nginx-spzx容器的管理命令
docker start nginx-spzx			# 启动容器
docker stop nginx-spzx			# 关闭容器
docker rm nginx-spzx			# 删除容器

#使用浏览器访问测试:http://ip

2、部署spzx-h5

删除/var/lib/docker/volumes/nginx_html/_data目录下的文件,将spzx-h5目录下的文件上传到该目录下。

浏览器访问:http://ip,打开手机模式

在这里插入图片描述

3、H5调用本地接口设置

我的 => 设置 =》 接口base路径,改为本地网关地址:http://IP:端口

注意:http://139.198.163.91:8400为云服务接口地址

在这里插入图片描述

2 搭建项目环境

本章讲解在spzx-parent基础上搭建尚品甄选H5端微服务开发环境。

2.1 项目结构说明

尚品甄选H5端微服务项目结构如下所示:

在这里插入图片描述

模块说明:

spzx-parent: 尚品甄选项目的父工程,进行项目依赖的统一管理,打包方式为pom

​ spzx-common: 尚品甄选项目公共模块的管理模块

​ common-util: 工具类模块

​ common-service:公共服务模块

​ spzx-model: 尚品甄选项目实体类模块

​ spzx-manager: 尚品甄选项目后台管理系统的后端服务(忽略、前面已讲)

​ spzx-server-gateway: 尚品甄选项目的网关

​ spzx-service: 尚品甄选微服务模块的父工程

​ service-product:商品微服务模块

​ service-user:用户微服务模块

​ service-order:订单微服务模块

​ spzx-service-client: 尚品甄选微服务模块远程调用接口的父工程

​ service-product-client:商品微服务远程调用接口模块

​ service-user-client:用户微服务远程调用接口模块

2.2 模块依赖说明

模块之间的依赖关系如下图所示:

在这里插入图片描述

对应说明:

1、common-service依赖common-util、spzx-mode

2、spzx-service依赖common-service、service-product-client、service-user-client

3、spzx-server-gateway依赖common-util、spzx-mode

2.3 环境说明

本次项目开发的时候所使用的软件环境版本如下所示:

软件名称版本说明
jdkjdk17
spring boot3.0.5
spring cloud2022.0.2
spring cloud alibaba2022.0.0.0-RC2
redis7.0.10
mybaits-spring-boot-starter3.0.1
mysql8.0.29
idea2022.2.2
nacos server2.2.1
sentinel dashboard2.0.0-alpha-preview

2.4 项目模块创建

2.4.1 spzx-parent

添加微服务依赖:

<properties>
    ...

    <cloud.version>2022.0.2</cloud.version>
    <alibaba.version>2022.0.0.0-RC2</alibaba.version>
    <alipay.version>4.8.73.ALL</alipay.version>
</properties>

<!-- 管理依赖,版本锁定 -->
<dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${cloud.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-dependencies</artifactId>
            <version>${alibaba.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <!--导入支付宝支付sdk-->
        <dependency>
            <groupId>com.alipay.sdk</groupId>
            <artifactId>alipay-sdk-java</artifactId>
            <version>${alipay.version}</version>
        </dependency>

        ...
    </dependencies>
</dependencyManagement>

2.4.2 spzx-service

在spzx-parent下面创建该子模块,并导入如下依赖:

<dependencies>

    <dependency>
        <groupId>com.atguigu.spzx</groupId>
        <artifactId>common-service</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>

    <!-- mybatis的起步依赖 -->
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
    </dependency>

    <!-- mysql驱动 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-configuration-processor</artifactId>
        <optional>true</optional>
    </dependency>

    <dependency>
        <groupId>com.github.pagehelper</groupId>
        <artifactId>pagehelper-spring-boot-starter</artifactId>
        <version>1.3.0</version>
    </dependency>

    <!-- 服务注册 -->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>

    <!-- 流量控制 -->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    </dependency>

</dependencies>

注意:删除src目录、修改项目packaging类型为pom

2.4.43 service-product

在spzx-service下面创建子模块service-product.在service-product服务的src/resources目录下创建application.yml、application-dev.yml文件,文件的内容如下所示:

application.yml

spring:
  profiles:
    active: dev

application-dev.yml

server:
  port: 8511

spring:
  application:
    name: service-product
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.136.142:8848
    sentinel:
      transport:
        dashboard: localhost:8080
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://192.168.136.142:3306/db_spzx?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true&useSSL=true
    username: root
    password: 1234

mybatis:
  config-location: classpath:mybatis-config.xml
  mapper-locations: classpath:mapper/*/*.xml

导入课程资料中提供的:logback-spring.xml配置文件,修改输出路径:

<property name="log.path" value="F://logs//service-product//logs" />

启动类创建

package com.atguigu.spzx.product;

@SpringBootApplication
public class ProductApplication {

    public static void main(String[] args) {
        SpringApplication.run(ProductApplication.class, args);
    }

}

2.5 导入接口文档

将课程资料中所提供的接口文档(OpenAPI文件夹下的json文件)导入到Apifox中,具体步骤如下所示:

1、在Apifox中创建项目

2、进入项目 ----> 项目设置 ----> 导入数据 -----> 点击或者拖拽文件到本区域导入

在这里插入图片描述

3、导出完毕以后可以在接口中管理中看到对应的接口信息

在这里插入图片描述

3 首页接口开发

3.1 需求分析

需求说明:在前台系统首页展示商品一级分类数据以及畅销商品列表数据,如下所示:

在这里插入图片描述

需求分析:

商品一级分类:查询category表,获取parent_id="0"的数据列表

畅销商品列表:查询product_sku表,根据sale_num字段排序,取前20条数据列表

整体的访问流程如下所示:

在这里插入图片描述

3.3 接口开发

对比着接口文档进行相关接口的开发。

查看接口文档:

首页接口地址及示例数据

get  /api/product/index
返回结果:
{
    "code": 200,
    "message": "成功",
    "data": {
        "productSkuList": [
            {
                "id": 1,
                "createTime": "2023-05-25 22:21:07",
                "skuCode": "1_0",
                "skuName": "小米 红米Note10 5G手机 颜色:白色 内存:8G",
                "productId": 1,
                "thumbImg": "http://139.198.127.41:9000/spzx/20230525/665832167-5_u_1 (1).jpg",
                "salePrice": 1999.00,
                "marketPrice": 2019.00,
                "costPrice": 1599.00,
                "stockNum": 99,
                "saleNum": 1,
                "skuSpec": "颜色:白色,内存:8G",
                "weight": "1.00",
                "volume": "1.00",
                "status": null,
                "skuSpecList": null
            },
            ...
        ],
        "categoryList": [
            {
                "id": 1,
                "createTime": "2023-05-22 23:30:28",
                "name": "数码办公",
                "imageUrl": "https://lilishop-oss.oss-cn-beijing.aliyuncs.com/230f48f024a343c6be9be72597c2dcd0.png",
                "parentId": 0,
                "status": 1,
                "orderNum": 1,
                "hasChildren": null,
                "children": null
            },
            ...
        ]
    }
}

3.3.1 IndexVo

定义一个实体类来封装要给前端详细的数据,如下所示:

// com.atguigu.spzx.model.vo.h5;
@Data
public class IndexVo {

    private List<Category> categoryList ;       // 一级分类的类别数据
    private List<ProductSku> productSkuList ;   // 畅销商品列表数据

}

3.3.2 IndexController

操作模块:service-product

表现层代码:

// com.atguigu.spzx.product.controller;

@Tag(name = "首页接口管理")
@RestController
@RequestMapping(value="/api/product/index")
@SuppressWarnings({"unchecked", "rawtypes"})
public class IndexController {

   @Autowired
   private CategoryService categoryService;

   @Autowired
   private ProductService productService;

   @Operation(summary = "获取首页数据")
   @GetMapping
   public Result<IndexVo> findData(){
      List<Category> categoryList = categoryService.findOneCategory();
      List<ProductSku> productSkuList = productService.findProductSkuBySale();
      IndexVo indexVo = new IndexVo() ;
      indexVo.setCategoryList(categoryList);
      indexVo.setProductSkuList(productSkuList);
      return Result.build(indexVo , ResultCodeEnum.SUCCESS);
   }

}

3.3.3 商品一级分类接口

CategoryService

业务层代码实现

// 业务接口
public interface CategoryService {

    List<Category> findOneCategory();

}

// 接口实现类
@Service
public class CategoryServiceImpl implements CategoryService {

	@Autowired
	private CategoryMapper categoryMapper;

    @Override
    public List<Category> findOneCategory() {
        return categoryMapper.findOneCategory();
    }
}
CategoryMapper

持久层代码实现

@Mapper
public interface CategoryMapper {

    List<Category> findOneCategory();
}
CategoryMapper.xml

在映射文件中定义对应的sql语句

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">


<mapper namespace="com.atguigu.spzx.product.mapper.CategoryMapper">

   <resultMap id="categoryMap" type="com.atguigu.spzx.model.entity.product.Category" autoMapping="true">
   </resultMap>
   
   <!-- 用于select查询公用抽取的列 -->
   <sql id="columns">
      id,name,image_url,parent_id,status,order_num,create_time,update_time,is_deleted
   </sql>

   <select id="findOneCategory" resultMap="categoryMap">
      select <include refid="columns" />
      from category
      where
          parent_id = 0
         and status = 1
         and is_deleted = 0
      order by order_num
   </select>

</mapper>

3.3.4 畅销商品列表接口

ProductService

业务层代码实现

// 业务接口
public interface ProductService {

    List<ProductSku> findProductSkuBySale();

}

// 接口实现类
@Service
public class ProductServiceImpl implements ProductService {

    @Autowired
    private ProductSkuMapper productSkuMapper;

    @Override
    public List<ProductSku> findProductSkuBySale() {
        return productSkuMapper.findProductSkuBySale();
    }
}
ProductSkuMapper

持久层代码实现

@Mapper
public interface ProductSkuMapper {

    List<ProductSku> findProductSkuBySale();
}
ProductSkuMapper.xml

在映射文件中定义对应的sql语句

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.atguigu.spzx.product.mapper.ProductSkuMapper">

   <resultMap id="productSkuMap" type="com.atguigu.spzx.model.entity.product.ProductSku" autoMapping="true">
   </resultMap>
   
   <!-- 用于select查询公用抽取的列 -->
   <sql id="columns">
      id,sku_code,sku_name,product_id,thumb_img,sale_price,market_price,cost_price,stock_num,sale_num,sku_spec,weight,volume,status,create_time,update_time,is_deleted
   </sql>

   <select id="findProductSkuBySale" resultMap="productSkuMap">
      select
          sku.id,sku.sku_code,sku.sku_name,sku.product_id,sku.thumb_img,sku.sale_price,sku.market_price,sku.cost_price,sku.stock_num,sku.sale_num,sku.sku_spec,sku.weight,sku.volume,sku.status,sku.create_time,sku.update_time,sku.is_deleted
      from product_sku sku
         left join product p on p.id = sku.product_id
      where
         p.status = 1
         and p.audit_status = 1
         and sku.is_deleted = 0
         and p.is_deleted = 0
         order by sku.sale_num desc
      limit 20
   </select>

</mapper>

3.3.5 Knife4j调试接口

配置前端H5接口

在common-service模块Knife4jConfig类添加配置信息:

@Bean
public GroupedOpenApi webApi() {      // 创建了一个api接口的分组
    return GroupedOpenApi.builder()
            .group("web-api")         // 分组名称
            .pathsToMatch("/api/**")  // 接口请求路径规则
            .build();
}
Knife4j调试首页接口

在这里插入图片描述

H5对接本地接口

启动nginx,访问:http://ip

我的 => 设置 =》 接口base路径,改为本地网关地址:http://IP:端口

请求

在这里插入图片描述

数据

在这里插入图片描述

3.3.6 接口测试

测试方向:

1、后端接口单独测试

2、配合前端项目测试

后端地址配置:

在这里插入图片描述

解决跨域问题:在IndexController类上添加**@CrossOrigin**注解接口跨域问题

4 分类接口开发

4.1 需求分析

需求说明:当用户点击分类导航按钮的时候,那么此时就需要将系统中所对应的所有的分类数据查询出来,如下所示:

在这里插入图片描述

展示全部商品三级联动的分类数据,查询category数据表

查看接口文档:

分类数据接口地址及示例数据

get  /api/product/category/findCategoryTree
返回结果:
{
    "code": 200,
    "message": "成功",
    "data": [
        {
            "id": 1,
            "createTime": "2023-05-22 23:30:28",
            "name": "数码办公",
            "imageUrl": "https://lilishop-oss.oss-cn-beijing.aliyuncs.com/230f48f024a343c6be9be72597c2dcd0.png",
            "parentId": 0,
            "status": 1,
            "orderNum": 1,
            "hasChildren": null,
            "children": [
                {
                    "id": 2,
                    "createTime": "2023-05-22 23:30:28",
                    "name": "手机通讯",
                    "imageUrl": "",
                    "parentId": 1,
                    "status": 1,
                    "orderNum": 0,
                    "hasChildren": null,
                    "children": [
                        {
                            "id": 3,
                            "createTime": "2023-05-22 23:30:28",
                            "name": "手机",
                            "imageUrl": "https://lilishop-oss.oss-cn-beijing.aliyuncs.com/1348576427264204943.png",
                            "parentId": 2,
                            "status": 1,
                            "orderNum": 0,
                            "hasChildren": null,
                            "children": null
                        },
                        {
                            "id": 4,
                            "createTime": "2023-05-22 23:30:28",
                            "name": "对讲机",
                            "imageUrl": "https://lilishop-oss.oss-cn-beijing.aliyuncs.com/1348576427264204944.png",
                            "parentId": 2,
                            "status": 1,
                            "orderNum": 0,
                            "hasChildren": null,
                            "children": null
                        },
                        ...
                    ]
                },
                ...
            ]
        },
        ...
    ]
}

4.2 接口开发

操作模块:service-product

4.2.1 CategoryController

表现层代码:

@Tag(name = "分类接口管理")
@RestController
@RequestMapping(value="/api/product/category")
@SuppressWarnings({"unchecked", "rawtypes"})
public class CategoryController {
   
   @Autowired
   private CategoryService categoryService;

   @Operation(summary = "获取分类树形数据")
   @GetMapping("findCategoryTree")
   public Result<List<Category>> findCategoryTree(){
      List<Category> list = categoryService.findCategoryTree();
      return Result.build(list,  ResultCodeEnum.SUCCESS);
   }

}

4.2.2 CategoryService

业务层代码实现

// 业务接口
List<Category> findCategoryTree();

// 接口实现类
@Override
public List<Category> findCategoryTree() {
    List<Category> categoryList = categoryMapper.findAll();
    //全部一级分类
    List<Category> oneCategoryList = categoryList.stream().filter(item -> item.getParentId().longValue() == 0).collect(Collectors.toList());
    if(!CollectionUtils.isEmpty(oneCategoryList)) {
        oneCategoryList.forEach(oneCategory -> {
            List<Category> twoCategoryList = categoryList.stream().filter(item -> item.getParentId().longValue() == oneCategory.getId().longValue()).collect(Collectors.toList());
            oneCategory.setChildren(twoCategoryList);
            if(!CollectionUtils.isEmpty(twoCategoryList)) {
                twoCategoryList.forEach(twoCategory -> {
                    List<Category> threeCategoryList = categoryList.stream().filter(item -> item.getParentId().longValue() == twoCategory.getId().longValue()).collect(Collectors.toList());
                    twoCategory.setChildren(threeCategoryList);
                });
            }
        });
    }
    return oneCategoryList;
}

4.2.3 CategoryMapper

持久层代码实现

List<Category> findAll();

4.2.4 CategoryMapper.xml

在映射文件中定义对应的sql语句

<select id="findAll" resultMap="categoryMap">
   select <include refid="columns" />
   from category
   where
   status = 1
   and is_deleted = 0
   order by order_num
</select>

4.2.5 接口测试

测试方向:

1、后端接口单独测试

2、配合前端项目测试

5 网关服务搭建

5.1 网关服务说明

后期针对前台系统会创建很多个后端微服务,前端在访问后端微服务的时候需要通过服务网关进行访问,如下图所示:

在这里插入图片描述

5.2 服务网关搭建

步骤:

1、在spzx-parent项目下创建一个spzx-server-gateway模块,并加入依赖

<dependencies>

    <dependency>
        <groupId>com.atguigu.spzx</groupId>
        <artifactId>common-util</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>

    <dependency>
        <groupId>com.atguigu.spzx</groupId>
        <artifactId>spzx-model</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>

    <!-- loadbalancer依赖 -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-loadbalancer</artifactId>
    </dependency>

    <!-- 服务注册 -->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>

    <!-- 服务保护组件 -->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    </dependency>

</dependencies>

2、在classpath路径下编写两个配置文件:application.yml、application-dev.yml,文件内容分别如下所示:

application.yml文件内容:

spring:
  profiles:
    active: dev

application-dev.yml文件内容:

server:
  port: 8500
spring:
  application:
    name: spzx-server-gateway
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.136.142:8848
    sentinel:
      transport:
        dashboard: localhost:8080
    gateway:
      discovery: #是否与服务发现组件进行结合,通过 serviceId(必须设置成大写) 转发到具体的服务实例。默认为false,设为true便开启通过服务中心的自动根据 serviceId 创建路由的功能。
        locator: #路由访问方式:http://Gateway_HOST:Gateway_PORT/大写的serviceId/**,其中微服务应用名默认大写访问。
          enabled: true
      globalcors:
        cors-configurations:
          '[/**]':
            allowedOriginPatterns: "*"
            # 允许请求中携带的头信息
            allowedHeaders: "*"
            # 运行跨域的请求方式
            allowedMethods: "*"
            # 跨域检测的有效期,单位s
            maxAge: 36000
      routes:
        - id: service-product
          uri: lb://service-product
          predicates:
            - Path=/*/product/**

3、编写启动类

// com.atguigu.spzx.gateway;
@SpringBootApplication
public class GatewayApplication {

    public static void main(String[] args) {
        SpringApplication.run(GatewayApplication.class , args) ;
    }

}

4、导入课程资料中提供的:logback-spring.xml配置文件,修改输出路径:

<property name="log.path" value="D://logs//spzx-server-gateway//logs" />

5.3 服务网关测试

具体步骤:

1、注释掉service-product微服务两个controller上的@CrossOrigin注解

2、后端地址配置

在这里插入图片描述

6 Redis缓存

分类数据一般情况下不会做过多的修改,因此可以将分类数据进行缓存,以提高页面的加载速度。

6.1 使用缓存

先将首页接口获取一级分类数据缓存

步骤:

1、在service-product微服务中集成Spring Data Redis,如下所示:

在service-product的pom.xml文件中添加如下依赖:

<!-- redis的起步依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

在application-dev.yml文件中添加如下Redis的相关配置:

spring:
  # Redis的相关配置
  data:
    redis:
      host: 192.168.136.142
      port: 6379
      password: 1234

2、对CategoryServiceImpl的findOneCategory方法进行改造,如下所示:

@Autowired
private RedisTemplate<String , String> redisTemplate ;

@Override
public List<Category> findOneCategory() {

    // 从Redis缓存中查询所有的一级分类数据
    String categoryListJSON = redisTemplate.opsForValue().get("category:one");
    if(!StringUtils.isEmpty(categoryListJSON)) {
        List<Category> categoryList = JSON.parseArray(categoryListJSON, Category.class);
        log.info("从Redis缓存中查询到了所有的一级分类数据");
        return categoryList ;
    }

    List<Category> categoryList = categoryMapper.findOneCategory();
    log.info("从数据库中查询到了所有的一级分类数据");
    redisTemplate.opsForValue().set("category:one" , JSON.toJSONString(categoryList) , 7 , TimeUnit.DAYS);
    return categoryList ;
}

3、启动程序进行测试:第一次查询从MySQL数据库获取,第二次查询从Redis获取

说明:针对查询所有分类的接口,也可以使用上述思想对分类数据进行缓存。

6.2 Spring Cache

6.2.1 介绍

Spring Cache是一个框架,实现了基于注解的缓存功能,只需要简单地加一个注解,就能实现缓存功能,大大简化我们在业务中操作缓存的代码

Spring Cache只是提供了一层抽象,底层可以切换不同的cache实现。具体就是通过CacheManager接口来统一不同的缓存技术。CacheManager是Spring提供的各种缓存技术抽象接口。

针对不同的缓存技术需要实现不同的CacheManager:

CacheManager描述
EhCacheCacheManager使用EhCache作为缓存技术
GuavaCacheManager使用Google的GuavaCache作为缓存技术
RedisCacheManager使用Redis作为缓存技术

6.2.2 注解

在SpringCache中提供了很多缓存操作的注解,常见的是以下的几个:

注解说明
@EnableCaching开启缓存注解功能
@Cacheable在方法执行前spring先查看缓存中是否有数据,如果有数据,则直接返回缓存数据;若没有数据,调用方法并将方法返回值放到缓存中
@CachePut将方法的返回值放到缓存中
@CacheEvict将一条或多条数据从缓存中删除

在spring boot项目中,使用缓存技术只需在项目中导入相关缓存技术的依赖包,并在启动类上使用**@EnableCaching**开启缓存支持即可。

例如,使用Redis作为缓存技术,只需要导入Spring data Redis的maven坐标即可。

6.2.3 入门程序

接下来,我们将通过一个入门案例来演示一下SpringCache的常见用法。 上面我们提到,SpringCache可以集成不同的缓存技术,如Redis、Ehcache

本次我们直接使用Redis作为缓存技术。

需求:给spring-cache-demo项目中的查询用户方法添加查询缓存逻辑

具体步骤:

1、打开课程资料中所提供的spring-cache-demo项目

2、在spring-cache-demo项目的pom.xml文件中添加如下依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

3、在application.yml文件中配置Redis的连接信息

spring:
  data:
    redis:
      host: 192.168.136.142
      port: 6379
      password: 1234

4、配置Redis的key的序列化器

// com.atguigu.spzx.cache.config;
@Configuration
public class RedisConfig {

    @Bean
    public CacheManager cacheManager(LettuceConnectionFactory connectionFactory) {

        //定义序列化器
        GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();


        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                //过期时间600秒
                .entryTtl(Duration.ofSeconds(600))
                // 配置序列化
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(stringRedisSerializer))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(genericJackson2JsonRedisSerializer));

        RedisCacheManager cacheManager = RedisCacheManager.builder(connectionFactory)
                .cacheDefaults(config)
                .build();

        return cacheManager;
    }

}

5、在启动类上添加**@EnableCaching**注解

6、在UserServiceImpl类中的findById方法上添加**@Cacheable**注解

@Override
@Cacheable(value = "userCache" , key = "#userId")
public User findById(Long userId) {
    log.info("用户数据查询成功...");
    User user = new User() ;
    user.setAge(23);
    user.setUserName("尚硅谷");
    return user;
}

7、启动程序进行测试:第一次查询时候会打印日志,第二次查询的时候由于Redis中已经存在了数据,因此直接从Redis中进行命中

@Cacheable 说明:

1、作用: 在方法执行前,spring先查看缓存中是否有数据,如果有数据,则直接返回缓存数据;若没有数据,调用方法并将方法返回值放到缓存中

2、value: 缓存的名称,每个缓存名称下面可以有多个key

3、key: 缓存的key ----------> 支持Spring的表达式语言SPEL语法

6.2.4 其他注解

@CachePut

作用: 将方法返回值,放入缓存

value: 缓存的名称, 每个缓存名称下面可以有很多key

key: 缓存的key ----------> 支持Spring的表达式语言SPEL语法

当前UserController的save方法是用来保存用户信息的,我们希望在该用户信息保存到数据库的同时,也往缓存中缓存一份数据,我们可以在save方

法上加上注解 @CachePut,用法如下:

@CachePut(value = "userCache", key = "#user.userName")
public User saveUser(User user) {
    log.info("用户数据保存成功...");
    return user ;
}

key的写法如下

#user.id : #user指的是方法形参的名称, id指的是user的id属性 , 也就是使用user的id属性作为key ;

#user.userName: #user指的是方法形参的名称, name指的是user的name属性 ,也就是使用user的name属性作为key ;

#result.id : #result代表方法返回值,该表达式 代表以返回对象的id属性作为key ;

#result.userName: #result代表方法返回值,该表达式 代表以返回对象的name属性作为key ;

@CacheEvict

作用: 清理指定缓存

value: 缓存的名称,每个缓存名称下面可以有多个key

key: 缓存的key ----------> 支持Spring的表达式语言SPEL语法

当我们在删除数据库user表的数据的时候,我们需要删除缓存中对应的数据,此时就可以使用**@CacheEvict**注解, 具体的使用方式如下:

@CacheEvict(value = "userCache" , key = "#userId")
public void deleteById(Long userId) {
    log.info("用户数据删除成功...");
}

6.3 缓存所有分类数据

需求:给查询所有的分类数据添加查询缓存,使用Spring Cache框架

步骤:

1、在service-product服务中的pom.xml文件中添加依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>

2、配置Redis的key的序列化器

// com.atguigu.spzx.cache.config;
@Configuration
public class RedisConfig {

    @Bean
    public CacheManager cacheManager(LettuceConnectionFactory connectionFactory) {

        //定义序列化器
        GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();


        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                //过期时间600秒
                .entryTtl(Duration.ofSeconds(600))
                // 配置序列化
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(stringRedisSerializer))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(genericJackson2JsonRedisSerializer));

        RedisCacheManager cacheManager = RedisCacheManager.builder(connectionFactory)
                .cacheDefaults(config)
                .build();

        return cacheManager;
    }

}

3、在启动类上添加**@EnableCaching**注解

4、在CategoryServiceImpl类中的findCategoryTree方法上添加**@Cacheable**注解

@Cacheable(value = "category" , key = "'all'")
public List<Category> findAllCategory() {
..
    return oneCategoryList;
}

5、启动程序进行测试

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/433359.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

LLM 应用的新兴架构

原文地址&#xff1a;emerging-architectures-for-llm-applications 大语言模型&#xff08;LLM&#xff09;为软件构建提供了一种强大的新方法。由于这种技术相对较新&#xff0c;且其运作方式与传统计算资源大相径庭&#xff0c;如何有效利用它们并不是显而易见的。 在这篇…

2024年护眼台灯哪家品牌好?五款优质品牌专业推荐

护眼台灯几乎是每个孩子书桌上都会有的灯具&#xff0c;但还是有不少家长觉得是“智商税”。其实护眼台灯好处非常多&#xff0c;列如能够提供舒适的照明&#xff0c;缓解用眼疲劳&#xff0c;预防近视等等。所以今天准备了一期护眼台灯测评&#xff0c;并附上护眼台灯的榜单&a…

QT:颜色选择器

普通 Qt提供了一个现成的QColorDialog类。 用法: #include <QColorDialog>QColor color QColorDialog::getColor(Qt::white, this); if(!color.isValid()){//点击 关闭 或 cancel 颜色无效 }else {ui->text->setText(color.name());//类似##ffffQRgb rgb colo…

爬虫实战——scrapy框架爬取多张图片

scrapy框架的基本使用&#xff0c;请参考我的另一篇文章&#xff1a;scrapy框架的基本使用 起始爬取的网页如下&#xff1a; 点击每张图片&#xff0c;可以进入图片的详情页&#xff0c;如下&#xff1a; 代码实现&#xff1a; 项目文件结构如下 img_download.py文件代码 im…

微信小程序开发系列(八)·微信小程序页面的划分以及轮播图区域的绘制和图片的添加

目录 1. 划分页面结构 2. 轮播图区域绘制 3. 轮播图图片添加 1. 划分页面结构 最终我们想达到如下效果&#xff1a; 其页面分为四层结构&#xff0c;因此我们需要配置四块view&#xff0c;代码如下&#xff1a; <!-- view 小程序提供的容器组件&#xff0c;可以当成…

算法沉淀——动态规划之其它背包问题与卡特兰数(leetcode真题剖析)

算法沉淀——动态规划之其它背包问题与卡特兰数 二维费用的背包问题01.一和零02.盈利计划 似包非包组合总和 Ⅳ 卡特兰数不同的二叉搜索树 二维费用的背包问题 01.一和零 题目链接&#xff1a;https://leetcode.cn/problems/ones-and-zeroes/ 给你一个二进制字符串数组 strs…

Springboot自动装配的原理

Springboot自动装配 1.前言2.EnableAutoConfiguration3.AutoConfigurationImportSelector4.spring.factories5.总结 1.前言 为什么springboot能够开箱即用&#xff0c;就是由于Springboot的自动装配。我们知道项目启动的方法上面都有一个注解SpringBootApplication&#xff0c…

武汉灰京文化:游戏市场推广与用户增长的成功典范

作为游戏行业的明星企业&#xff0c;武汉灰京文化在市场推广和用户增长方面的成功经验备受瞩目。他们以创造性和独特性的市场营销策略&#xff0c;成功吸引了大量用户。这不仅提高了其游戏的知名度&#xff0c;还为公司带来了持续的增长。这一成功模式不仅对公司自身有益&#…

计算机中msvcp140.dll,丢失怎么修复与解决

一、msvcp140.dll20个软件环境 msvcp140.dll文件是许多软件运行环境的组成部分&#xff0c;通常与Microsoft Visual C Redistributable关联。以下是可能使用该文件的软件环境&#xff1a; 微软办公软件&#xff1a;如Microsoft Office套件&#xff0c;包括Word、Excel、Power…

仓库福音!三防工业手机:远程RFID和条形码扫描仪的优势

在仓库中&#xff0c;由于堆货量众多&#xff0c;仓库管理员想要细分货物的种类十分困难&#xff0c;因此保持准确的库存记录至关重要&#xff0c;这样公司就不会导致货物积压。资产跟踪也可能是繁琐的任务之一&#xff0c;会对公司产生重大影响。没有为特定部件记录准确或错误…

[Echart]图谱中的富文本标签

[Echart]图谱中的富文本标签 series-graph.links.label.rich option {title: {text: Basic Graph},tooltip: {},animationDurationUpdate: 1500,animationEasingUpdate: quinticInOut,series: [{type: graph,// layout: force,symbolSize: 50,roam: true,label: {show: tru…

C及C++每日练习(2)

1.选择&#xff1a; 1.使用printf函数打印一个double类型的数据&#xff0c;要求&#xff1a;输出为10进制&#xff0c;输出左对齐30个字符&#xff0c;4位精度。以下哪个选项是正确的&#xff1f; A.%-30.4e B.%4.30e C.%-30.4f D.%-4.30 在上一篇文章中&#xff0c;提到了…

社科院与杜兰大学金融管理硕士——精心准备,只为那一刻的刚刚好

我们每个人都是夜空中独一无二的那颗星&#xff0c;静静地照耀&#xff0c;期待着照亮自己的宇宙。我们的每一步前行&#xff0c;都像是星尘累积&#xff0c;汇聚成璀璨的光轨&#xff0c;照亮未来的道路。正如我们现在努力申请的社科院与杜兰大学金融管理硕士项目&#xff0c;…

深圳/广州/厦门/上海/宁波/义乌2024最新跨境电商展计划表发布!

全国各城市2024年跨境电商年度效果好的跨境展会排期表来了&#xff0c;具体展会活动议程根据组委会实际公示结果为准。 励佳展览(上海)有限公司是一家专业组织、代理国内跨境电商行业展会会展公司&#xff0c;励佳展览拥有高素质的员工队伍&#xff0c;广泛的客户资源&#xf…

香港媒体发稿:【超值1元港媒发稿套餐】推广技巧-华媒舍

在当今竞争激烈的市场中&#xff0c;品牌的推广是企业取得成功的关键。众多的宣传渠道中&#xff0c;香港媒体发稿无疑是一种高效的品牌推广方式。本文将为您介绍《超值1元港媒发稿套餐》的各个组成部分&#xff0c;以及它如何帮助您实现品牌的腾飞。 1. 1元套餐的优势 1元港媒…

田宏斌:以人为本的听力健康管理实践经验 | 演讲嘉宾公布

一、助辅听器材Ⅲ分论坛 助辅听器材Ⅲ分论坛将于3月28日同期举办&#xff01; 听力贯穿人的一生&#xff0c;听觉在生命的各个阶段都是至关重要的功能&#xff0c;听力问题一旦出现&#xff0c;会严重影响生活质量。助辅听器材能有效提高生活品质。在这里&#xff0c;我们将分享…

逆变器专题(15)-弱电网下的LCL逆变器控制以及谐振峰问题(2)

相应仿真原件请移步资源下载 LCL滤波器 LCL滤波器因其本身为一个二阶系统&#xff0c;其本身就会引发谐振&#xff0c;导致相应谐振频率处的增益得到放大&#xff0c;进而产生谐波等问题&#xff1b;另一方面&#xff0c;在弱电网下&#xff0c;逆变器会与电网阻抗发生耦合&am…

IP劫持的危害及应对策略

随着互联网的发展&#xff0c;网络安全问题日益凸显&#xff0c;其中IP劫持作为一种常见的网络攻击手段&#xff0c;对个人和企业的信息安全造成了严重的威胁。IP数据云将分析IP劫持的危害&#xff0c;并提出相应的应对策略。 IP地址查询&#xff1a;IP数据云 - 免费IP地址查询…

多张gif怎么拼接?三步教你在线拼接gif

GIF拼图常用于制作表情包、动态海报、广告宣传等场景。它可以将多个图像或动画元素组合在一起&#xff0c;形成一个有趣、生动的动画效果&#xff0c;吸引观众的注意力&#xff0c;并传达特定的信息或情感。要将多个GIF动图拼接在一张图像上&#xff0c;要使用适合的gif动画图片…

奥壹科技推出婚恋相亲系统免费软件,旨在扶持创业者降低行业入门门槛

(广州, 2024年3月6日星期三) - 随着婚恋市场的日益增长&#xff0c;奥壹科技宣布推出其旗下奥壹云相亲系统的全新免费版(OEyun Free V1.0)。该公司表示&#xff0c;该举措旨在通过降低创业门槛&#xff0c;为有意进入婚恋行业的创业者及小型企业提供支持&#xff0c;从而进一步…