Spring @Cacheable的缓存数据手动清理问题_Java_tiancen2001的专栏-CSDN博客

Spring Cache 本身完美支持缓存的CRUD. 可以通过注解来实现缓存的清理, 详见: org.springframework.cache.annotation.CacheEvict. 此处不赘述. 

假如, 我们希望手动清理@Cacheable的缓存数据呢? 为什么有这样low的需求, 这么变态的需求? 运行中的系统总有千奇百怪的问题, 总有时候你可能需要手动清理缓存. 

而手动清理缓存多半不能找系统管理员, 系统管理员总有各种出于安全的理由拒绝为你效劳. 既然我们是程序员, 那就用程序的方式解决. 

分两步:

  1. 记录cacheName. cacheName就是@Cacheable注解的value. 
    之所以要记录cacheName, 是出于性能考虑. 一般产品线的Redis是不允许执行keys命令的. 所以不知道哪些数据被真正被缓存在了redis中. 
  2. 提供一个cache console页面, 用于访问redis并清理指定的cacheName对应的缓存数据. 这个页面是需要登录, 可以用公司的SSO登录, 并有一定的权限控制.

本文只涉及第一步. 第二步就是写一个web页面, 很简单, 所以就不提了.

记录cacheName要用到Spring的AOP.

/** * Aspect to handle Cache */@Aspect@Slf4jclass CacheAspect implements Ordered {     @Pointcut("@annotation(org.springframework.cache.annotation.Cacheable)")    public void cacheable() {    }     @Before("cacheable()")    public void handleCacheable(JoinPoint joinPoint) throws Throwable {        Method method = ObjectUtil.getAopTargetMethod(joinPoint);        saveCacheName(method.getDeclaredAnnotation(Cacheable.class).cacheNames());    }     static void saveCacheName(String[] cacheNames) {        Flux.fromArray(cacheNames)                .filter(StringUtils::hasText)                .doOnError(e -> log.warn("failed to saveCacheName: " + e.getMessage()))                .subscribe(cacheName -> {                    HashOperations<String, String, String> opsForHash = SpringContextHelper.started().getBean(StringRedisTemplate.class).opsForHash();                    opsForHash.putIfAbsent("__cacheName:" + value(SpringContextHelper.started().getApplicationName(), "unknown"), cacheName, "{}");                });    }     @Override    public int getOrder() {        return -999;    }}

这个AOP拦截所有对@Cacheable注解了的方法的访问, 记录其cacheName, 异步写入redis的一个hash中. 用于后续cache console页面的查询.

异步写入用到了Spring 5的Reactor.  

filter(StringUtils::hasText), 用于过滤空的cacheName, 有些开发人员可能粗心把@Cacheable的value指定为了一个空字符串, 此处要绕过空串避免AOP出错.

doOnError(e -> log.warn("failed to saveCacheName: " + e.getMessage())), 用于错误事件的处理.

subscribe(cacheName -> {...}); 这一句是实际写入redis. 

最后, 还需要注册到bean Factory. 

@Bean(name = "cacheAspect")public CacheAspect cacheAspect() {    return new CacheAspect();}

Original url: Access
Created at: 2020-05-09 15:43:04
Category: default
Tags: none

请先后发表评论
  • 最新评论
  • 总共0条评论