@Cacheable(value = "users", key = "#id"),其中#id有什么作用
我们使用springboot提供的注解去操作Redis非常方便,但是@Cacheable(value = "users", key = "#id"),其中#id有什么作用。本篇博客用来记录一下它的使用方式与作用
在 @Cacheable(value = "users", key = "#id") 注解中,#id 是一个 SpEL(Spring Expression Language)表达式,它的作用是 动态生成缓存键。
#id 的具体作用
-
引用方法参数:
#id表示引用方法的id参数值- 如果方法签名是
getUserById(Long id),那么#id就是传入的id参数值
-
组成缓存键:
- 最终缓存键会是:
users::<id的实际值> - 例如调用
getUserById(123L),缓存键就是users::123
- 最终缓存键会是:
-
避免键冲突:
- 确保不同ID的数据不会互相覆盖
- 每个ID都有自己独立的缓存条目
其他常见SpEL用法示例
// 基本参数引用
@Cacheable(value = "users", key = "#userId") // 引用userId参数
// 对象属性引用
@Cacheable(value = "orders", key = "#order.id") // 引用order参数的id属性
// 组合键
@Cacheable(value = "products", key = "#category+'-'+#page") // 组合多个参数
// 方法调用
@Cacheable(value = "config", key = "#root.methodName") // 使用方法名作为键
// 安全引用(避免null)
@Cacheable(value = "items", key = "#id?:'default'") // 如果id为null则使用'default'
键的完整结构
默认情况下,Spring Cache生成的完整键格式是:
<cacheNames>::<key>
所以 @Cacheable(value = "users", key = "#id") 会产生类似:
users::123
重要提示
- 参数名必须匹配:
#id中的id必须与方法参数名完全一致 - 支持复杂表达式:可以写更复杂的SpEL表达式,如条件判断、方法调用等
- 性能考虑:过于复杂的key表达式可能影响性能
- 可读性:保持key表达式简单明了
如果需要更复杂的键生成策略,可以考虑实现自定义的 KeyGenerator。
如果我想获取List对象,但是方法没有参数,请问cacheable应该如何设计,其方法代码如何实现
例如:无参数方法获取List对象的缓存设计
当你想缓存一个返回 List 的无参数方法时,可以通过以下几种方式设计 @Cacheable 注解:
1.固定键方式
@Cacheable(value = "userList", key = "'allUsers'")
public List<User> getAllUsers() {
// 数据库查询逻辑
return userRepository.findAll();
}
特点:
- 使用固定字符串
'allUsers'作为键(注意单引号表示字符串字面量) - 所有调用都会命中同一个缓存条目
2.方法名作为键
@Cacheable(value = "userList", key = "#root.methodName")
public List<User> getAllUsers() {
return userRepository.findAll();
}
特点:
#root.methodName会自动使用方法名作为键- 键格式类似
userList::getAllUsers
3.自定义复合键(适合更复杂场景)
@Cacheable(value = "userList", key = "T(java.util.UUID).randomUUID().toString()")
public List<User> getAllUsers() {
return userRepository.findAll();
}
注意:这种每次生成新键的方式实际上会禁用缓存,仅作示例展示SpEL能力
4.最佳实践方案(带缓存刷新控制)
@Cacheable(value = "userList", key = "'ALL_USERS_CACHE'")
public List<User> getAllUsers() {
// 这里可以添加日志,监控缓存未命中情况
log.debug("Cache miss for ALL_USERS_CACHE, querying database...");
return userRepository.findAll();
}
// 当数据变更时清除缓存
@CacheEvict(value = "userList", key = "'ALL_USERS_CACHE'")
public void refreshAllUsersCache() {
// 方法体可以为空,注解已经实现了缓存清除
}
5.带条件缓存的高级用法
@Cacheable(value = "userList",
key = "'ACTIVE_USERS_' + T(java.time.LocalDate).now().toString()",
unless = "#result == null || #result.isEmpty()")
public List<User> getActiveUsers() {
return userRepository.findByStatus("ACTIVE");
}
特点:
- 组合日期信息作为键的一部分,实现按天缓存
unless确保空结果不缓存
注释事项
-
缓存更新策略:
- 列表数据变更时要记得清除或更新缓存
- 可以使用
@CacheEvict或@CachePut
-
大列表缓存:
- 如果列表很大,考虑分页缓存而非全量缓存
- 或者使用
@Cacheable配合分页参数
-
序列化要求:
- 确保 List 中的对象实现了 Serializable
- 检查 Redis 的序列化配置是否合适
-
内存考虑:
- 监控大列表缓存的内存占用
- 可以设置合适的 TTL(生存时间)
这样的设计既保持了 API 的简洁性(无参数方法),又通过合理的键设计实现了有效的缓存机制。

浙公网安备 33010602011771号