Spring Boot @Cacheable与@CachePut缓存始终为空问题求助
Spring Boot缓存始终为空的问题排查与修复
问题概述
构建Spring Boot应用处理文本文件中的加载数据,调用标注@Cacheable/@CachePut的UpdateCacheService方法后缓存始终为空,即使存在大量重复customer_id的数据也无法命中缓存,通过CacheManager直接访问缓存返回null。
核心问题分析
1. @Cacheable方法返回null导致缓存不存储
Spring Cache默认不会将null值存入缓存。loadCachedCustomerDailyData方法返回null,首次调用缓存未命中后,不会在缓存中创建任何条目,后续调用依然会执行方法返回null,导致缓存永远无数据。
2. @CachePut的key表达式错误
insertRecordIntoCache方法参数是DailyLoadInfo,但key使用#load.customerId,方法内不存在load参数,SpEL解析失败,无法将值存入缓存。updateWeeklyCache方法参数只有BigDecimal total,key使用#load.customerId,同样无load参数,无法关联到具体客户的缓存条目。
3. @Cacheable的key字段不一致
loadCachedCustomerWeeklyTotal的key使用#load.customer_id(下划线),而其他方法使用#load.customerId(驼峰),如果Load类的字段是驼峰命名customerId,该key无法匹配到正确的缓存条目,导致缓存不命中。
4. @CacheEvict动态缓存名称的潜在问题
clearCache方法的value = "#cacheName",Spring Cache默认不支持这种动态指定缓存名称的方式(ConcurrentMapCacheManager不支持),会导致清除缓存操作失效。
修复步骤
1. 修正@Cacheable方法的返回值
缓存未命中时返回默认初始化对象,而非null,确保缓存能存储条目:
@Cacheable(value = DAILY_LOADS_CACHE, key = "#load.customerId") public DailyLoadInfo loadCachedCustomerDailyData(Load load) { // 返回初始化的DailyLoadInfo,而非null DailyLoadInfo defaultInfo = new DailyLoadInfo(); defaultInfo.setCustomerId(load.getCustomerId()); defaultInfo.setTransactionDate(load.getTransactionDate()); defaultInfo.setLoadCount(0); defaultInfo.setTotalAmount(BigDecimal.ZERO); return defaultInfo; } @Cacheable(value = WEEKLY_LOADS_CACHE, key = "#load.customerId") public BigDecimal loadCachedCustomerWeeklyTotal(Load load) { return BigDecimal.ZERO; // 返回0会正常存入缓存 }
2. 修正@CachePut的key表达式和方法参数
确保key能正确关联到目标缓存条目,传递必要的关联参数:
@CachePut(value = DAILY_LOADS_CACHE, key = "#dailyCustomerLoadInfo.customerId") public DailyLoadInfo insertRecordIntoCache(DailyLoadInfo dailyCustomerLoadInfo) { return dailyCustomerLoadInfo; } // 修改方法签名,传入customerId用于定位缓存key @CachePut(value = WEEKLY_LOADS_CACHE, key = "#customerId") public BigDecimal updateWeeklyCache(String customerId, BigDecimal total) { return total; }
3. 统一@Cacheable的key字段
保持所有缓存key的字段命名一致:
@Cacheable(value = WEEKLY_LOADS_CACHE, key = "#load.customerId") // 改为驼峰的customerId public BigDecimal loadCachedCustomerWeeklyTotal(Load load) { return BigDecimal.ZERO; }
4. 修正CacheManagementService中的调用逻辑
对应修改updateWeeklyCache的调用,传入customerId:
if (isDatabaseSuccess) { this.lastSeenDate = this.updateCacheService.insertRecordIntoCache(dailyCustomerLoadInfo).getTransactionDate(); // 传入load的customerId作为缓存key的参数 this.updateCacheService.updateWeeklyCache(load.getCustomerId(), load.getLoadAmount().add(weeklyLoadAmount)); }
5. 修复@CacheEvict的动态缓存名称问题
改用CacheManager直接操作缓存,替代动态@CacheEvict:
// 移除@CacheEvict注解 public void clearCache(String cacheName) { Cache cache = cacheManager.getCache(cacheName); if (cache != null) { cache.clear(); } }
额外验证建议
- 在缓存方法中添加日志,打印缓存命中/未命中状态,确认缓存逻辑执行情况。
- 使用
cacheManager.getCache(DAILY_LOADS_CACHE).get("528")(替换为实际customerId)在代码中打印缓存内容,验证是否成功存入。 - 移除重复的
@EnableCaching注解(主类或配置类保留一个即可),避免冗余配置。
内容的提问来源于stack exchange,提问作者Brandon Yip
相关产品推荐
相关产品推荐

