SpringBoot混合使用StringRedisTemplate和RedisTemplate的坑及解決
但在實踐中,有朋友遇到這樣的問題,就是存儲到Redis數(shù)據(jù)取不到值。
兩種Template的源碼分析
這是為什么呢?是因為他同時使用了StringRedisTemplate和RedisTemplate在Redis中存儲和讀取數(shù)據(jù)。
它們最重要的一個區(qū)別就是默認采用的序列化方式不同(在課程中已經(jīng)講到)。
這里我們再來回顧一下相關(guān)源碼,StringRedisTemplate的部分源碼如下:
public class StringRedisTemplate extends RedisTemplate<String, String> {
/**
* Constructs a new <code>StringRedisTemplate</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中存儲數(shù)據(jù)的Key的。
下面再來看看RedisTemplate中默認采用什么形式來序列化對應的Key。
public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperations<K, V>, BeanClassLoaderAware {
// 省略其他源碼
private @Nullable RedisSerializer<?> defaultSerializer;
private @Nullable ClassLoader classLoader;
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.RedisAccessor#afterPropertiesSet()
*/
@Override
public 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
@SpringBootTest
class RedisDifferentTemplateTest {
@Resource
private RedisTemplate<String, Object> redisTemplate;
@Resource
private StringRedisTemplate stringRedisTemplate;
@Test
void testSimple() {
redisTemplate.opsForValue().set("myWeb", "www.choupangxia.com");
Assertions.assertEquals("www.choupangxia.com", redisTemplate.opsForValue().get("myWeb"));
Assertions.assertEquals("www.choupangxia.com",stringRedisTemplate.opsForValue().get("myWeb"));
}
}
在上述方法中先通過redisTemplate存儲一個key為myWeb的數(shù)據(jù)到Redis中,隨后通過redisTemplate獲取并判斷斷言,可以成功通過。
但隨后通過stringRedisTemplate獲取同樣的key的值,則拋出異常,異常信息如下:
org.opentest4j.AssertionFailedError:
Expected :www.choupangxia.com
Actual :null
<Click to see difference>
也就是說獲取的結(jié)果為null。
那么,我們再通過Redis客戶端看一下兩種形式存儲到redis中key的值的情況。

我們可以看到通過StringRedisTemplate存儲的數(shù)據(jù)Key為“myWeb”,而RedisTemplate存儲的Key為“\xAC\xED\x00\x05t\x00\x05myWeb”,這也就是為什么默認情況下兩者存儲的數(shù)據(jù)沒辦法混合使用了。
解決方案
那么,如果在生產(chǎn)環(huán)境中想通用StringRedisTemplate和RedisTemplate進行字符串的處理該怎么辦?
此時就需要指定統(tǒng)一的Key的序列化處理類,比如在RedisTemplate序列化時指定與StringRedisTemplate相同的類。
在上述單元測試中添加如下方法:
@BeforeEach
void init() {
redisTemplate.setKeySerializer(RedisSerializer.string());
}
也就是設(shè)置RedisTemplate也使用RedisSerializer.string()來序列化Key。注意此處使用的是Junit5。
這樣就解決問題了嗎?沒有。因為RedisTemplate的Value也是采用默認的序列化類,也要進行統(tǒng)一修改。
因此上面的方法變成如下:
@BeforeEach
void init() {
redisTemplate.setKeySerializer(RedisSerializer.string());
redisTemplate.setValueSerializer(RedisSerializer.string());
}
總結(jié)
經(jīng)過上述步驟,關(guān)于SpringBoot中混合使用StringRedisTemplate和RedisTemplate的坑已經(jīng)填平了。
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
- 在SpringBoot中注入RedisTemplate實例異常的解決方案
- SpringBoot整合Redis使用@Cacheable和RedisTemplate
- SpringBoot整合RedisTemplate實現(xiàn)緩存信息監(jiān)控的步驟
- 詳解SpringBoot使用RedisTemplate操作Redis的5種數(shù)據(jù)類型
- SpringBoot整合Redis使用RedisTemplate和StringRedisTemplate
- springboot使用redisRepository和redistemplate操作redis的過程解析
- SpringBoot集成RedisTemplate的實現(xiàn)示例
相關(guān)文章
javaSE基礎(chǔ)如何通俗的理解javaBean是什么
所謂的Java Bean,就是一個java類,編譯后成為了一個后綴名是 .class的文件。這就是Java Bean,很多初學者,包括當年的我自己,總是被這些專有名詞搞的暈頭轉(zhuǎn)向2021-10-10
Java實現(xiàn)統(tǒng)計文檔中關(guān)鍵字出現(xiàn)的次數(shù)
這篇文章主要為大家分享了利用Java語言實現(xiàn)統(tǒng)計關(guān)鍵字在文檔中出現(xiàn)的次數(shù)的方法,文中的示例代碼講解詳細,感興趣的小伙伴可以了解一下2022-05-05

