苍穹外卖【day7|缓存套餐_Spring Cache】

🌈个人主页 :一条泥憨鱼 (欢迎各位大佬莅临)

🎬精选专栏:数据结构与算法Java,AI与Agent

Spring Cache

Spring Cache常用注解

代码开发

实现思路

一、整体架构概览

套餐缓存模块采用 Spring Cache 注解驱动 方式,结合 Redis 作为缓存后端实现。

复制代码
┌──────────────────────────────────────────────────────────────┐
│                    Spring Cache 架构                          │
├──────────────────────────────────────────────────────────────┤
│                                                              │
│  ┌──────────────────┐        ┌──────────────────────────┐    │
│  │   C端查询接口     │        │     Admin管理接口        │    │
│  │  @Cacheable      │        │   @CacheEvict           │    │
│  └────────┬─────────┘        └────────────┬─────────────┘    │
│           │                               │                  │
│           ▼                               ▼                  │
│  ┌─────────────────────────────────────────────────────┐     │
│  │              Spring Cache 抽象层                      │     │
│  │  (CacheManager + CacheResolver)                    │     │
│  └─────────────────────┬───────────────────────────────┘     │
│                        │                                     │
│                        ▼                                     │
│  ┌─────────────────────────────────────────────────────┐     │
│  │                    Redis 缓存层                       │     │
│  │  Key: setmealCache::categoryId                      │     │
│  │  Value: List<Setmeal>                               │     │
│  └─────────────────────────────────────────────────────┘     │
│                        │                                     │
│                        ▼                                     │
│  ┌─────────────────────────────────────────────────────┐     │
│  │                    MySQL 数据库                        │     │
│  │                   setmeal 表                          │     │
│  └─────────────────────────────────────────────────────┘     │
│                                                              │
└──────────────────────────────────────────────────────────────┘

二、核心组件解析

1. 缓存开启配置

文件 :/sky/SkyApplication

java 复制代码
@SpringBootApplication
@EnableTransactionManagement
@EnableCaching  // 关键:开启缓存注解支持
public class SkyApplication {
    public static void main(String[] args) {
        SpringApplication.run(SkyApplication.class, args);
    }
}

作用说明:

  • @EnableCaching 注解启用 Spring 的缓存管理功能
  • 自动扫描并处理 @Cacheable@CacheEvict@CachePut 等缓存注解
  • 配合 Redis 配置,自动将缓存数据存储到 Redis

2. Redis 依赖与配置

依赖 (pom.xml):

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>

配置 (application.yml):

yaml

XML 复制代码
spring:
  redis:
    host: ${sky.redis.host}
    port: ${sky.redis.port}
    password: ${sky.redis.password}
    database: ${sky.redis.database}

三、缓存读取机制(C端)

文件 : user/SetmealController.java

java 复制代码
@GetMapping("/list")
@ApiOperation("根据分类id查询套餐")
@Cacheable(cacheNames = "setmealCache", key = "#categoryId")  // 核心注解
public Result<List<Setmeal>> list(Long categoryId) {
    Setmeal setmeal = new Setmeal();
    setmeal.setCategoryId(categoryId);
    setmeal.setStatus(StatusConstant.ENABLE);  // 只查询起售中的套餐

    List<Setmeal> list = setmealService.list(setmeal);
    return Result.success(list);
}

@Cacheable 注解解析

属性 说明
cacheNames "setmealCache" 缓存名称,作为Key前缀
key "#categoryId" SpEL表达式,使用方法参数作为缓存键
value 方法返回值 自动缓存方法返回的 List<Setmeal>

缓存Key生成规则

复制代码
完整Key格式: cacheNames::key
示例:        setmealCache::100

执行流程

复制代码
用户请求 → @Cacheable 拦截 → 检查缓存
              │
              ├── 缓存存在 → 直接返回缓存数据(不执行方法体)
              │
              └── 缓存不存在 → 执行方法体 → 将返回值写入缓存 → 返回结果

四、缓存失效机制(Admin端)

文件 : admin/SetmealController.java

4.1 精准失效(新增套餐)

