在 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 字符串。