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

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