- 問題描述:
項目中一個接口,服務器端不定期報錯ClassCastExcepiton,具體的堆棧信息如下。
- 查找問題:
1、可以看到這個錯誤是由getConfigValue()導致的,由於報錯信息是ClassCastExcepiton,所以主要從類型轉換異常的方向去考慮問題。
@Override
@Cacheable(cacheNames = {"sysConfig"},key = "#sysConfigKeyString")
public String getConfigValue(String sysConfigKeyString) {
return Optional.ofNullable(sysConfigMapper.checkConfigKeyUnique(sysConfigKeyString)).map(SysConfig::getConfigValue).orElse(null);
}
/**
* 根據key獲取值
* @param keys
* @return
*/
@Override
@Cacheable(cacheNames = {"sysConfig"},key = "#sysConfigKeyMap")
public Map<String,String> getConfigValues(String sysConfigKeyMap){
List<SysConfig> list= sysConfigMapper.selectListByKeys(Arrays.asList(sysConfigKeyMap.split(",")));
return list.stream().collect(Collectors.toMap(SysConfig::getConfigKey,SysConfig::getConfigValue));
}
2、以上是最初的業務代碼,從具體的邏輯看,好像並沒有什麼異常,getConfigValue方法中也並沒有存在類型轉換的代碼。然後就開始了百度之旅,在網上百度各種原因,有說是由於CGILB導致的,有說是DEVTOOLS導致的……無果
3、靜下心,由於這個錯誤是不定期出現,開始分析業務代碼,嘗試着復現這個異常。在經過多種嘗試後,發現這個問題是在某種特定的情況下調用了getConfigValues()方法之後(sysConfigKeyString和sysConfigKeyMap傳入值相同時),會必現這個問題。
4、由此發現問題的突破口是在緩存上。由於我們使用了@Cacheable註解,會自動將查詢過後的數據進行緩存,再次讀取的時候會從緩存中取得相應的數據
5、由於getConfigValues緩存的是map格式的數據,getConfigValue緩存的String類型的數據,兩個使用的是同一個cacheNames。由此,才查詢相同key(sysConfigKeyString和sysConfigKeyMap相同)的時候,會優先讀取緩存中的數據,因此可能會造成類型轉換異常的問題。
- 問題解決:
找到問題之後,我們將map和string兩種格式的key存儲到不同的cacheNames中,可以解決上述問題。
@Override
@Cacheable(cacheNames = {"sysConfigString"},key = "#sysConfigKeyString")
public String getConfigValue(String sysConfigKeyString) {
return Optional.ofNullable(sysConfigMapper.checkConfigKeyUnique(sysConfigKeyString)).map(SysConfig::getConfigValue).orElse(null);
}
/**
* 根據key獲取值
* @param keys
* @return
*/
@Override
@Cacheable(cacheNames = {"sysConfigMap"},key = "#sysConfigKeyMap")
public Map<String,String> getConfigValues(String sysConfigKeyMap){
List<SysConfig> list= sysConfigMapper.selectListByKeys(Arrays.asList(sysConfigKeyMap.split(",")));
return list.stream().collect(Collectors.toMap(SysConfig::getConfigKey,SysConfig::getConfigValue));
}
-
問題總結:
1、遇到代碼問題優先靜心排查業務代碼問題
2、在Cacheable的使用過程中,一定要注意同個cacheName中key值相同的可能覆蓋的問題。