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中默認采用什么形式來序列化對應(yīng)的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)文章
Java高級架構(gòu)之FastDFS分布式文件集群詳解
這篇文章主要介紹了Java高級架構(gòu)之FastDFS分布式文件集群詳解,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-04-04
詳解mall整合SpringBoot+MyBatis搭建基本骨架
這篇文章主要介紹了詳解mall整合SpringBoot+MyBatis搭建基本骨架,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-08-08
Eclipse開發(fā)JavaWeb項目配置Tomcat的方法步驟
本文主要介紹了Eclipse開發(fā)JavaWeb項目配置Tomcat的方法步驟,首先介紹eclipse開發(fā)JavaWeb項目需要配置的相關(guān)環(huán)境,使用tomcat軟件在本地搭建服務(wù)器,然后再在eclipse環(huán)境下配置tomcat,感興趣的可以了解一下2021-08-08
SpringBoot集成gRPC微服務(wù)工程搭建實踐的方法
這篇文章主要介紹了SpringBoot集成gRPC微服務(wù)工程搭建實踐的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-01-01
Spring?Boot?3.1中整合Spring?Security和Keycloak的方法
本文介紹在最新的SpringBoot3.1版本之下,如何將Keycloak和Spring?Security一起跑起來,文中結(jié)合實例代碼給大家介紹的非常詳細,需要的朋友參考下吧2023-06-06

