@Cacheable、@CachePut和@CacheEvict三个注解的使用及区别
作者:dushky
@Cacheable、@CachePut和@CacheEvict是Spring框架中的核心缓存注解,分别用于查询、更新和删除缓存操作,@Cacheable在缓存命中时跳过方法执行;@CachePut强制执行并更新缓存;@CacheEvict删除或清空缓存,注意事项包括键的一致性、事务处理和条件过滤等
在 Spring 框架中,@Cacheable、@CachePut 和 @CacheEvict 是三个与缓存管理相关的核心注解,它们与 Redis 等缓存中间件结合使用时,可以显著提升应用性能。
以下是它们的核心区别和适用场景:
@Cacheable
作用:
在方法执行前检查缓存,如果缓存中存在对应结果,直接返回缓存值,跳过方法执行;若不存在,则执行方法并将返回值存入缓存。
适用场景:
- 查询操作(例如根据 ID 查询用户信息)。
- 适用于结果稳定、不频繁变更的数据。
关键参数:
value/cacheNames:缓存名称(如"users")。key:缓存的键(支持 SpEL 表达式)。condition/unless:缓存条件(如condition="#id > 10")。
示例:
@Cacheable(value = "users", key = "#id")
public User getUserById(Long id) {
// 查询数据库
return userRepository.findById(id);
}@CachePut
作用:
无论缓存是否存在,始终执行方法,并将返回值更新到缓存中。适用于需要强制刷新缓存的场景。
适用场景:
- 更新操作(例如修改用户信息后同步更新缓存)。
- 需要确保缓存与数据库数据一致。
关键参数:
与 @Cacheable 相同,但需确保 key 的生成逻辑与 @Cacheable 一致,否则可能导致缓存不一致。
示例:
@CachePut(value = "users", key = "#user.id")
public User updateUser(User user) {
// 更新数据库
userRepository.update(user);
return user; // 更新后的结果存入缓存
}@CacheEvict
作用:
删除指定缓存条目或清空整个缓存。支持在方法调用前或后触发。
适用场景:
- 删除操作(例如删除用户后清除缓存)。
- 数据变更后需要清理旧缓存(如批量更新后清空列表缓存)。
关键参数:
allEntries:是否清空整个缓存(默认为false,仅删除key对应的条目)。beforeInvocation:是否在方法执行前删除缓存(默认为false,方法执行后删除)。
示例:
@CacheEvict(value = "users", key = "#id")
public void deleteUser(Long id) {
userRepository.deleteById(id);
}
// 清空整个缓存
@CacheEvict(value = "users", allEntries = true)
public void refreshAllUsers() {
// 无需实际逻辑,仅触发缓存清理
}三者的核心区别
| 注解 | 是否执行方法体 | 缓存行为 | 典型场景 |
|---|---|---|---|
| @Cacheable | 缓存命中时跳过 | 读取缓存或写入新结果 | 查询(幂等操作) |
| @CachePut | 始终执行 | 强制更新缓存 | 更新(非幂等) |
| @CacheEvict | 通常执行 | 删除缓存条目或清空整个缓存 | 删除或清理缓存 |
注意事项
键的一致性
@Cacheable 和 @CachePut 的 key 生成逻辑必须一致,否则更新后可能无法覆盖旧缓存。
事务与缓存的顺序
默认情况下,缓存操作在方法执行后触发。若方法有事务,需确保缓存操作在事务提交后执行(可通过 @Transactional 配置)。
条件过滤
使用 condition 和 unless 避免缓存无效数据(如 unless="#result == null")。
分布式锁
在高并发场景下,结合分布式锁(如 Redis 的 RedLock)避免缓存击穿或雪崩。
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
