<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

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

          共 3337字,需瀏覽 7分鐘

           ·

          2020-12-29 14:31

          最近項目中使用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?redisTemplate(RedisConnectionFactory?redisConnectionFactory)?throws?UnknownHostException?{
          ????????RedisTemplate?template?=?new?RedisTemplate();
          ????????template.setConnectionFactory(redisConnectionFactory);
          ????????return?template;
          ????}

          ????@Bean
          ????@ConditionalOnMissingBean
          ????public?StringRedisTemplate?stringRedisTemplate(RedisConnectionFactory?redisConnectionFactory)?throws?UnknownHostException?{
          ????????StringRedisTemplate?template?=?new?StringRedisTemplate();
          ????????template.setConnectionFactory(redisConnectionFactory);
          ????????return?template;
          ????}
          }

          可以看到RedisAutoConfiguration中初始化了兩個RedisTemplate的bean。第一個Bean類型為RedisTemplate,Bean的名稱為redisTemplate,而且是當(dāng)容器中不存在對應(yīng)的Bean name時才會進(jìn)行初始化。第二Bean類型為StringRedisTemplate,Bean的名稱為stringRedisTemplate,該類繼承自RedisTemplate。

          也就說一個Bean是針對Object對象處理的,一個是針對String對象進(jìn)行處理的。

          導(dǎo)致出現(xiàn)坑的原因便是set時注入的是RedisTemplate,而獲取時注入的是StringRedisTemplate。這么明顯的錯誤應(yīng)該很容易排查的???

          問題為什么隱藏的那么深?

          如果直接是因為兩處類型不一致導(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的bean,因為它們的類型都是String的。

          而當(dāng)使用@Resource注入時,默認(rèn)采用的是根據(jù)名稱匹配,源碼中可以看到redisTemplate對應(yīng)的類型為RedisTemplate。因此,兩處注入了不同的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)建方式,強烈推薦你用它...


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


          關(guān)注我,每天陪你進(jìn)步一點點!

          瀏覽 52
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲欧美第一页 | 日日日日操 | 熟女久久久久久久 | 国产宴妇精品久久久久久 | 日本黄色一级片日 |