苍穹外卖【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" | 精准删除指定键(可选) |
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注解自动处理 |
| 代码侵入性 | 需要手动编写缓存逻辑 | 零侵入,注解声明式 |
| 扩展性 | 需要手动维护 | 支持多种缓存后端切换 |
至此,缓存套餐的代码已经开发完毕
下一期将为大家带来对购物车进行操作的代码开发讲解
