zoukankan      html  css  js  c++  java
  • ehcache模糊批量移除缓存

    目录

    前言

    众所周知,encache是现在最流行的java开源缓存框架,配置简单,结构清晰,功能强大。通过注解@Cacheable可以快速添加方法结果到缓存。通过@CacheEvict可以快速清除掉指定的缓存。

    但由于@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。

    当添加、删除任务时,列表就会发生改变。这时候,就需要把listOfTask_*相关的缓存全部去掉。而这时,我不知道缓存中到底缓存了多少和listOfTask_*相关的内容,不可能调用@CacheEvict挨个删除。

    既然ehcache本身无法支持,那就只能靠我们自己实现了。

    实现

    考虑到使用的注解添加的缓存,那么移除缓存也使用注解处理,可以保持开发的一致性。注解对开发者来说也很友好。那么我们就考虑使用自定义注解来来模糊批量移除缓存。

    首先,定义注解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

    我给出的处理方式,也是我认为最简单的处理方式是:

    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,就全缓存遍历几次。具体实现留给读者自行完成。希望对各位有所帮助。

  • 相关阅读:
    《剑指offer》第六十八题(树中两个结点的最低公共祖先)
    《剑指offer》第六十七题(把字符串转换成整数)
    《剑指offer》第六十六题(构建乘积数组)
    《剑指offer》第六十五题(不用加减乘除做加法)
    ECShop 2.7.2版本,数据库表
    织梦在导航栏下拉菜单中调用当前栏目子类的方法
    让dedecms autoindex,itemindex 从0到1开始的办法!
    dedeCMS列表页中如何给前几条文章加单独样式?
    dedecms标签调用大全
    完美解决ecshop与jquery冲突兼容
  • 原文地址:https://www.cnblogs.com/zer0Black/p/8410984.html
Copyright © 2011-2022 走看看