ehcache模糊批量移除緩存

目錄

前言

衆所周知,encache是如今最流行的java開源緩存框架,配置簡單,結構清晰,功能強大。經過註解@Cacheable能夠快速添加方法結果到緩存。經過@CacheEvict能夠快速清除掉指定的緩存。java

但因爲@CacheEvict註解使用的是key-value的,不支持模糊刪除,就會遇到問題。當我用@Cacheable配合Spring EL表達式添加了同一方法的多個緩存好比:正則表達式

@GetMapping("/listOfTask/{page}/")
@Cacheable(value = "BusinessCache", key = "'listOfTask_'+ #page")
public ResponseMessage<PageTaskVO> getTaskList(@PathVariable("page") String page) {
    do something...
}

上述代碼是分頁獲取任務信息。用EL表達式獲取到參數中的page,並做爲緩存的key,使用@Cacheable添加到ehcache的緩存中。此時,在緩存中就會出現listOfTask_1, listOfTask_2, listOfTask_3這種類型的key。spring

當添加、刪除任務時,列表就會發生改變。這時候,就須要把listOfTask_*相關的緩存所有去掉。而這時,我不知道緩存中到底緩存了多少和listOfTask_*相關的內容,不可能調用@CacheEvict挨個刪除。數組

既然ehcache自己沒法支持,那就只能靠咱們本身實現了。緩存

實現

考慮到使用的註解添加的緩存,那麼移除緩存也使用註解處理,能夠保持開發的一致性。註解對開發者來講也很友好。那麼咱們就考慮使用自定義註解來來模糊批量移除緩存。app

首先,定義註解CacheRemove框架

@Target({ java.lang.annotation.ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface CacheRemove {
    String value();
    String[] key();
}

其中,value 同 ehcache 同樣,用於定義要操做的緩存名。key 是一個數組,用於存放多種緩存 key 的正則表達式。起名 CacheRemove 清晰易懂,也不與 ehcache 自己的註解衝突。註解的定義到此爲止。接下來,就須要處理註解了,因爲使用的 spring 框架,很天然的,就會想到用 AOP 來作註解的具體實現。工具

註解的目的是批量模糊移除緩存。需考慮以下兩個問題:優化

  1. 用什麼方式模糊匹配
  2. 怎麼批量刪除key

我給出的處理方式,也是我認爲最簡單的處理方式是:code

  1. 用什麼方式模糊匹配 —— CacheRemove中的key傳正則,能夠傳多個,使用正則匹配
  2. 怎麼批量刪除key —— 循環全部的key,找到匹配正則的就刪除

首先定義類名CacheRemoveAspect

@Aspect
@Component
public class CacheRemoveAspect {
    @Pointcut(value = "(execution(* *.*(..)) && @annotation(com.example.CacheRemove))")
    private void pointcut() {}

    do something...
}

在切面中定義切點,使用execution(* *.*(..) && @annotation(com.example.CacheRemove))表示全部帶註解類CacheRemove都執行,@annotation中的值是註解的全限定名。

切點定義完畢,下面的重頭戲就是切面的具體實現了。通常來講,緩存會在增刪改的方法執行完後纔要移除。因此使用@AfterReturning()來實現。在具體實現中須要作如下幾件事:

  1. 攔截方法上的註解
  2. 判斷註解是否是CacheRemove
  3. 因爲註解傳入的 key 是個數組,循環處理每一個key
  4. 在循環中編制每一個 key 爲 pattern, 並循環全部的緩存,移除匹配上的緩存

具體實現以下:

@AfterReturning(value = "pointcut()")
private void process(JoinPoint joinPoint){
    MethodSignature signature = (MethodSignature) joinPoint.getSignature();
    Method method = signature.getMethod();
    CacheRemove cacheRemove = method.getAnnotation(CacheRemove.class);

    if (cacheRemove != null){
        String value = cacheRemove.value();
        String[] keys = cacheRemove.key(); //須要移除的正則key

        List cacheKeys = CacheUtils.cacheKeys(value);
        for (String key : keys){
            Pattern pattern = Pattern.compile(key);
            for (Object cacheKey: cacheKeys) {
                String cacheKeyStr = String.valueOf(cacheKey);
                if (pattern.matcher(cacheKeyStr).find()){
                    CacheUtils.remove(value, cacheKeyStr);
                }
            }
        }
    }
}

以上,爲 ehcache 模糊批量移除緩存的具體實現。其中 BusinessCacheUtils 爲本身封裝的 ehcache 工具類。主要實現獲取緩存池,獲取緩存,移除緩存,添加緩存,查看全部緩存等正常功能。代碼以下:

public class CacheUtils {

    private static CacheManager cacheManager = SpringContextHolder.getBean("ehCacheManagerFactory");

    public static Object get(String cacheName, String key) {
        Element element = getCache(cacheName).get(key);
        return element == null ? null : element.getObjectValue();
    }

    public static void put(String cacheName, String key, Object value) {
        Element element = new Element(key, value);
        getCache(cacheName).put(element);
    }

    public static void remove(String cacheName, String key) {
        getCache(cacheName).remove(key);
    }

    public static List cacheKeys(String cacheName){
        return getCache(cacheName).getKeys();
    }

   /**
     * 得到一個Cache,沒有則建立一個。
     * @param cacheName
     * @return
     */
    private static Cache getCache(String cacheName) {
        Cache cache = cacheManager.getCache(cacheName);
        if (cache == null) {
            cacheManager.addCache(cacheName);
            cache = cacheManager.getCache(cacheName);
            cache.getCacheConfiguration().setEternal(true);
        }
        return cache;
    }

    public static CacheManager getCacheManager() {
        return cacheManager;
    }

}

至此,整個ehcache 模糊批量移除緩存的功能就實現了。

總結

整個過程思路簡單,用到了一些 AOP 的知識就完成了須要的功能。但具體的移除部分代碼可考慮進行優化。經過一次緩存的所有循環,就把須要移除的緩存都移除乾淨,而不是想如今這樣有幾個key,就全緩存遍歷幾回。具體實現留給讀者自行完成。但願對各位有所幫助。

相關文章
相關標籤/搜索