java 复制代码
@PostMapping
@ApiOperation("新增套餐")
@CacheEvict(cacheNames = "setmealCache", key = "#setmealDTO.categoryId")
public Result save(@RequestBody SetmealDTO setmealDTO) {
    setmealService.saveWithDish(setmealDTO);
    return Result.success();
}

失效策略: 只删除新增套餐所属分类的缓存

4.2 全量失效(删除/修改/状态变更)

java 复制代码
// 批量删除
@DeleteMapping
@CacheEvict(cacheNames = "setmealCache", allEntries = true)
public Result delete(@RequestParam List<Long> ids) {
    setmealService.deleteBatch(ids);
    return Result.success();
}

// 修改套餐
@PutMapping
@CacheEvict(cacheNames = "setmealCache", allEntries = true)
public Result update(@RequestBody SetmealDTO setmealDTO) {
    setmealService.update(setmealDTO);
    return Result.success();
}

// 起售停售
@PostMapping("/status/{status}")
@CacheEvict(cacheNames = "setmealCache", allEntries = true)
public Result startOrStop(@PathVariable Integer status, Long id) {
    setmealService.startOrStop(status, id);
    return Result.success();
}

@CacheEvict 注解解析

属性 说明
cacheNames "setmealCache" 缓存名称
key "#setmealDTO.categoryId" 精准删除指定键(可选)
allEntries true 删除该缓存名称下的所有条目
beforeInvocation 默认false 方法执行后再删除缓存

五、失效策略对比

操作 策略 注解配置 原因
新增套餐 精准失效 key="#setmealDTO.categoryId" 只影响当前分类
删除套餐 全量失效 allEntries=true 无法确定影响范围
修改套餐 全量失效 allEntries=true 分类可能改变
状态变更 全量失效 allEntries=true 影响所有分类列表

六、Spring Cache 工作原理解析

6.1 注解处理流程

复制代码
┌────────────────────────────────────────────────────────────┐
│                   @Cacheable 执行流程                       │
├────────────────────────────────────────────────────────────┤
│                                                            │
│  1. 方法调用                                               │
│       │                                                    │
│       ▼                                                    │
│  2. CacheInterceptor 拦截                                  │
│       │                                                    │
│       ▼                                                    │
│  3. 根据 cacheNames + key 生成缓存键                         │
│       │                                                    │
│       ▼                                                    │
│  4. 查询 CacheManager → Redis                              │
│       │                                                    │
│       ├── 命中 → 返回缓存值,不执行方法                      │
│       │                                                    │
│       └── 未命中 → 执行方法 → 将结果写入缓存 → 返回结果       │
│                                                            │
└────────────────────────────────────────────────────────────┘

6.2 缓存抽象层架构

复制代码
┌─────────────────────────────────────────────────────┐
│              Spring Cache 抽象层                    │
├─────────────────────────────────────────────────────┤
│                                                     │
│  @Cacheable / @CacheEvict / @CachePut              │
│         │                                           │
│         ▼                                           │
│  CacheInterceptor (AOP切面)                         │
│         │                                           │
│         ▼                                           │
│  CacheManager (缓存管理器)                           │
│         │                                           │
│         ▼                                           │
│  RedisCacheManager (Redis实现)                       │
│         │                                           │
│         ▼                                           │
│  RedisTemplate                                      │
│         │                                           │
│         ▼                                           │
│  Redis Server                                       │
│                                                     │
└─────────────────────────────────────────────────────┘

七、与菜品缓存的对比

维度 菜品缓存 (Dish) 套餐缓存 (Setmeal)
实现方式 手动 RedisTemplate 操作 Spring Cache 注解
缓存键格式 dish_{categoryId} setmealCache::{categoryId}
读取方式 redisTemplate.opsForValue().get(key) @Cacheable 注解自动处理
失效方式 cleanCache(pattern) 手动方法 @CacheEvict 注解自动处理
代码侵入性 需要手动编写缓存逻辑 零侵入,注解声明式
扩展性 需要手动维护 支持多种缓存后端切换

至此,缓存套餐的代码已经开发完毕

下一期将为大家带来对购物车进行操作的代码开发讲解