RedisTemplate 调用链
RedisTemplate 的核心不是命令数量,而是把连接、序列化、异常和资源释放集中到一次模板执行中。
先给答案:RedisTemplate 的价值是把命令调用、资源释放和序列化组合成稳定模板
Section titled “先给答案:RedisTemplate 的价值是把命令调用、资源释放和序列化组合成稳定模板”Template 对外提供按数据结构组织的操作对象,对内通过 execute/callback 获取连接、执行命令并归还资源。业务代码因此不必重复处理连接生命周期,但也要接受模板默认的序列化和连接上下文。
不同操作对象共享连接工厂,却不共享所有语义:普通 value、hash、set、stream 的序列化路径不同;在 callback 内直接使用底层连接可以获得更多能力,也可能绕过模板的类型和事务约束。
opsForHash().put(k,hk,hv) -> rawKey/rawHashKey/rawHashValue -> execute(callback) -> getConnection(factory, txSupport) -> callback.doInRedis(connection proxy) -> releaseConnection()类声明与线程安全说明在 core/RedisTemplate.java:68-104。execute(RedisCallback) 在 :370-384 委托到完整重载,真正逻辑在 :397-427。
当 exposeConnection=false 时,模板在 :416-417 把连接包装成 close-suppressing proxy,防止 callback 误关闭由模板管理的连接;代理构造位于 :545-550。这是资源所有权隔离,而不是性能优化。
opsForValue、opsForHash、opsForStream 等方法集中在 :1059-1128。它们返回命令域对象,命令域对象只负责把类型化参数转换为模板回调,不直接管理连接。
为什么用回调模板
Section titled “为什么用回调模板”替代方案:每个操作对象都自己获取、关闭连接。
为什么不行:事务绑定、管道和异常释放会在几十个操作类中重复,且容易出现连接泄漏。
证据:所有 callback 都经由 RedisTemplate#execute,并在 finally 的 RedisConnectionUtils.releaseConnection 中释放,见 RedisTemplate.java:403-427。
| 场景 | 现象 | 原因 | 规避 |
|---|---|---|---|
| 未调用初始化 | 使用时报 template not initialized | serializer 和操作对象未准备好 | Bean 初始化后使用 |
| 自己关闭 callback 连接 | 后续命令失败 | 连接所有权属于模板 | 不暴露或不关闭 native connection |
| 类型 serializer 不匹配 | 反序列化异常 | 泛型不等于运行时格式 | key/value 分别配置 serializer |
模板方法适合把资源租借、异常转换和审计统一起来;命令对象适合承载领域语义,但不应越过模板边界管理底层资源。
面试锚点
RedisTemplate#execute的 finally 做了什么?- 为什么默认不暴露原生连接?
opsForValue和opsForStream如何复用同一连接链?