@cacheput 的 condition 必须在 key 表达式执行前生效,用于安全拦截 null 结果;若不加 condition="#result != null",key 中访问 #result.id 会触发 nullpointerexception;condition 控制是否进入缓存流程,unless 仅控制最终是否写入,二者不可互换。

@CachePut 的 condition 属性必须在 key 表达式执行前生效
很多人给 @CachePut 配了类似 key = "'user_' + #result.id" 却没加 condition,结果一遇到 null 返回就直接抛 NullPointerException。这是因为 Spring 会在执行 key 表达式时尝试访问 #result.id,而此时 #result 是 null —— 它根本不会等你“存不存”,先报错再说。
condition 在 @CachePut 里不是可选逻辑开关,而是安全守门员:它必须在 key 解析前完成判断,否则 key 就会炸。
-
condition = "#result != null"是最常见且必要的兜底写法 - 如果返回对象可能为
null(比如 findById 找不到记录),不加这个 condition,方法体哪怕执行成功,也会在缓存写入阶段失败 -
unless不行——它发生在 key 已经生成、甚至值已序列化之后,无法阻止 key 表达式里的空指针
condition 和 unless 在 @CachePut 中的分工差异
condition 控制「要不要走缓存流程」,unless 控制「要不要把结果真正写进去」。两者位置不同,作用不可互换。
-
condition:方法执行完后立刻判断,决定是否继续执行 key 解析 → 序列化 → 写入 Redis 流程 -
unless:整个流程走到最后一步才触发,只影响「是否落库」,不阻止 key 表达式求值或序列化动作 - 举例:
@CachePut(key = "'user_' + #result.id", unless = "#result.price —— 若 <code>#result为null,仍会因#result.id报错;但若#result存在且price ,则 key 正常生成、序列化也完成,只是最终不写入 Redis
真实场景中 condition 的典型组合写法
光写 #result != null 往往不够,业务常需叠加状态校验。关键点是:所有判断都得基于 #result,不能引用参数(@CachePut 不支持用参数做 condition,那是 @Cacheable 的事)。
- 确保对象非空且主键有效:
condition = "#result != null and #result.id != null and #result.id > 0" - 排除软删除数据:
condition = "#result != null and #result.deleted == false" - 配合业务状态码(如 ResultVO):
condition = "#result != null and #result.code == 0" - 避免缓存中间态对象:
condition = "#result != null and #result.status == 'ACTIVE'"
注意:这些表达式全部在 SpEL 中运行,不支持 Java 方法调用(如 #result.isValid() 除非该方法是 public 且无参)。
为什么 @CachePut 的 condition 不能引用方法参数
@CachePut 的设计语义是「以方法返回值为中心更新缓存」,它的 condition 和 key 都默认绑定到 #result。Spring 源码里明确限制了 @CachePut 的 SpEL 上下文只暴露 #result 和 #root(含 method/args/target),但 condition 解析时 args 并未注入——这是和 @Cacheable 最本质的区别。
如果你真需要根据入参控制是否更新(比如只允许管理员更新缓存),要么改用 @CacheEvict + @Cacheable 组合,要么在方法体内手动调用 Cache.put(),别硬套 @CachePut 的 condition。
最容易被忽略的一点:condition 为 false 时,整个缓存写入流程静默终止,连日志都不会打——出问题时得先查 condition 是否意外为 false,而不是盯着 Redis 里有没有 key。











