Cached data manual cleanup issue with Spring @Cacheable

Spring Cache itself perfectly supports cached CRUD. You can use annotations to clean up the cache, see: org.springframework.cache.annotation.CacheEvict. I won't go into details here. 

 

What if we want to manually clear the cached data of @Cacheable? Why is there such a low demand, such a perverted demand? There are always strange problems in the running system, and sometimes you may need to manually clear the cache. 

Most of the time, you can't find a system administrator to manually clear the cache. The system administrator always refuses to work for you for various reasons of security. Since we are programmers, we will solve it in a programmatic way. 

 

In two steps:

  1. Record cacheName. cacheName is the value of the @Cacheable annotation. The 
    reason for recording cacheName is for performance reasons. Redis of general product line does not allow the execution of keys command. So I don't know which data is actually cached in redis. 
  2. Provides a cache console page for accessing redis and cleaning the cached data corresponding to the specified cacheName. This page requires login, you can log in with the company's SSO, and have certain permission control.

 

This article only covers the first step. The second step is to write a web page, which is very simple, so I won't mention it.

 

Recording cacheName uses Spring's AOP.

 

/**
 * Aspect to handle Cache
 */
@Aspect
@ Slf4j
class 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) {
        Stream.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;
    }
}

This AOP intercepts all accesses to methods annotated with @Cacheable, records its cacheName, and writes it asynchronously to a hash of redis. It is used for subsequent cache console page queries.

 

 

 

Asynchronous writing uses Spring 5's Reactor.  

 

filter(StringUtils::hasText), used to filter empty cacheName, some developers may carelessly specify the value of @Cacheable as an empty string, here to bypass the empty string to avoid AOP errors.

doOnError(e -> log.warn("failed to saveCacheName: " + e.getMessage())), for handling error events.

subscribe(cacheName -> {...}); This sentence is actually written to redis. 

 

Finally, you also need to register with the bean Factory. 

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

 

 

 

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326102941&siteId=291194637