众所周知,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 来作注解的具体实现。工具
注解的目的是批量模糊移除缓存。需考虑以下两个问题:优化
我给出的处理方式,也是我认为最简单的处理方式是:code
CacheRemove
中的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()
来实现。在具体实现中须要作如下几件事:
CacheRemove
具体实现以下:
@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,就全缓存遍历几回。具体实现留给读者自行完成。但愿对各位有所帮助。