SpringBoot混合使用StringRedisTemplate和RedisTemplate的坑及解決
但在實(shí)踐中,有朋友遇到這樣的問題,就是存儲(chǔ)到Redis數(shù)據(jù)取不到值。
兩種Template的源碼分析
這是為什么呢?是因?yàn)樗瑫r(shí)使用了StringRedisTemplate和RedisTemplate在Redis中存儲(chǔ)和讀取數(shù)據(jù)。
它們最重要的一個(gè)區(qū)別就是默認(rèn)采用的序列化方式不同(在課程中已經(jīng)講到)。
這里我們?cè)賮砘仡櫼幌孪嚓P(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中存儲(chǔ)數(shù)據(jù)的Key的。
下面再來看看RedisTemplate中默認(rèn)采用什么形式來序列化對(duì)應(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,默認(rèn)情況下為JdkSerializationRedisSerializer。
如果未指定Key的序列化類,keySerializer與defaultSerializer采用相同的序列化類。
通過上述兩個(gè)Template的分析我們就可以看出它們?cè)赗edis存儲(chǔ)的Key,采用了不同的序列化方法。
而且JdkSerializationRedisSerializer序列化時(shí)會(huì)在Key的前面添加一些特殊字符。
還原測(cè)試
下面先看一個(gè)單元測(cè)試:
@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存儲(chǔ)一個(gè)key為myWeb的數(shù)據(jù)到Redis中,隨后通過redisTemplate獲取并判斷斷言,可以成功通過。
但隨后通過stringRedisTemplate獲取同樣的key的值,則拋出異常,異常信息如下:
org.opentest4j.AssertionFailedError:
Expected :www.choupangxia.com
Actual :null
<Click to see difference>
也就是說獲取的結(jié)果為null。
那么,我們?cè)偻ㄟ^Redis客戶端看一下兩種形式存儲(chǔ)到redis中key的值的情況。

我們可以看到通過StringRedisTemplate存儲(chǔ)的數(shù)據(jù)Key為“myWeb”,而RedisTemplate存儲(chǔ)的Key為“\xAC\xED\x00\x05t\x00\x05myWeb”,這也就是為什么默認(rèn)情況下兩者存儲(chǔ)的數(shù)據(jù)沒辦法混合使用了。
解決方案
那么,如果在生產(chǎn)環(huán)境中想通用StringRedisTemplate和RedisTemplate進(jìn)行字符串的處理該怎么辦?
此時(shí)就需要指定統(tǒng)一的Key的序列化處理類,比如在RedisTemplate序列化時(shí)指定與StringRedisTemplate相同的類。
在上述單元測(cè)試中添加如下方法:
@BeforeEach
void init() {
redisTemplate.setKeySerializer(RedisSerializer.string());
}
也就是設(shè)置RedisTemplate也使用RedisSerializer.string()來序列化Key。注意此處使用的是Junit5。
這樣就解決問題了嗎?沒有。因?yàn)镽edisTemplate的Value也是采用默認(rèn)的序列化類,也要進(jìn)行統(tǒng)一修改。
因此上面的方法變成如下:
@BeforeEach
void init() {
redisTemplate.setKeySerializer(RedisSerializer.string());
redisTemplate.setValueSerializer(RedisSerializer.string());
}
總結(jié)
經(jīng)過上述步驟,關(guān)于SpringBoot中混合使用StringRedisTemplate和RedisTemplate的坑已經(jīng)填平了。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
- 在SpringBoot中注入RedisTemplate實(shí)例異常的解決方案
- SpringBoot整合Redis使用@Cacheable和RedisTemplate
- SpringBoot整合RedisTemplate實(shí)現(xiàn)緩存信息監(jiān)控的步驟
- 詳解SpringBoot使用RedisTemplate操作Redis的5種數(shù)據(jù)類型
- SpringBoot整合Redis使用RedisTemplate和StringRedisTemplate
- springboot使用redisRepository和redistemplate操作redis的過程解析
- SpringBoot集成RedisTemplate的實(shí)現(xiàn)示例
相關(guān)文章
Java高級(jí)架構(gòu)之FastDFS分布式文件集群詳解
這篇文章主要介紹了Java高級(jí)架構(gòu)之FastDFS分布式文件集群詳解,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-04-04
詳解mall整合SpringBoot+MyBatis搭建基本骨架
這篇文章主要介紹了詳解mall整合SpringBoot+MyBatis搭建基本骨架,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08
Eclipse開發(fā)JavaWeb項(xiàng)目配置Tomcat的方法步驟
本文主要介紹了Eclipse開發(fā)JavaWeb項(xiàng)目配置Tomcat的方法步驟,首先介紹eclipse開發(fā)JavaWeb項(xiàng)目需要配置的相關(guān)環(huán)境,使用tomcat軟件在本地搭建服務(wù)器,然后再在eclipse環(huán)境下配置tomcat,感興趣的可以了解一下2021-08-08
SpringBoot集成gRPC微服務(wù)工程搭建實(shí)踐的方法
這篇文章主要介紹了SpringBoot集成gRPC微服務(wù)工程搭建實(shí)踐的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-01-01
Spring?Boot?3.1中整合Spring?Security和Keycloak的方法
本文介紹在最新的SpringBoot3.1版本之下,如何將Keycloak和Spring?Security一起跑起來,文中結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友參考下吧2023-06-06
Java實(shí)現(xiàn)分布式鎖的3種方法總結(jié)
分布式鎖是一種用于保證分布式系統(tǒng)中多個(gè)進(jìn)程或線程同步訪問共享資源的技術(shù),同時(shí)它又是面試中的常見問題,所以我們本文就重點(diǎn)來看分布式鎖的具體實(shí)現(xiàn),希望對(duì)大家有所幫助2023-09-09

