Spring Data Redis 教程

Spring Data Redis 序列化简介

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


说说我的看法
全部评论(
没有评论
关于
本网站属于个人的非赢利性网站,转载的文章遵循原作者的版权声明,如果原文没有版权声明,请来信告知:hxstrive@outlook.com
公众号