Spring Cache调用getCache返回null但缓存实例已存数据问题
问题根因
三个错误叠加导致返回null:
- 缓存名称拼写完全不统一:
- CacheManager初始化时指定的缓存名是
optCache(字母顺序o-p-t) - 原Service层@Cacheable注解指定的缓存名是
otpCache(字母顺序o-t-p,和OTP一次性密码的业务语义匹配) - Controller层读取时写的缓存名是
"cache",和前两个完全不匹配
三个位置的缓存名没有一处对齐,自然调用getCache()拿不到对应实例。
- CacheManager初始化时指定的缓存名是
- Spring AOP代理内部调用失效:@Cacheable是基于Spring AOP动态代理实现的,类内部方法直接调用
this.buildOtpCache()时,不会走代理逻辑,注解完全不生效,根本不会往缓存里写数据。调试时看到cacheManager里的残留数据,是之前其他测试路径写入的,不是这段内部调用生成的。 - Controller层执行顺序错误:原代码里先执行
cacheManager.getCache()读缓存,之后才调用otpService.verify()触发缓存写入,读操作在写操作之前,就算逻辑正确也拿不到值。
修复方案
- 统一所有位置的缓存名,按业务语义统一为
otpCache,修正配置类:
@EnableCaching public class CachingConfig { @Bean public CacheManager cacheManager() { return new ConcurrentMapCacheManager("otpCache"); } }
- 解决内部调用AOP失效问题,二选一即可:
- 方案一:保留@Cacheable注解,注入自身代理对象调用,避免this直连跳过AOP:
@Service public class OtpService { @Autowired private OtpService self; public void verify(Request request, String authorization) { String memberId = parseAuthToken(authorization).getMembershipID(); // 通过代理对象调用才会触发@Cacheable写入逻辑 self.buildOtpCache(memberId, request.getTokenUUID(), 0); } @Cacheable("otpCache") public OTPCache buildOtpCache(String memberId, String uuid, int counter) { return OTPCache.builder() .memberId(memberId) .tokenUUID(uuid) .timestamp(LocalDateTime.now()) .counter(counter) .build(); } }- 方案二:用你调整后的手动写入逻辑,注意统一缓存名即可:
public void verify(Request request, String authorization) { String memberId = parseAuthToken(authorization).getMembershipID(); cacheManager.getCache("otpCache").put(memberId, buildOtpCache(memberId, request.getTokenUUID(), 0)); } public OTPCache buildOtpCache(String memberId, String uuid, int counter) { return OTPCache.builder() .memberId(memberId) .tokenUUID(uuid) .timestamp(LocalDateTime.now()) .counter(counter) .build(); } - 修正Controller层执行顺序和缓存名,先触发写入再读取:
@Override public void verifyOTP(MeOTPVerifyRequest verifyOTPRequest, String authorization) { String memberId = parseAuthToken(authorization).getMembershipID(); // 先执行业务逻辑写入缓存 otpService.verify(verifyOTPRequest, authorization); // 用统一的缓存名读取 OTPCache otpCache = cacheManager.getCache("otpCache").get(memberId, OTPCache.class); // 后续业务逻辑 }
补充说明:ConcurrentMapCacheManager默认支持懒加载缓存,即使不在构造参数里提前声明缓存名,第一次调用对应名字的@Cacheable方法或者getCache()时也会自动创建缓存,但前提是所有位置的缓存名字符串必须完全一致,大小写、字母顺序错一个字符都无法匹配。
内容的提问来源于stack exchange,提问作者Trung Nguyen
相关产品推荐
相关产品推荐

