Spring Boot 2.5.12 内存缓存@Cacheable使用与问题排查
Spring Boot 内存缓存配置问题解决方案(基于2.5.12版本)
问题1:使用account ID作为缓存key的配置方式
@Cacheable的key属性支持SpEL表达式直接读取参数属性,配置方式如下:
@Cacheable(cacheNames = "products", key = "#account.id") public Product getProduct(AccountDetails account){ return configProduct.getProduct(account.getId()); }
其中#account对应方法入参的account对象,.id即读取该对象的id字段作为缓存key。
问题2:配置cacheNames、key属性报错的原因
出现该问题100%是注解导错包:
- Spring原生缓存注解的全限定名为
org.springframework.cache.annotation.Cacheable,该注解中value和cacheNames是互为别名的属性,二者效果完全一致,同时原生支持key属性配置。 - 如果导入了其他框架、自定义封装的同名
@Cacheable注解,就会出现属性不匹配、仅能识别value属性的问题,把导包替换为Spring原生注解即可解决。
问题3:缓存刷新实现方案(支持同方法实现)
缓存刷新的本质是先删除旧缓存,再执行方法拉取新值写入缓存,有两种常用实现:
方案1:独立方法实现(逻辑更清晰)
用@CacheEvict完成缓存剔除,再封装刷新逻辑调用查询方法即可自动写入新缓存:
// 剔除指定key的缓存,方法内不需要写业务逻辑 @CacheEvict(cacheNames = "products", key = "#account.id") public void clearProductCache(AccountDetails account) {} // 刷新缓存 public Product refreshProduct(AccountDetails account) { clearProductCache(account); // 清除缓存后调用查询方法,会直接执行外部Jar调用逻辑,将新结果写入缓存 return getProduct(account); }
方案2:同一方法内实现刷新
给方法增加一个布尔类型的刷新控制参数,通过condition属性搭配@Cacheable和@CachePut注解实现:
// refresh为true时走@CachePut逻辑:直接执行方法,将结果更新到缓存 // refresh为false时走@Cacheable逻辑:优先查缓存,缓存不存在再执行方法 @CachePut(cacheNames = "products", key = "#account.id", condition = "#refresh") @Cacheable(cacheNames = "products", key = "#account.id", condition = "!#refresh") public Product getProduct(AccountDetails account, boolean refresh){ return configProduct.getProduct(account.getId()); }
调用时传入refresh=true即可强制刷新缓存,传入false则走正常缓存逻辑。
缓存不生效排查&缓存值验证方法
常见不生效原因
- 启动类未加
@EnableCaching注解,缓存注解默认不会生效 - 存在类内自调用:
@Cacheable基于Spring AOP代理实现,同一个类中直接调用方法不会走代理逻辑,必须通过Spring注入的Bean实例调用方法才能触发缓存 - key匹配问题:未自定义key时,默认的SimpleKeyGenerator会将所有入参拼接作为key,如果AccountDetails类未实现equals方法,可能导致key匹配失败
验证缓存是否写入
Spring Boot默认内存缓存底层为ConcurrentMapCache,可直接注入CacheManager查看缓存内容:
@Autowired private CacheManager cacheManager; public void checkCache(AccountDetails account) { Cache productCache = cacheManager.getCache("products"); // 获取指定key的缓存值 Product cachedValue = productCache.get(account.getId(), Product.class); if (cachedValue != null) { System.out.println("缓存命中,值为:" + cachedValue); } else { System.out.println("对应key无缓存"); } }
最简验证方式:在getProduct方法内部加一行打印日志或打端点,连续两次调用方法,如果第二次调用没有进入方法内部、没有触发日志/断点,说明缓存正常生效;如果每次都进入方法,按照上述不生效原因逐一排查即可。
内容的提问来源于stack exchange,提问作者Sajal Saxena
相关产品推荐
相关产品推荐

