SpringBoot @Cacheable 缓存不生效的问题
背景
Springboot + CaffeineCache + 使用@Cacheable注解
请求查询一个方法,因为数据变化频率低,查询频率高,于是使用缓存,并使用注解。
但发现用了 @Cacheable 这个注解,发现并没有生效。
代码是这样子的
***ServiceImpl.java public void getUser(UserFo fo){ // 省略 // .... // 在这里希望使用缓存,因为这个方法会被频繁调用,那也会频繁访问数据库,就大可不必,没有太多的必要 User user = getUserInfo(fo.getUsername()); // 省略 // .... } @Cacheable(value = "common", key="#username") public User getUserInfo(String username){ // 省略 // .... } ***CacheConfig.java // 在这个类里面,写了两个 cache 超时机制,都是放在了 CacheManager @Configuration public class CacheConfig { /** * 默认的超时时间 */ @Value("${caching.expire.duration:60}") private int duration; @Value("${caching.expire.longDuration:60}") private int longDuration; @Value("${caching.maximumSize:1000}") private int maximumSize; @Bean public CacheManager cacheManager() { CaffeineCache cache = buildCache("common", duration); CaffeineCache longDurationCache = buildCache("longDurationCache", longDuration); SimpleCacheManager manager = new SimpleCacheManager(); manager.setCaches(Arrays.asList(cache, longDurationCache)); return manager; } private CaffeineCache buildCache(String name, int secondsToExpire) { return new CaffeineCache(name, Caffeine.newBuilder() .expireAfterWrite(secondsToExpire, TimeUnit.SECONDS) .maximumSize(maximumSize) .build()); } }
方法如上,搞了很久,一开始还以为是没有缓存定义的 key 哪里有问题 —— 不是
然后,又以为是在启动类没有加 @EnableCaching —— 不是
后来终于找到了正解:
一个方法A调同一个类里的另一个有缓存注解的方法B,这样是不走缓存的。
原因: 使用@Cacheable添加缓存实际上就是使用动态代理做的,在代理的方法前后做缓存的相应处理。这样一来,单独的去调方法B是有缓存的,但是如果调方法A,A里面再去调B方法,哪怕B方法配置了缓存,也是不会生效的。
解决方法:
- a、不使用注解的方式,直接取 Ehcache 的 CacheManger 对象,把需要缓存的数据放到里面,类似于使用 Map,缓存的逻辑自己控制
- b、把方法A和方法B放到两个不同的类里面,例如:如果两个方法都在service接口里,把方法B放到另一个service里面,这样A方法里调B方法,就可以使用B方法的缓存
一开始就是按方法一去写的,是已经可用了的,但感觉使用注解会更加方便,于是乎就改成使用注解,想不到使用注解,注出了问题,缓存根本不生效。于是各种找原因,也好在终于是找到了。
再附上,直接使用 CacheManager 对象,通过 autowired 注入方式的方案:
@Autowired CacheManager cacheManager; // @Cacheable(value = "common", key="#username") // 因为这个方法是被同个类的另外一个方法调用的,所以在这个方法上加注解是无效的。emmmm,这种情况,只能使用CacheManager 这种方案 public UserVO getUserInfo(String username) { // 这是在使用注解时做的调试,每次都进来这里,说明缓存没有生效。 // if (null != cacheManager.getCache("common").get(username)) { // Object common = cacheManager.getCache("common").get("login-user-info-" + username).get(); // log.warn("cache !!!============ {}", common); // } else { // log.warn("no cache !!!============"); // } // // ObjectResponse<UserVO> response = rbacClient.findByLdapId(username); // return Optional.ofNullable(response) // .map(resp -> resp.getData()) // .orElse(null); String cacheKey = "login-user-info:" + username; Cache cache = cacheManager.getCache("common"); UserVO user = (UserVO) Optional.ofNullable(cache) .map(c -> c.get(cacheKey)) .map(wapper -> { Object o = wapper.get(); log.debug("fetch user from cache: username: {}, userInfo: {}", username, o); return o; }).orElseGet(() -> { ObjectResponse<UserVO> response = rbacClient.findByLdapId(username); return Optional.ofNullable(response) .map(resp -> resp.getData()) .map(data -> { cache.put(cacheKey, data); return data; }).orElse(null); }); return user; }
坑了自己一把,记录一下。。。。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南