使用默认Key时Spring Cacheable不生效问题求助
问题分析与解决方案
我来帮你梳理下这个缓存失效的问题,这其实是Spring Cache默认key生成策略结合POJO字段处理的一个常见坑。
问题根源
Spring Cache默认使用SimpleKeyGenerator生成缓存key:当方法只有一个参数时,直接把这个参数实例作为缓存key。缓存命中的核心要求是两次请求的参数实例hashCode()相等且equals()返回true。
从你的代码和请求信息来看,有两个关键疑点:
equals()和hashCode()包含未在请求中传递的字段:你的DashboardRequest的这两个方法里包含了organizer字段,但请求payload里并没有这个字段。如果在请求处理过程中(比如反序列化后、进入Controller前),有其他逻辑给organizer设置了不同的值(比如每次生成新对象、随机值),那每次的DashboardRequest实例hashCode会不一样,equals()也会返回false,导致缓存key不匹配,无法命中。- 请求中的部分字段未加入
equals()和hashCode():你的请求payload里有state和district字段,但这两个字段并没有出现在equals()和hashCode()中。虽然这不是当前缓存失效的直接原因,但会导致即使这两个字段值不同,也会被认为是相同的请求,可能不符合业务预期。
而你用key="#dashboardRequest.level"时,只基于固定值1生成key,所以不管其他字段如何变化,key始终一致,自然能正常缓存。
解决方案
根据你的场景,推荐以下几种解决方式:
方案一:修正equals()和hashCode()方法
确保这两个方法只包含需要参与缓存key判断的字段,排除那些会被动态修改或不属于请求传递的字段:
- 打开Eclipse,右键
DashboardRequest类 →Source→Generate hashCode() and equals() - 在弹出的对话框中,选中所有请求payload里的字段(
fromDate、toDate、theme、activity、level、levelValue、state、district),取消选中organizer(如果它不是请求传递的字段且会被动态修改) - 点击
Finish生成新的方法代码
方案二:显式指定包含所有必要字段的SpEL key
如果不想修改POJO的方法,可以直接用SpEL指定key包含所有需要的字段,比如:
@Cacheable(value="myDash", key="new org.springframework.cache.interceptor.SimpleKey(#dashboardRequest.fromDate, #dashboardRequest.toDate, #dashboardRequest.theme, #dashboardRequest.activity, #dashboardRequest.level, #dashboardRequest.levelValue, #dashboardRequest.state, #dashboardRequest.district)")
SimpleKey会自动处理字段为null的情况,生成稳定的key。
方案三:自定义KeyGenerator
如果多个方法需要类似的key生成逻辑,可以自定义一个KeyGenerator,指定哪些字段参与key的生成:
@Component public class DashboardKeyGenerator implements KeyGenerator { @Override public Object generate(Object target, Method method, Object... params) { DashboardRequest request = (DashboardRequest) params[0]; return new SimpleKey(request.getFromDate(), request.getToDate(), request.getTheme(), request.getActivity(), request.getLevel(), request.getLevelValue(), request.getState(), request.getDistrict()); } }
然后在@Cacheable中指定:
@Cacheable(value="myDash", keyGenerator="dashboardKeyGenerator")
验证方法
你可以在Controller方法开头加入日志,打印dashboardRequest的hashCode()和organizer字段值:
LOGGER.info("Request hashCode: {}, organizer: {}", dashboardRequest.hashCode(), dashboardRequest.getOrganizer());
如果每次请求的hashCode不同,或者organizer字段值不一致,就验证了我们的推测。
内容的提问来源于stack exchange,提问作者megamind
相关产品推荐
相关产品推荐

