在 Spring 中可使用 Redis 缓存数据,可以通过 RedisTemplate 直接操作,也可以通过 @Cacheable 注解实现缓存。不论使用何种方式,最终都要将 key、value 序列化成字节数组或者字符串保存到 Redis。从 RedisTemplate 源码中可以看出来,其序列化是通过一系列的RedisSerializer 接口实现的,部分代码如下:
public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperations<K, V>, BeanClassLoaderAware {
private boolean enableTransactionSupport = false;
private boolean exposeConnection = false;
private boolean initialized = false;
private boolean enableDefaultSerializer = true;
// 默认序列化器
private @Nullable RedisSerializer<?> defaultSerializer;
private @Nullable ClassLoader classLoader;
// Redis Key 序列化器
@SuppressWarnings("rawtypes") private @Nullable RedisSerializer keySerializer = null;
// Redis Value 序列化器
@SuppressWarnings("rawtypes") private @Nullable RedisSerializer valueSerializer = null;
// Redis Hash 类型的 Key 序列化器
@SuppressWarnings("rawtypes") private @Nullable RedisSerializer hashKeySerializer = null;
// Redis Hash 类型的 Value 序列化器
@SuppressWarnings("rawtypes") private @Nullable RedisSerializer hashValueSerializer = null;
private RedisSerializer<String> stringSerializer = RedisSerializer.string();
private @Nullable ScriptExecutor<K> scriptExecutor;
//...省略代码...
}上面代码中,hashKeySerializer、hashValueSerializer、keySerializer、valueSerializer 分别对应 HASH 结构和非 HASH 结构的序列化机制。所以我们可以分别指定 key、value 的序列化机制,也可以针对 HASH 和非 HASH 进行区分处理。如果没有指定 HASH 结构和非 HASH 结构的序列化机制,RedisTemplate 会选择 defaultSerializer 的配置来进行序列化。
defaultSerializer 的默认实现是 JdkSerializationRedisSerializer,默认序列化的一个典型问题是针对对象,会保存成二进制格式,序列化效率上不是最差的,但是长度上会多出来尾巴,影响可读性和传输效率。例如:
// 定义 RedisTemplate Bean
@Bean
public RedisTemplate<String,Object> defSerialRedisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(factory);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
// 验证默认序列化器,保存一个 Date 对象到 Redis
@RunWith(SpringRunner.class)
@SpringBootTest
public class DefSerialRedisTemplate {
@Autowired
private RedisTemplate<String,Object> defSerialRedisTemplate;
@Test
public void demo() {
ValueOperations<String,Object> ops = defSerialRedisTemplate.opsForValue();
// 设置值
ops.set("key", new Date());
// 获取值
Date value = (Date) ops.get("key");
System.out.println(value);
}
}运行后,效果如下图:

为了改进上面的问题,我们可以替换默认序列化接口实现机制,例如:使用 Jackson2JsonRedisSerializer 或 StringRedisSerializer序列化器,亦或者自定义实现。下面以 Jackson 实现为例:
// 配置 RedisTemplate,只设置简单 key 和 value 的序列化器
@Bean
public RedisTemplate<String,Object> jsonSerialRedisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(factory);
// 指定 key 的序列化器,替代默认序列化器
redisTemplate.setKeySerializer(new StringRedisSerializer());
// 指定 value 的序列化器,替代默认序列化器
redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<Object>(Object.class));
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
// 使用上面配置的 RedisTemplate 保存 User
@RunWith(SpringRunner.class)
@SpringBootTest
public class JacksonSerialRedisTemplate {
@Autowired
private RedisTemplate<String,Object> jsonSerialRedisTemplate;
@Test
public void demo() {
ValueOperations<String,Object> ops = jsonSerialRedisTemplate.opsForValue();
// 设置值
ops.set("key", new User(100, "ZhangSan"));
// 获取值
Object value = ops.get("key");
System.out.println(value.getClass());
System.out.println(value);
}
class User {
private int id;
private String name;
public User() {}
public User(int id, String name) {
this.id = id;
this.name = name;
}
// 忽略 getter 和 setter 方法
}
}运行示例,输出如下:
class java.util.LinkedHashMap
{id=100, name=ZhangSan}从输出结果可知,存储在 Redis 中的内容是 JSON 字符串。