【Java踩坑記】——SpringBoot中使用@Cacheable

  • 問題描述:

項目中一個接口,服務器端不定期報錯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值相同的可能覆蓋的問題。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章