叙述
RedisTemplate
中的几个角色:
RedisSerializer
:由于与Redis服务器的通信一定是使用字节数组完成的,所以RedisSerializer
是将Java对象编码解码的组件RedisOperations
:封装了一些Redis操作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;
}