最近專案中使用SpringBoot集成Redis,踩到了一個坑:從Redis中獲取資料為null,但實際上Redis中是存在對應的資料的,是什么原因導致此坑的呢?
本文就帶大家從SpringBoot集成Redis、所踩的坑以及自動配置原始碼分析來學習一下SpringBoot中如何正確的使用Redis,
SpringBoot集成Redis
在SpringBoot專案中只需在pom檔案中引入Redis對應的starter,配置Redis連接資訊即可進行使用了,pom依賴引入:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
對應application組態檔配置:
spring:
redis:
host: 127.0.0.1
port: 6379
database: 1
password: 123456
timeout: 5000
通過以上兩項配置即完成了Redis的集成,下面便是具體的使用,這里以單元測驗的形式呈現,
@SpringBootTest
@RunWith(SpringRunner.class)
public class TokenTest {
@Autowired
private RedisTemplate redisTemplate;
@Test
public void getValue() {
Object value = https://www.cnblogs.com/rutaha/p/redisTemplate.opsForValue().get("1");
System.out.println("value:" + value);
}
}
可以看到直接通過@Autowired注入RedisTemplate之后,即可呼叫RedisTemplate提供的方法操作,RedisTemplate提供了豐富的Redis操作方法,具體使用查看相應的API即可,這里不再拓展,
專案中遇到的坑
回歸到最開始的問題:從Redis中獲取資料為null,但實際上Redis中是存在對應的資料的,
其實問題表象很詭異,但問題的原因很簡單,就是Redis中存資料和取資料時采用了不同的RedisTemplate導致的,
在SpringBoot中,針對Redis的自動配置類默認會初始化兩個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
@ConditionalOnMissingBean
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
可以看到RedisAutoConfiguration中初始化了兩個RedisTemplate的bean,第一個Bean型別為RedisTemplate<Object, Object>,Bean的名稱為redisTemplate,而且是當容器中不存在對應的Bean name時才會進行初始化,第二Bean型別為StringRedisTemplate,Bean的名稱為stringRedisTemplate,該類繼承自RedisTemplate<String, String>,
也就說一個Bean是針對Object物件處理的,一個是針對String物件進行處理的,
導致出現坑的原因便是set時注入的是RedisTemplate<Object, Object>,而獲取時注入的是StringRedisTemplate,這么明顯的錯誤應該很容易排查的啊?
問題為什么隱藏的那么深?
如果直接是因為兩處型別不一致導致的,的確很好排查,看一下注入的RedisTemplate即可,
但問題難以排查,還因為另外一個因素:@Resource和@Autowired注入的問題,
默認情況下@Resource采用先根據bean名稱注入,找不到再根據型別注入,而@Autowired默認采用根據型別注入,專案獲取資料時采用了@Resource注入方式,如下:
@Resource
private RedisTemplate<String, String> redisTemplate;
而存盤時采用的是@Autowired注入的:
@Autowired
private RedisTemplate<String, String> redisTemplate;
上面兩種形式的注入,在只存在單個實體時好像并不是什么問題,要么其中一個直接報錯,要么注入成功,但當像上述場景,出現了兩個RedisTemplate時,問題就變得隱蔽了,
當采用@Autowired時,根據型別注入,直接注入了RedisTemplate<String, String>的bean,因為它們的型別都是String的,
而當使用@Resource注入時,默認采用的是根據名稱匹配,原始碼中可以看到redisTemplate對應的型別為RedisTemplate<Object, Object>,因此,兩處注入了不同的RedisTemplate,于是就導致了獲取時獲取不到值的問題,
解決方案
找到問題的根源之后,解決問題便容易多了,
方案一,將@Resource的注入改為@Autowired,
方案二:將@Resource注入的bean名稱由redisTemplate改為stringRedisTemplate,當然根據具體業務場景還有其他解決方案,
小結
關于SpringBoot集成Redis其實很簡單,SpringBoot已經幫我們做了大多數的事情,但因為默認初始化了兩個RedisTemplate,再加上@Autowired和@Resource注解的區別就導致了問題的復雜度,因此,在使用的程序中盡量保持各處采用一致的規范,阿里Java開發手冊推薦使用@Resource注解,同時,當然少不了對原始碼、注解等的使用的深入學習和了解,
關注公眾號【有故事的程式員】后回復“資料”即可領取200+頁的《Java工程師面試指南》
強烈推薦,幾乎涵蓋所有Java工程師必知必會的知識點,不管是復習還是面試,都很實用,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/240337.html
標籤:Java
上一篇:面試一家小公司,被問了 1 個小時并發編程。。全程已記錄!
下一篇:java多重繼承
