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的前面添加一些特殊字符。
還原測試
下面先看一個(gè)單元測試:
@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相同的類。
在上述單元測試中添加如下方法:
@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è)參考,也希望大家多多支持腳本之家。
相關(guān)文章
SpringBoot在idea中的 .idea和 .iml文件的作用
本文主要介紹了SpringBoot在idea中的 .idea和 .iml文件,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-08-08InterProcessMutex實(shí)現(xiàn)zookeeper分布式鎖原理
本文主要介紹了InterProcessMutex實(shí)現(xiàn)zookeeper分布式鎖原理,文中根據(jù)實(shí)例編碼詳細(xì)介紹的十分詳盡,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03JDK安裝方法和Linux常見設(shè)置詳細(xì)版教程
這篇文章主要給大家介紹了關(guān)于JDK安裝方法和Linux常見設(shè)置的相關(guān)資料,文章詳細(xì)介紹了如何在Linux系統(tǒng)中設(shè)置靜態(tài)IP、用戶名和主機(jī)名,配置防火墻,安裝JDK以及如何創(chuàng)建系統(tǒng)快照,需要的朋友可以參考下2024-11-11java虛擬機(jī)學(xué)習(xí)筆記進(jìn)階篇
在本篇內(nèi)容里小編給大家分享了關(guān)于java虛擬機(jī)學(xué)習(xí)筆記的進(jìn)階內(nèi)容,需要的朋友們跟著學(xué)習(xí)下。2019-06-06Java Netty實(shí)現(xiàn)心跳機(jī)制過程解析
這篇文章主要介紹了Java Netty實(shí)現(xiàn)心跳機(jī)制過程解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03Spring整合mybatis實(shí)現(xiàn)過程詳解
這篇文章主要介紹了Spring整合mybatis實(shí)現(xiàn)過程詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-07-07SpringBoot調(diào)用WebService接口的實(shí)現(xiàn)示例
本文主要介紹了SpringBoot調(diào)用WebService接口的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2025-03-03