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

苍穹外卖【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

@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):

<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

spring: redis: host: ${sky.redis.host} port: ${sky.redis.port} password: ${sky.redis.password} database: ${sky.redis.database}

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

文件:user/SetmealController.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 精准失效(新增套餐)

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

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

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

// 批量删除 @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"精准删除指定键(可选)
allEntriestrue删除该缓存名称下的所有条目
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注解自动处理
代码侵入性需要手动编写缓存逻辑零侵入,注解声明式
扩展性需要手动维护支持多种缓存后端切换

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

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