Spring Boot集成Redis,這個坑把我害慘了!

最近項目中使用SpringBoot集成Redis,踩到了一個坑:從Redis中獲取數(shù)據(jù)為null,但實際上Redis中是存在對應(yīng)的數(shù)據(jù)的。是什么原因?qū)е麓丝拥哪兀?/span>
本文就帶大家從SpringBoot集成Redis、所踩的坑以及自動配置源碼分析來學(xué)習(xí)一下SpringBoot中如何正確的使用Redis。
SpringBoot集成Redis
在SpringBoot項目中只需在pom文件中引入Redis對應(yīng)的starter,配置Redis連接信息即可進(jìn)行使用了。pom依賴引入:
????org.springframework.boot
????spring-boot-starter-data-redis
對應(yīng)application配置文件配置:
spring:
??redis:
????host:?127.0.0.1
????port:?6379
????database:?1
????password:?123456
????timeout:?5000
通過以上兩項配置即完成了Redis的集成,下面便是具體的使用,這里以單元測試的形式呈現(xiàn)。
@SpringBootTest
@RunWith(SpringRunner.class)
public?class?TokenTest?{
????@Autowired
????private?RedisTemplate?redisTemplate;
????@Test
????public?void?getValue()?{
????????Object?value?=?redisTemplate.opsForValue().get("1");
????????System.out.println("value:"?+?value);
????}
}
可以看到直接通過@Autowired注入RedisTemplate之后,即可調(diào)用RedisTemplate提供的方法操作。RedisTemplate提供了豐富的Redis操作方法,具體使用查看相應(yīng)的API即可,這里不再拓展。
項目中遇到的坑
回歸到最開始的問題:從Redis中獲取數(shù)據(jù)為null,但實際上Redis中是存在對應(yīng)的數(shù)據(jù)的。
其實問題表象很詭異,但問題的原因很簡單,就是Redis中存數(shù)據(jù)和取數(shù)據(jù)時采用了不同的RedisTemplate導(dǎo)致的。
在SpringBoot中,針對Redis的自動配置類默認(rèn)會初始化兩個RedisTemplate,先來看一下RedisAutoConfiguration中源碼:
@Configuration
@ConditionalOnClass({RedisOperations.class})
@EnableConfigurationProperties({RedisProperties.class})
@Import({LettuceConnectionConfiguration.class,?JedisConnectionConfiguration.class})
public?class?RedisAutoConfiguration?{
????@Bean
????@ConditionalOnMissingBean(
????????name?=?{"redisTemplate"}
????)
????public?RedisTemplate可以看到RedisAutoConfiguration中初始化了兩個RedisTemplate的bean。第一個Bean類型為RedisTemplate
也就說一個Bean是針對Object對象處理的,一個是針對String對象進(jìn)行處理的。
導(dǎo)致出現(xiàn)坑的原因便是set時注入的是RedisTemplate
問題為什么隱藏的那么深?
如果直接是因為兩處類型不一致導(dǎo)致的,的確很好排查,看一下注入的RedisTemplate即可。
但問題難以排查,還因為另外一個因素:@Resource和@Autowired注入的問題。
默認(rèn)情況下@Resource采用先根據(jù)bean名稱注入,找不到再根據(jù)類型注入,而@Autowired默認(rèn)采用根據(jù)類型注入。項目獲取數(shù)據(jù)時采用了@Resource注入方式,如下:
@Resource
private?RedisTemplate?redisTemplate;
而存儲時采用的是@Autowired注入的:
@Autowired
private?RedisTemplate?redisTemplate;
上面兩種形式的注入,在只存在單個實例時好像并不是什么問題,要么其中一個直接報錯,要么注入成功。但當(dāng)像上述場景,出現(xiàn)了兩個RedisTemplate時,問題就變得隱蔽了。
當(dāng)采用@Autowired時,根據(jù)類型注入,直接注入了RedisTemplate
而當(dāng)使用@Resource注入時,默認(rèn)采用的是根據(jù)名稱匹配,源碼中可以看到redisTemplate對應(yīng)的類型為RedisTemplate
解決方案
找到問題的根源之后,解決問題便容易多了。
方案一,將@Resource的注入改為@Autowired。
方案二:將@Resource注入的bean名稱由redisTemplate改為stringRedisTemplate。當(dāng)然根據(jù)具體業(yè)務(wù)場景還有其他解決方案。
小結(jié)
關(guān)于SpringBoot集成Redis其實很簡單,SpringBoot已經(jīng)幫我們做了大多數(shù)的事情,但因為默認(rèn)初始化了兩個RedisTemplate,再加上@Autowired和@Resource注解的區(qū)別就導(dǎo)致了問題的復(fù)雜度。因此,在使用的過程中盡量保持各處采用一致的規(guī)范,阿里Java開發(fā)手冊推薦使用@Resource注解。同時,當(dāng)然少不了對源碼、注解等的使用的深入學(xué)習(xí)和了解。

往期推薦

文件寫入的6種方法,這種方法性能最好

線程池的7種創(chuàng)建方式,強烈推薦你用它...

求求你,別再用wait和notify了!
