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

news2024/11/24 18:36:57

文章目录

  • 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、启动程序进行测试

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[最佳实践] Windows上构建一个和Linux类似的Terminal

感谢大佬批评指正&#xff0c;现已更新 preview Target&#xff1a;致力打造最赏心悦目Window下的终端&#xff0c;同时能够很接近Linux的使用习惯 key word&#xff1a;windows终端美化 windows terminal windows powershell 类似Linux下的Window终端 Window也能用ll windows…

让Excel 365 Excel 2021快速转化为生产力,创造价值!

文章目录 每日一句正能量前言关键点内容简介作者简介读者赞誉后记购买链接赠书活动 每日一句正能量 人的一生&#xff0c;好不好只有自己知道&#xff0c;乐不乐只有自己明白。快乐是一种心情&#xff0c;一种自然、积极向上的心态。在平凡之中寻求快乐&#xff0c;在磨难之中寻…

AI智能伪原创工具:原创文章自动生成的革新

随着人工智能技术的迅猛发展&#xff0c;AI智能伪原创工具正逐渐改变着我们的日常生活和工作方式。其中&#xff0c;原创文章自动生成技术的出现&#xff0c;为内容创作者、企业和学术界带来了全新的可能性和便利。这项技术的引入不仅提高了内容创作的效率&#xff0c;还为用户…

【查找算法】插值查找

一&#xff1a;插值查找 代码公式&#xff1a;int mid left (right - left) * (findVal - arr[left]) / (arr[right] - arr[left]); 1.1 基本概念 插值查找&#xff0c;有序表的一种查找方式。插值查找是根据查找关键字与查找表中最大最小记录关键字比较后的查找方法。插值…

什么样的跨网数据交换产品 能实现数据摆渡和数据同步?

首先&#xff0c;为什么会产生跨网数据摆渡的需求和场景呢&#xff1f;那是因为做了网络隔离&#xff0c;企业进行网络隔离的原因主要包括以下几点&#xff1a; 1、提高安全性&#xff1a;网络隔离是防止未授权访问和网络攻击的有效手段。通过将网络划分为多个独立的子网&…

Docker 快速入门实操教程(完结)

Docker 快速入门实操教程&#xff08;完结&#xff09; Docker&#xff0c;启动&#xff01; 如果安装好Docker不知道怎么使用&#xff0c;不理解各个名词的概念&#xff0c;不太了解各个功能的用途&#xff0c;这篇文章应该会对你有帮助。 前置条件&#xff1a;已经安装Doc…

flutter 使用webview

背景&#xff1a; 一般都有使用webview加载网页的需求&#xff0c;比如加载隐私协议、用户协议等。 如何做&#xff1a; 当然&#xff0c;我们自己不用封装轮子&#xff0c;在pub.dev上有成熟的轮子&#xff1a;webview_flutter 首先&#xff0c;将依赖导入&#xff0c;在pub…

Appium系列(1)安装启动Appium

Appium环境准备 Mac电脑jdk环境AndroidSDK环境node>8.1.0&#xff08;最好用最新版本&#xff09; 安装命令 npm i -g appium安装不成功请检查node 版本是否正确 安装成功命令行输入appium回车查看 安装驱动程序 1、先检查当前驱动情况 通过 appium driver list 进行…

鸿蒙Harmony应用开发—ArkTS声明式开发(基础手势:PinchGesture)

用于触发捏合手势&#xff0c;触发捏合手势的最少手指为2指&#xff0c;最大为5指&#xff0c;最小识别距离为5vp。 说明&#xff1a; 从API Version 7开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 接口 PinchGesture(value?: { finge…

刷题笔记day27-回溯算法3

39. 组合总和 var path []int var tmp []int var result [][]int// 还是需要去重复&#xff0c;题目中要求的是至少一个数字备选的数量不同。 // 所以需要剪枝操作&#xff0c;右边的要比左边的> func combinationSum(candidates []int, target int) [][]int {// 组合问题pa…

淘宝下单接口的解析|跨境电商中淘宝代购功能实现的API接口接入详解【附代码实例】

