在 Spring 框架中,@Cacheable
、@CachePut
和 @CacheEvict
是三个与缓存管理相关的核心注解,它们与 Redis 等缓存中间件结合使用时,可以显著提升应用性能。以下是它们的核心区别和适用场景:
1. @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);
}
2. @CachePut
作用:
无论缓存是否存在,始终执行方法,并将返回值更新到缓存中。适用于需要强制刷新缓存的场景。
适用场景:
-
更新操作(例如修改用户信息后同步更新缓存)。
-
需要确保缓存与数据库数据一致。
关键参数:
与 @Cacheable
相同,但需确保 key
的生成逻辑与 @Cacheable
一致,否则可能导致缓存不一致。
示例:
@CachePut(value = "users", key = "#user.id")
public User updateUser(User user) {
// 更新数据库
userRepository.update(user);
return user; // 更新后的结果存入缓存
}
3. @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)避免缓存击穿或雪崩。