缓存是否参与一次调用,与业务方法返回后是否保存结果,是两个判断时点。理解这一区别,才能解释已有缓存为什么没有命中、空结果为什么反复查询,以及权限检查为什么不能只放在缓存加载方法里。
资料基于 Spring Framework 7.0.10。原始核查:2026 年 10 月 25 日;发布整理复核:2026 年 10 月 30 日 JST。以下 Java 练习尚未运行;已有 6.2.11 配套项目的完整兼容性未验证。
condition 和 unless 检查什么
在 @Cacheable 上,condition 为 false 时,本次调用像没有启用缓存一样执行方法,即使缓存已经有值。unless 在方法执行后判断,用来否决本次结果加入缓存。命中已有值时,不能把 unless 当作再次筛查缓存结果或重新授权的机制。条件缓存指南
Optional 会被解包:有值时对应业务对象,无值时对应 null。表达式 #result 指业务对象,不能假定它还是 Optional 并调用 #result.isPresent()。Cacheable API及固定版本源码可用于核对解包和判断时点。
练习一:缓存已有值时让 condition 为 false
使用方法签名 find(long id, boolean useCache),在 @Cacheable 上同时指定 key="#p0" 与 condition="#p1"。预放缓存时使用同一个 Long 键 1001L,查询方法带独立计数器。不要使用默认双参数键,否则 useCache 也会参与键的计算,预放条目可能根本对不上。通过 Spring 代理调用独立测试 Bean,避免自调用绕过拦截。
先预放旧假结果,再以 false、true 分别调用。第一组应执行查询方法,第二组应命中已有缓存;每组独立重置缓存和计数,记录返回内容与执行次数。
为另一个独立方法配置 unless="#result == null"。让它先返回 null,再返回假商户,检查每次业务调用与缓存写入次数。之后命中已有非空值时,不把这次命中记录为“再次执行结果判断”。
练习二:Optional.empty 是未命中还是已缓存的空值
让同一只读查询返回 Optional.empty() 或 Optional.of(假商户),比较不配置 unless 与配置 unless="#result == null" 两组。显式使用允许 null 值的测试缓存,保存提供者与配置。
连续查询空结果时,第一组检查是否复用已缓存的空值,第二组检查是否重新访问底层。检查缓存 API 返回的条目包装与条目中的值,不能仅凭调用方“返回空”判断未命中。
随后模拟原本不存在的商户变为存在:先写清空结果何时失效,再核对新结果能否被看见。负缓存是保存确定的无结果,不能把数据库异常或授权失败改造成确定无结果。
按业务决定是否缓存空结果
不缓存空结果会增加重复无结果查询的后端负载;负缓存的有效期与失效策略应由业务及提供者决定。sync=true 不支持 unless,不要把两个独立例子的注解直接拼在一起;已有同步缓存章节保留了这一限制的实测基线。
本节新增判断时点、Optional 解包和空条目验收;缓存键身份、事务与旧读回填仍按原课程处理。来源独立发布日期未标明,官方指南为滚动页面;本文没有 Java、Redis 或压力测试成绩。