AssertionFailedError

在《SpringBoot视频教程全家桶》系列教程中,我们分别讲解了StringRedisTemplate和RedisTemplate的使用和区别。

但在实践中,有朋友遇到这样的问题,就是存储到Redis数据取不到值。

两种Template的源码分析这是为什么呢?是因为他同时使用了StringRedisTemplate和RedisTemplate在Redis中存储和读取数据。它们最重要的一个区别就是默认采用的序列化方式不同(在课程中已经讲到)。这里我们再来回顾一下相关源码,StringRedisTemplate的部分源码如下:

public class StringRedisTemplate extends RedisTemplateString, String {/** * Constructs a new codeStringRedisTemplate/code instance. {@link #setConnectionFactory(RedisConnectionFactory)} * and {@link #afterPropertiesSet()} still need to be called. */public StringRedisTemplate() {setKeySerializer(RedisSerializer.string());setValueSerializer(RedisSerializer.string());setHashKeySerializer(RedisSerializer.string());setHashValueSerializer(RedisSerializer.string());} }通过该源码我们可以看到StringRedisTemplate采用的是RedisSerializer.string()来序列化Redis中存储数据的Key的。

下面再来看看RedisTemplate中默认采用什么形式来序列化对应的Key。

public class RedisTemplateK, V extends RedisAccessor implements RedisOperationsK, V, BeanClassLoaderAware { // 省略其他源码private @Nullable RedisSerializer? defaultSerializer;private @Nullable ClassLoader classLoader;/* * (non-Javadoc) * @see org.springframework.data.redis.core.RedisAccessor#afterPropertiesSet() */@Overridepublic void afterPropertiesSet() {super.afterPropertiesSet();boolean defaultUsed = false;if (defaultSerializer == null) {defaultSerializer = new JdkSerializationRedisSerializer(classLoader != null ? classLoader : this.getClass().getClassLoader());}if (enableDefaultSerializer) {if (keySerializer == null) {keySerializer = 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;}// 省略其他源码 }我们可以看到RedisTemplate使用的序列化类为defaultSerializer,默认情况下为JdkSerializationRedisSerializer。如果未指定Key的序列化类,keySerializer与defaultSerializer采用相同的序列化类。

通过上述两个Template的分析我们就可以看出它们在Redis存储的Key,采用了不同的序列化方法。

而且JdkSerializationRedisSerializer序列化时会在Key的前面添加一些特殊字符。

还原测试下面先看一个单元测试:

@Slf4j@SpringBootTestclass RedisDifferentTemplateTest {@Resourceprivate RedisTemplateString, Object redisTemplate;@Resourceprivate StringRedisTemplate stringRedisTemplate;@Testvoid testSimple() {redisTemplate.opsForValue().set("myWeb", "的值,则抛出异常,异常信息如下:

org.opentest4j.AssertionFailedError: Expected :。

那么,我们再通过Redis客户端看一下两种形式存储到redis中key的值的情况。

AssertionFailedError

我们可以看到通过StringRedisTemplate存储的数据Key为“myWeb”,而RedisTemplate存储的Key为“\xAC\xED\x00\x05t\x00\x05myWeb”,这也就是为什么默认情况下两者存储的数据没办法混合使用了。

解决方案那么,如果在生产环境中想通用StringRedisTemplate和RedisTemplate进行字符串的处理该怎么办?

此时就需要指定统一的Key的序列化处理类,比如在RedisTemplate序列化时指定与StringRedisTemplate相同的类。

在上述单元测试中添加如下方法:

@BeforeEachvoid init() {redisTemplate.setKeySerializer(RedisSerializer.string());}也就是设置RedisTemplate也使用RedisSerializer.string()来序列化Key。注意此处使用的是Junit5。

这样就解决问题了吗?没有。因为RedisTemplate的Value也是采用默认的序列化类,也要进行统一修改。

因此上面的方法变成如下:

@BeforeEachvoid init() {redisTemplate.setKeySerializer(RedisSerializer.string());redisTemplate.setValueSerializer(RedisSerializer.string());}小结经过上述步骤,关于SpringBoot中混合使用StringRedisTemplate和RedisTemplate的坑已经填平了。

本文首发于公众号:程序新视界。更多精彩内容,请关注一下。

(0)
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,请发送邮件至 ZLME@ZLME.COM 举报,一经查实,立刻删除。

相关推荐