StringRedisTemplate

上一小节我们提到了RedisTemplate<Stinrg,Object>使用SpringDataRedis 默认的 JDK 序列化方式时,Redis 的key/value 可读性比较差。

因此课程里通常会自定义 RedisTemplate:

目的就是:

plaintext 复制代码
默认 JDK 序列化
        ↓
数据不直观、不方便阅读

改成

Key   → String 序列化
Value → JSON 序列化
        ↓
Redis 中的数据更容易阅读

但这里又产生了新的问题:

假设保存:

java 复制代码
User user = new User("张三", 20);

redisTemplate.opsForValue().set("user:1", user);

为了以后能够把 JSON 反序列化回 User 对象,某些 JSON 序列化方案会把 Java 类型信息一起保存进去。

Redis 中可能类似:

js 复制代码
{
  "@class": "com.example.pojo.User",
  "name": "张三",
  "age": 20
}

问题就在这里:

text 复制代码
真正业务数据
name
age

+

额外类型信息
com.example.pojo.User

也就是说:

为了自动反序列化对象,需要保存额外的 Java 类型信息,增加了 Redis 的存储开销,而且 Redis 数据和 Java 类结构耦合得更紧。

为了解决这个问题我们提供了另一种思路:StringRedisTemplate

它不帮你处理复杂 Java 对象,而是明确规定:

text 复制代码
Key   → String
Value → String

因此:

java 复制代码
@Autowired
private StringRedisTemplate stringRedisTemplate;

存普通字符串:

java 复制代码
stringRedisTemplate.opsForValue()
        .set("name", "Jack");

如果要存 User:

java 复制代码
User user = new User("张三", 20);

先由我们自己:

text 复制代码
User 对象
   ↓
ObjectMapper
   ↓
JSON 字符串
   ↓
StringRedisTemplate
   ↓
Redis

Redis 里只需要

js 复制代码
{
  "name": "张三",
  "age": 20
}

读取的时候反过来:

text 复制代码
Redis
 ↓
JSON String
 ↓
ObjectMapper
 ↓
User

也就是:

先通过ObjectMapper将 user 对象转成 JSON 格式的字符串:

java 复制代码
String json = objectMapper.writeValueAsString(user);

然后调用 Spring 给我们的提供的 API将 String 类型的 key/value 写入 redis:

java 复制代码
redisTemplate.opsForvalue().set("user",json);

读取的时候同样需要调用ObjectMapper 手动将读取到的JSON 字符串通过字节码对象转成 User 对象:

java 复制代码
String json = stringRedisTemplate.opsForValue()
        .get("user");

User user = objectMapper.readValue(json, User.class);

虽然看起来多写了一步:

java 复制代码
对象 → JSON
JSON → 对象

但是好处是Redis 只负责存字符串,Java 程序自己决定这个字符串应该转换成什么对象。

总结:

为了节省内存空间,我们并不会使用 JSON 序列化器来处理 value,而是统一使用 String 序列化器,要求只能存储 String 类型的 key 和 value,这两者的区别在于:JSON 序列化器会手动帮我们把Java 对象序列化成 String 数据,但为了实现自动反序列化需要保存额外的 Java 类型信息,导致Redis 的存储开销变大并且增加了Java 类与 Redis 数据的耦合度,而后者则在需要存储 Java 对象时,通过ObjectMapper 的 writeValueAsString(user)和 readValue(json,User.class)手动完成对象的序列化和反序列化。

补充:

  1. ObjectMapper 是一个 JSON 工具

    需要我们手动的创建对象:

    private static final ObjectMapper mapper = new ObjectMapper();

  2. Spring 默认提供了一个 StringRedisTemplate 类,它的 key 和 value 默认就是 String 方式,省去了我们自定义 RedisTemplate 的过程.

  3. User.class得到的是 User 类对应的 Class 对象,可以暂时把它理解为"字节码对象",但更严谨的叫法是:

User.class 是 User 类在 JVM 中对应的 Class<User> 对象。

text 复制代码
User.java
   ↓ 编译
User.class 字节码文件
   ↓ JVM 加载
JVM 中的 User 类
   ↓
对应一个 Class<User> 对象
   ↑
User.class 可以拿到它

相当于拿到这个对象的信息:

text 复制代码
json        → 数据是什么
User.class  → 告诉 Jackson 要转换成什么类型

                  ↓

              User 对象
相关推荐
蜗牛互联网2 小时前
WSL Containers GA:本地AI容器的生命周期、网络与治理验收
java·网络·人工智能·后端
Zhou1411363 小时前
SpringBoot_02_自动配置原理
java·spring boot·后端
子兮曰4 小时前
Node.js 50个优势场景盘点:一个人单干,为啥我多数时候只用它
前端·后端
IT_陈寒4 小时前
我花3小时debug,就因为JavaScript这个隐式转换坑
前端·人工智能·后端
程序猿阿越4 小时前
containerd如何拉取镜像
后端·kubernetes·源码阅读
一条小小yu4 小时前
为什么使用springboot
java·spring boot·后端
香瓜子rd5 小时前
MySQL InnoDB 并发控制核心原理:事务、隔离级别、MVCC 与锁机制
数据库·后端
RISCV_Explorer5 小时前
RISC-V RVV向量编程模型机制解析——vsetvl配置、掩码与尾元素策略及长度无关编程
后端·risc-v
对象存储与RustFS5 小时前
升级 RustFS 二进制不停机:一条一条换,留一条退路
后端·rust·开源
hasty5 小时前
限制写了却没生效:OpenTelemetry Go 的 Unicode 截断边界
开发语言·后端·golang