更新方法向缓存写入什么,取决于实际返回结果。若查询方法期望完整商户视图,更新方法却把 boolean 成功标志存进同一个键,响应状态正常也不能证明缓存契约成立。
资料基于 Spring Framework 7.0.10。原始核查:2026 年 10 月 26 日;发布整理复核:2026 年 10 月 30 日 JST。以下是未运行的同步本地教学夹具,不叠加事务或重试。
CachePut 保留方法执行,并缓存返回结果
通过 Spring 缓存代理调用时,@CachePut 不会因为已有条目而跳过方法;方法仍执行,再按 condition 与 unless 决定是否写入结果。它不会自动缓存入参,两个条件也不代替业务执行许可。CachePut API
固定版本注解源码说明其 condition 在方法调用后评估,可以引用结果。不要套用 @Cacheable 的判断时序。
一般应把查询与更新拆成独立方法,避免在同一方法叠加 @Cacheable 和 @CachePut。二者分别用于复用已有结果与执行后更新缓存,混用容易形成难以判断的调用路径。CachePut 指南
最小设计片段
两个方法放在受代理管理的 Bean 公共方法中,经外部调用,使用同一缓存区域与商户 ID。ShopView、repository 及 updateAndReadBack 由练习实现;后者返回更新后的完整视图。片段不是完整可运行程序。
@Cacheable(cacheNames = "shops", key = "#p0")
public ShopView find(long id) { return repository.find(id); }
@CachePut(cacheNames = "shops", key = "#p0")
public ShopView rename(long id, String name) {
return repository.updateAndReadBack(id, name);
}
练习一:更新执行次数与缓存值来源
使用本地缓存、假 repository 和调用计数器,预填 ID 1 的旧 ShopView。连续两次调用 rename(1, 不同名称),让假 repository 规范化名称,以便区分请求内容与实际返回内容。
验收更新计数为 2,缓存值等于第二次返回的完整视图;随后经代理调用 find(1),命中该视图且不增加查询计数。每组记录原始输入、实际返回值、缓存键、缓存值类型和完整字段。
练习二:同键保存了错误类型
另建独立测试,把同缓存同键的更新方法改成返回 boolean。先检查缓存实际值类型,再调用期望 ShopView 的查询;记录类型不兼容出现的位置,不预设所有实现抛同一种异常。
随后恢复完整视图返回值,再运行同样断言。不要只用 HTTP 200、一个成功标志或一次缓存命中证明修复完成。
返回值契约之外仍有时序问题
这些练习不证明数据库与 Redis 原子提交、并发更新顺序或崩溃恢复。需要分别衔接事务感知缓存、写入失败和版本下界练习。
Since 3.1 表示功能起始版本,不是页面发布日期。来源独立发布日期未标明;既有 6.2.11 项目的完整兼容性未验证,不需要为阅读本节自动升级。