ehcache模糊批量移除缓存的方法

 更新时间:2018年02月05日 08:56:37   作者:zer0black  
本篇文章主要介绍了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,就全缓存遍历几次。具体实现留给读者自行完成。希望对各位有所帮助。也希望大家多多支持脚本之家。

相关文章

  • 使用maven创建普通项目命令行程序详解

    使用maven创建普通项目命令行程序详解

    大部分使用maven创建的是web项目,这里使用maven创建一个命令行程序,目的是让大家了解maven特点和使用方式,有需要的朋友可以借鉴参考下
    2021-10-10
  • ArrayList源码和多线程安全问题分析

    ArrayList源码和多线程安全问题分析

    这篇文章主要介绍了ArrayList源码和多线程安全问题,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,下面小编和大家一起来学习一下吧
    2019-05-05
  • Springboot如何利用拦截器拦截请求信息收集到日志详解

    Springboot如何利用拦截器拦截请求信息收集到日志详解

    一些系统经常需要关注用户请求的具体信息,如用户信息、请求参数、响应结果等等,在SpringBoot应用中可通过拦截器的方式统一处理,下面这篇文章主要给大家介绍了关于Springboot如何利用拦截器拦截请求信息收集到日志的相关资料,需要的朋友可以参考下
    2021-08-08
  • RHEL6.5下JDK1.8安装教程

    RHEL6.5下JDK1.8安装教程

    这篇文章主要为大家详细介绍了RHEL6.5下JDK1.8安装教程的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-03-03
  • Java反射获取所有Controller和RestController类的方法

    Java反射获取所有Controller和RestController类的方法

    这篇文章给大家分享了Java反射获取所有Controller和RestController类的方法,文中有详细的代码示例讲解,具有一定的参考价值,需要的朋友可以参考下
    2023-08-08
  • SpringCloud组件性能优化的技巧

    SpringCloud组件性能优化的技巧

    这篇文章主要介绍了SpringCloud组件性能优化的技巧,Springcloud 原始的配置,性能是很低的,大家可以使用 Jmeter 测试一下,QPS 不会到 50,要做到高并发,需要做不少的配置优化,需要的朋友可以参考下
    2023-09-09
  • 关于mybatis-plus逻辑删除自动填充更新时间的问题

    关于mybatis-plus逻辑删除自动填充更新时间的问题

    mybatis-plus是对mybatis的增强,mybatis-plus更像是面向对象编程,数据库基本CRUD的操作可以不用手动编写SQL语句,大大提高了开发的效率,这篇文章主要介绍了mybatis-plus逻辑删除自动填充更新时间问题,需要的朋友可以参考下
    2022-07-07
  • SpringBoot在 POM 中引入本地 JAR 包的方法

    SpringBoot在 POM 中引入本地 JAR 包的方法

    在开发 Spring Boot 应用程序时,您可能需要使用本地 JAR 包来添加自定义库或功能,本文将介绍在 Spring Boot 项目的 POM 文件中如何引入本地 JAR 包,感兴趣的朋友跟随小编一起看看吧
    2023-08-08
  • java程序员常见的sql错误

    java程序员常见的sql错误

    当Java程序员在SQL中要写个查询语句是很简单的。但在Java里类似的语句却不容易,因为程序员不仅要反复考虑编程范式,而且也要考虑算法的问题。下面我们来看看这几个常见的错误吧
    2019-06-06

最新评论