javascript
Spring Boot集成Redis,这个坑把我害惨了!
最近項目中使用SpringBoot集成Redis,踩到了一個坑:從Redis中獲取數(shù)據(jù)為null,但實際上Redis中是存在對應(yīng)的數(shù)據(jù)的。是什么原因?qū)е麓丝拥哪?#xff1f;
本文就帶大家從SpringBoot集成Redis、所踩的坑以及自動配置源碼分析來學(xué)習(xí)一下SpringBoot中如何正確的使用Redis。
SpringBoot集成Redis
在SpringBoot項目中只需在pom文件中引入Redis對應(yīng)的starter,配置Redis連接信息即可進(jìn)行使用了。pom依賴引入:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId> </dependency>對應(yīng)application配置文件配置:
spring:redis:host:?127.0.0.1port:?6379database:?1password:?123456timeout:?5000通過以上兩項配置即完成了Redis的集成,下面便是具體的使用,這里以單元測試的形式呈現(xiàn)。
@SpringBootTest @RunWith(SpringRunner.class) public?class?TokenTest?{@Autowiredprivate?RedisTemplate?redisTemplate;@Testpublic?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<Object,?Object>?redisTemplate(RedisConnectionFactory?redisConnectionFactory)?throws?UnknownHostException?{RedisTemplate<Object,?Object>?template?=?new?RedisTemplate();template.setConnectionFactory(redisConnectionFactory);return?template;}@Bean@ConditionalOnMissingBeanpublic?StringRedisTemplate?stringRedisTemplate(RedisConnectionFactory?redisConnectionFactory)?throws?UnknownHostException?{StringRedisTemplate?template?=?new?StringRedisTemplate();template.setConnectionFactory(redisConnectionFactory);return?template;} }可以看到RedisAutoConfiguration中初始化了兩個RedisTemplate的bean。第一個Bean類型為RedisTemplate<Object, Object>,Bean的名稱為redisTemplate,而且是當(dāng)容器中不存在對應(yīng)的Bean name時才會進(jìn)行初始化。第二Bean類型為StringRedisTemplate,Bean的名稱為stringRedisTemplate,該類繼承自RedisTemplate<String, String>。
也就說一個Bean是針對Object對象處理的,一個是針對String對象進(jìn)行處理的。
導(dǎo)致出現(xiàn)坑的原因便是set時注入的是RedisTemplate<Object, Object>,而獲取時注入的是StringRedisTemplate。這么明顯的錯誤應(yīng)該很容易排查的啊?
問題為什么隱藏的那么深?
如果直接是因為兩處類型不一致導(dǎo)致的,的確很好排查,看一下注入的RedisTemplate即可。
但問題難以排查,還因為另外一個因素:@Resource和@Autowired注入的問題。
默認(rèn)情況下@Resource采用先根據(jù)bean名稱注入,找不到再根據(jù)類型注入,而@Autowired默認(rèn)采用根據(jù)類型注入。項目獲取數(shù)據(jù)時采用了@Resource注入方式,如下:
@Resource private?RedisTemplate<String,?String>?redisTemplate;而存儲時采用的是@Autowired注入的:
@Autowired private?RedisTemplate<String,?String>?redisTemplate;上面兩種形式的注入,在只存在單個實例時好像并不是什么問題,要么其中一個直接報錯,要么注入成功。但當(dāng)像上述場景,出現(xiàn)了兩個RedisTemplate時,問題就變得隱蔽了。
當(dāng)采用@Autowired時,根據(jù)類型注入,直接注入了RedisTemplate<String, String>的bean,因為它們的類型都是String的。
而當(dāng)使用@Resource注入時,默認(rèn)采用的是根據(jù)名稱匹配,源碼中可以看到redisTemplate對應(yīng)的類型為RedisTemplate<Object, Object>。因此,兩處注入了不同的RedisTemplate,于是就導(dǎo)致了獲取時獲取不到值的問題。
解決方案
找到問題的根源之后,解決問題便容易多了。
方案一,將@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)建方式,強(qiáng)烈推薦你用它...
求求你,別再用wait和notify了!
關(guān)注我,每天陪你進(jìn)步一點點!
總結(jié)
以上是生活随笔為你收集整理的Spring Boot集成Redis,这个坑把我害惨了!的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 互动直播的视频录制与合成—支持多人离线重
- 下一篇: 这8种常见的SQL错误用法,你还在用吗?