叙述

RedisTemplate中的几个角色:

  1. RedisSerializer:由于与Redis服务器的通信一定是使用字节数组完成的,所以RedisSerializer是将Java对象编码解码的组件
  2. RedisOperations:封装了一些Redis操作
  3. XXXOperations:封装了指定类型或功能的数据的操作,如ZSetOperations

RedisSerializer

RedisSerializer提供了两个方法,一个用于序列化,一个用于反序列化。并且,它提供了一个泛型T,代表该序列化器处理的类型。

public interface RedisSerializer<T> {

	@Nullable
	byte[] serialize(@Nullable T t) throws SerializationException;

	@Nullable
	T deserialize(@Nullable byte[] bytes) throws SerializationException;

}

它的实现类有下面这些:

从实现类的名字可以看出,其中有将对象转换为json的,有使用JDK自带的序列化机制进行序列化反序列化的,有专门处理String的...

默认情况下,RedisTemplate使用JdkSerializationRedisSerializer,也就是JDK默认的序列化机制来进行序列化


默认序列化器#

RedisTemplate的成员属性中有如下和序列化器相关的属性:

// 是否启用默认序列化器
private boolean enableDefaultSerializer = true;
// 默认序列化器
private @Nullable RedisSerializer<?> defaultSerializer;

// 键的序列化器
@SuppressWarnings("rawtypes") private @Nullable RedisSerializer keySerializer = null;
// 值序列化器
@SuppressWarnings("rawtypes") private @Nullable RedisSerializer valueSerializer = null;
// hash键序列化器
@SuppressWarnings("rawtypes") private @Nullable RedisSerializer hashKeySerializer = null;
// hash值序列化器
@SuppressWarnings("rawtypes") private @Nullable RedisSerializer hashValueSerializer = null;
// 字符串序列化器,使用StringRedisSerializer
private RedisSerializer<String> stringSerializer = RedisSerializer.string();

从这里我们可以看出,我们可以对RedisTemplate进行设置,在不同的情况下使用不同的序列化器,如在hash值的序列化上使用Jdk序列化器,而在普通的值上使用字符串序列化器。

RedisTemplate继承了InitailizingBean,所以,在Bean的初始化阶段结束后,它的afterPropertiesSet方法被调用并执行了以下代码:

@Override
public void afterPropertiesSet() {
    super.afterPropertiesSet();
    boolean defaultUsed = false;
    // 如果默认初始化器为空,创建JdkSerializationRedisSerializer
    if (defaultSerializer == null) {
        defaultSerializer = new JdkSerializationRedisSerializer(
                classLoader != null ? classLoader : this.getClass().getClassLoader());
    }

    // 如果 enableDefaultSerializer = true
    // 将key/value/hashkey和hashvalue的序列化器都设置成默认序列化器
    if (enableDefaultSerializer) {
        if (keySerializer == null) {
            keySerializer = defaultSerializer;
            defaultUsed = true;
        }
        if (valueSerializer == null) {
            valueSerializer = defaultSerializer;
            defaultUsed = true;
        }
        if (hashKeySerializer == null) {
            hashKeySerializer = defaultSerializer;
            defaultUsed = true;
        }
        if (hashValueSerializer == null) {
            hashValueSerializer = defaultSerializer;
            defaultUsed = true;
        }
    }

    if (enableDefaultSerializer && defaultUsed) {
        Assert.notNull(defaultSerializer, "default serializer null and not all serializers initialized");
    }

    if (scriptExecutor == null) {
        this.scriptExecutor = new DefaultScriptExecutor<>(this);
    }

    initialized = true;
}

所以默认情况下,所有的(除了那个stringSerializer之外)操作都将使用Jdk自带的序列化和反序列化来对对象进行编解码,这要求你传入到Redis中的对象必须实现了Serializable接口

下面是默认情况下的一个示例:

@SpringBootTest
public class RedisTemplateTest {
    @Autowired
    private RedisTemplate redisTemplate;
    @Test
    void test() {
        redisTemplate.opsForValue().set("key", "value");
    }

}

去Redis中查询,出现了莫名其妙的前缀,这是Java序列化机制添加的。


序列化器的设置

在下面的例子中,我们将默认序列化器设置成了StringRedisSerializer,所以,在我们插入字符串时,它会按照字符串的方式编码

@Configuration
public class RedisTemplateConfig {
    @Bean
    public RedisTemplate redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate redisTemplate = new RedisTemplate();
        redisTemplate.setConnectionFactory(factory);
        redisTemplate.setDefaultSerializer(new StringRedisSerializer());
        return redisTemplate;
    }
}

再次运行之前的测试用例,redis中出现了这个结果:

StringRedisSerializer只支持字符串类型的转化,而且默认使用UTF-8编码,所以现在,如果你使用非String的对象,应该会出错

@Test
void testObject() {
    Student student = new Student();
    redisTemplate.opsForValue().set("student", student);
}

而在使用最初的Jdk序列化器时,则不会出现这个错误,下面,我们将键的序列化器设置成String的,将值的设置成Jdk序列化器,这样,我们可以保证在key上不会出现那些奇奇怪怪的前缀字符,而且可以将值设置成对象

@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory factory) {
    RedisTemplate redisTemplate = new RedisTemplate();
    redisTemplate.setConnectionFactory(factory);
    redisTemplate.setKeySerializer(new StringRedisSerializer());
    redisTemplate.setHashKeySerializer(new StringRedisSerializer());
    // 默认情况下不设置也是这个
    redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
    redisTemplate.setHashValueSerializer(new JdkSerializationRedisSerializer());
    return redisTemplate;
}


Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