ehcache模糊批量移除緩存的方法
前言
眾所周知,encache是現(xiàn)在最流行的java開源緩存框架,配置簡(jiǎn)單,結(jié)構(gòu)清晰,功能強(qiáng)大。通過注解 @Cacheable 可以快速添加方法結(jié)果到緩存。通過 @CacheEvict 可以快速清除掉指定的緩存。
但由于 @CacheEvict 注解使用的是key-value的,不支持模糊刪除,就會(huì)遇到問題。當(dāng)我用 @Cacheable 配合Spring EL表達(dá)式添加了同一方法的多個(gè)緩存比如:
@GetMapping("/listOfTask/{page}/") @Cacheable(value = "BusinessCache", key = "'listOfTask_'+ #page") public ResponseMessage<PageTaskVO> getTaskList(@PathVariable("page") String page) { do something... }
上述代碼是分頁獲取任務(wù)信息。用EL表達(dá)式獲取到參數(shù)中的page,并作為緩存的key,使用 @Cacheable 添加到ehcache的緩存中。此時(shí),在緩存中就會(huì)出現(xiàn) listOfTask_1 , listOfTask_2 , listOfTask_3 這種類型的key。
當(dāng)添加、刪除任務(wù)時(shí),列表就會(huì)發(fā)生改變。這時(shí)候,就需要把 listOfTask_* 相關(guān)的緩存全部去掉。而這時(shí),我不知道緩存中到底緩存了多少和 listOfTask_* 相關(guān)的內(nèi)容,不可能調(diào)用 @CacheEvict 挨個(gè)刪除。
既然ehcache本身無法支持,那就只能靠我們自己實(shí)現(xiàn)了。
實(shí)現(xiàn)
考慮到使用的注解添加的緩存,那么移除緩存也使用注解處理,可以保持開發(fā)的一致性。注解對(duì)開發(fā)者來說也很友好。那么我們就考慮使用自定義注解來來模糊批量移除緩存。
首先,定義注解 CacheRemove :
@Target({ java.lang.annotation.ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) public @interface CacheRemove { String value(); String[] key(); }
其中,value 同 ehcache 一樣,用于定義要操作的緩存名。key 是一個(gè)數(shù)組,用于存放多種緩存 key 的正則表達(dá)式。起名 CacheRemove 清晰易懂,也不與 ehcache 本身的注解沖突。注解的定義到此為止。接下來,就需要處理注解了,由于使用的 spring 框架,很自然的,就會(huì)想到用 AOP 來做注解的具體實(shí)現(xiàn)。
注解的目的是批量模糊移除緩存。需考慮如下兩個(gè)問題:
- 用什么方式模糊匹配
- 怎么批量刪除key
我給出的處理方式,也是我認(rèn)為最簡(jiǎn)單的處理方式是:
- 用什么方式模糊匹配 —— CacheRemove 中的key傳正則,可以傳多個(gè),使用正則匹配
- 怎么批量刪除key —— 循環(huán)所有的key,找到匹配正則的就刪除
首先定義類名 CacheRemoveAspect :
@Aspect @Component public class CacheRemoveAspect { @Pointcut(value = "(execution(* *.*(..)) && @annotation(com.example.CacheRemove))") private void pointcut() {} do something... }
在切面中定義切點(diǎn),使用 execution(* *.*(..) && @annotation(com.example.CacheRemove)) 表示所有帶注解類 CacheRemove 都執(zhí)行, @annotation 中的值是注解的全限定名。
切點(diǎn)定義完畢,下面的重頭戲就是切面的具體實(shí)現(xiàn)了。一般來說,緩存會(huì)在增刪改的方法執(zhí)行完后才要移除。所以使用 @AfterReturning() 來實(shí)現(xiàn)。在具體實(shí)現(xiàn)中需要做以下幾件事:
- 攔截方法上的注解
- 判斷注解是不是 CacheRemove
- 由于注解傳入的 key 是個(gè)數(shù)組,循環(huán)處理每個(gè)key
- 在循環(huán)中編制每個(gè) key 為 pattern, 并循環(huán)所有的緩存,移除匹配上的緩存
具體實(shí)現(xiàn)如下:
@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 模糊批量移除緩存的具體實(shí)現(xiàn)。其中 BusinessCacheUtils 為自己封裝的 ehcache 工具類。主要實(shí)現(xiàn)獲取緩存池,獲取緩存,移除緩存,添加緩存,查看所有緩存等正常功能。代碼如下:
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(); } /** * 獲得一個(gè)Cache,沒有則創(chuàng)建一個(gè)。 * @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; } }
至此,整個(gè)ehcache 模糊批量移除緩存的功能就實(shí)現(xiàn)了。
總結(jié)
整個(gè)過程思路簡(jiǎn)單,用到了一些 AOP 的知識(shí)就完成了需要的功能。但具體的移除部分代碼可考慮進(jìn)行優(yōu)化。通過一次緩存的全部循環(huán),就把需要移除的緩存都移除干凈,而不是想現(xiàn)在這樣有幾個(gè)key,就全緩存遍歷幾次。具體實(shí)現(xiàn)留給讀者自行完成。希望對(duì)各位有所幫助。也希望大家多多支持腳本之家。
相關(guān)文章
深入淺析java web log4j 配置及在web項(xiàng)目中配置Log4j的技巧
這篇文章主要介紹了2015-11-11使用maven創(chuàng)建普通項(xiàng)目命令行程序詳解
大部分使用maven創(chuàng)建的是web項(xiàng)目,這里使用maven創(chuàng)建一個(gè)命令行程序,目的是讓大家了解maven特點(diǎn)和使用方式,有需要的朋友可以借鑒參考下2021-10-10Springboot如何利用攔截器攔截請(qǐng)求信息收集到日志詳解
一些系統(tǒng)經(jīng)常需要關(guān)注用戶請(qǐng)求的具體信息,如用戶信息、請(qǐng)求參數(shù)、響應(yīng)結(jié)果等等,在SpringBoot應(yīng)用中可通過攔截器的方式統(tǒng)一處理,下面這篇文章主要給大家介紹了關(guān)于Springboot如何利用攔截器攔截請(qǐng)求信息收集到日志的相關(guān)資料,需要的朋友可以參考下2021-08-08Java反射獲取所有Controller和RestController類的方法
這篇文章給大家分享了Java反射獲取所有Controller和RestController類的方法,文中有詳細(xì)的代碼示例講解,具有一定的參考價(jià)值,需要的朋友可以參考下2023-08-08關(guān)于mybatis-plus邏輯刪除自動(dòng)填充更新時(shí)間的問題
mybatis-plus是對(duì)mybatis的增強(qiáng),mybatis-plus更像是面向?qū)ο缶幊?,?shù)據(jù)庫基本CRUD的操作可以不用手動(dòng)編寫SQL語句,大大提高了開發(fā)的效率,這篇文章主要介紹了mybatis-plus邏輯刪除自動(dòng)填充更新時(shí)間問題,需要的朋友可以參考下2022-07-07SpringBoot在 POM 中引入本地 JAR 包的方法
在開發(fā) Spring Boot 應(yīng)用程序時(shí),您可能需要使用本地 JAR 包來添加自定義庫或功能,本文將介紹在 Spring Boot 項(xiàng)目的 POM 文件中如何引入本地 JAR 包,感興趣的朋友跟隨小編一起看看吧2023-08-08