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;
    }

坑了自己一把,记录一下。。。。

posted @ 2023-01-06 19:58  aaacarrot  阅读(5567)  评论(0编辑  收藏  举报