淘宝接口解析是指通过淘宝提供的API&#xff08;Application Programming Interface&#xff09;来实现程序与淘宝平台的数据交互和功能调用。通过淘宝接口&#xff0c;用户可以实现商品信息获取、订单管理、物流跟踪等功能。 在使用淘宝接口前&#xff0c;首先需要注册成为阿…

在 SpringBoot3 中使用 Mybatis-Plus 报错

在 SpringBoot3 中使用 Mybatis-Plus 报错 Property ‘sqlSessionFactory’ or ‘sqlSessionTemplate’ are required Caused by: java.lang.IllegalArgumentException: Property sqlSessionFactory or sqlSessionTemplate are requiredat org.springframework.util.Assert.no…

Sora:AI视频模型的无限可能与挑战

随着人工智能技术的突飞猛进&#xff0c;AI视频模型已成为科技领域的新焦点。OpenAI推出的AI视频模型Sora&#xff0c;凭借其卓越的技术性能和前瞻性&#xff0c;为AI视频领域的发展揭开了新的篇章。本文将从技术解析、应用场景、未来展望、伦理与创意以及用户体验与互动五个方…

内存溢出(OOM)-汇总指南

目录 java.lang.OutOfMemoryError:Java heap space 原因分析 示例 解决方案 java.lang.OutOfMemoryError:GC overhead limit exceeded 原因分析 示例 解决方案 java.lang.OutOfMemoryError:Permgen space 原因分析 示例 解决方案 java.lang.OutOfMemoryError:Metas…

【Apple Vision Pro应用源码】Vision Pro吸尘器项目源代码

超级有趣Vision Pro 应用 吸尘器项目 这是一个非常有趣的 Vision Pro项目&#xff0c;会让孩子们爱上打扫卫生。 在这里我展示了如何使用 ARKit&#xff1a;头部跟踪、手部跟踪、场景理解加载和播放声音进程冲突使用 MTLBuffers 处理底层网格数据 项目源代码&#xff1a;Git…

vue3中的生命周期有哪些和怎么使用?

目录 前言&#xff1a; 正文&#xff1a; 总结: 前言&#xff1a; Vue.js 3是Vue.js框架的最新主要版本&#xff0c;引入了一些重大的改变和增强。在Vue 3中&#xff0c;由于Composition API的引入&#xff0c;生命周期钩子被替换为生命周期函数。 正文&#xff1a; 以下是…

Claude3 AI系列重磅推出:引领多模态智能时代的前沿技术,超越GPT-4

Claude3正式发布&#xff1a;号称性能超 GPT-4&#xff0c;免费使用、支持中文 划重点: &#x1f680; Claude3系列发布&#xff0c;包括Haiku、Sonnet和Opus版本&#xff0c;Opus在多个领域超越GPT-4。 &#x1f310; 用户可免费使用Claude3Sonnet模型&#xff0c;支持中文&am…

PostgreSQL开发与实战(6)体系结构1

作者&#xff1a;太阳 一、进程结构 PG数据库启动时会先启动一个主进程&#xff08;9.3之前称为postmaster&#xff0c;9.3以后称为postgres server process&#xff09;&#xff0c;然后fork出一些辅助子进程&#xff08;backend、backgroud相关进程&#xff09;&#xff0c;…

Go-知识简短变量声明

Go-知识简短变量声明 1. 简短变量声明符2. 简短变量赋值可能会重新声明3. 简短变量赋值不能用于函数外部4. 简短变量赋值作用域问题5. 总结 githuio地址&#xff1a;https://a18792721831.github.io/ 1. 简短变量声明符 在Go语言中&#xff0c;可以使用关键字var或直接使用简短…

2024新鲜出炉阿里巴巴面试真题,如果不想35岁被淘汰这篇文章必看

最近看到群里看到一个女生&#xff0c;讲述了她从开始选择Android&#xff0c;经过非常努力的学习和挣扎&#xff0c;然而最后面对当前的环境却不得不放弃。看完以后真的非常替她感觉惋惜&#xff0c;如果早几年入行可能结果会比现在好很多&#xff0c;但可惜&#xff0c;这就是…