缓存
缓存抽象与依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
Spring 缓存抽象把「缓存管理」与具体存储解耦。不额外配置时,Boot 默认使用基于 ConcurrentHashMap 的进程内缓存(重启即失);要存进 Redis,只需让 spring-boot-starter-data-redis 也在类路径,并设置 spring.cache.type=redis。加一个配置类开启缓存:
@Configuration
@EnableCaching
public class CacheConfig {
}
三个核心注解
| 注解 | 行为 |
|---|---|
| @Cacheable | 先查缓存,命中直接返回;未命中执行方法并把结果写入缓存 |
| @CachePut | 总是执行方法,并用返回值更新缓存(写操作后刷新缓存) |
| @CacheEvict | 执行方法并删除缓存(配合删除操作) |
常用参数:cacheNames(缓存分区名)、key(SpEL 表达式,如 #id)、condition(执行前判断是否走缓存)、unless(返回后判断是否不写缓存,如 unless = "#result == null")。多个注解组合用 @Caching。
完整示例
@Service
public class UserService {
@Cacheable(cacheNames = "user", key = "#id")
public User findById(Long id) {
return userDao.findById(id); // 首次查库,之后同 key 命中缓存
}
@CacheEvict(cacheNames = "user", key = "#id")
public void deleteById(Long id) {
userDao.deleteById(id); // 删除数据同时清掉缓存
}
@CachePut(cacheNames = "user", key = "#user.id")
public User save(User user) {
return userDao.save(user); // 更新数据并刷新缓存
}
}
调用链效果:findById(1) 第一次执行方法并缓存;第二次同参数直接返回缓存值;save/delete 后缓存与数据库保持一致。
配置示例
spring.cache.type=redis # 缓存到 Redis
spring.cache.cache-names=user,role # 预声明缓存分区
Redis 模式下缓存对象的序列化方式、TTL、前缀等配置(如 spring.cache.redis.*)以官方文档为准。
失效场景与注意
- 自调用不走代理:同类内部直接调
findById不经缓存代理,注解不生效(与 17 章事务同理); - key 不写时默认用方法参数生成
SimpleKey,多参数方法建议显式写 key; - 对象缓存到 Redis 时默认 JDK 序列化,跨语言/排查不便,生产建议配置 JSON 序列化;
- 缓存与数据库的强一致需要结合
@CacheEvict/@CachePut的失效策略与业务容忍度权衡; - 多实例部署时进程内缓存各实例独立,建议直接使用 Redis 缓存。
小结
缓存抽象四步:加 starter、开 @EnableCaching、注解声明、选缓存后端。读多写少的查询用 @Cacheable,写操作配合 @CachePut/@CacheEvict 维护一致性,就能以很小成本拿到可观收益。