【问题标题】:How to invoke a listener or interceptor in @CacheEvict如何在@CacheEvict 中调用监听器或拦截器
【发布时间】:2018-05-27 17:16:18
【问题描述】:

我需要在调用 @CacheEvict 时调用某些功能。有没有办法在 Spring @CacheEvict 中调用要调用的侦听器或拦截器?

【问题讨论】:

  • 如果我们查看@CacheEvict 的文档:注释表明一个方法(或类上的所有方法)会触发org.springframework.cache.Cache.evict(Object) 缓存驱逐操作。您可以创建 Aspects 来拦截此调用,但这取决于您使用的缓存框架(例如 GuavaCache、Ehcache 或简单的 Spring)
  • 我使用的是简单的 Spring 缓存。您能否给我一些指导,如何为我的以下方法创建一个方面 @CacheEvict(cacheNames="order", allEntries=true) public void getOrders(){}
  • 这很简单,您可以找到大量示例 (mkyong.com/spring3/spring-aop-aspectj-annotation-example)。对于您的情况"execution(* org.springframework.cache.ConcurrentMapCache.evict(..))",请为您选择最佳点,例如之前或之后。我希望应该可以工作,没有源代码我无法更好地解释。
  • 更新类包:` "execution(* org.springframework.cache.concurrent.ConcurrentMapCache.evict(..))" `
  • 非常感谢@borino。

标签: spring-mvc spring-boot spring-data spring-cache


【解决方案1】:

通常,这是非常“缓存提供程序”特定的,因为没有 2 个缓存提供程序具有相同的功能。

例如,我主要使用内存数据网格 (IMDG) 技术,例如 Pivotal GemFire 和 OSS 版本 Apache Geode。两者都可以是used as a "caching provider" in Spring's Cache Abstraction。使用 GemFire/Geode,您可以在 GemFire/Geode Region(本质上是 java.util.Map)上注册一个 o.a.g.cache.CacheListener 回调,即 backing Spring Cache 接口,并用于Spring 的缓存基础设施作为后备存储的“适配器”。正如您在 SD GemFire/Geode 提供程序实现中看到的那样,"eviction" triggers 是 GemFire/Geode Region.remove(key)。随后可以在Region's 注册的CacheListener.afterDestroy(:EntryEvent) 回调方法中捕获和处理此驱逐。

但是,这只是在应用程序中处理驱逐通知的一种方法。

当然,正如 @Borino 所指出的,您可以利用 Spring 的 AOP 支持来“拦截”缓存驱逐操作。这种方法的优点是它更通用且可跨不同的缓存提供程序重用。

虽然,我会说您不应该按照 @Borino 的指示开发基于底层“缓存提供程序”的 AOP 切入点表达式,即 ...

execution(* org.springframework.cache.concurrent.ConcurrentMapCache.evic‌​t(..))

此表达式将您的 AOP 特性与 ConcurrentMapCache“提供者”联系起来,这是 Spring 的缓存抽象(以及 Spring Boot 中的默认值)。

当您在应用程序中使用 Ehcache、Hazelcast、Redis、GemFire/Geode 或这些“提供程序”的多种组合时会发生什么?

相反,您可以将 AOP Pointcut 表达式稍微调整为这个...

execution(* org.springframework.cache.Cache.evic‌​t(..))

here。这是安全的,因为所有“缓存提供者”都必须提供两件事:一个CacheManager 实现和一个Cache 实现,用于应用程序中指定的每个缓存。同样,Cache 接口是后备存储的“适配器”。同样,see the docs 了解更多详情。

这两种方法都需要权衡取舍。提供商特定的解决方案通常会为您提供更多的控制能力,但使用 AOP 方法的可重用性更高。做适合您的 UC 的事情。

希望这会有所帮助。

干杯! -约翰

【讨论】:

  • 谢谢约翰。这正是我正在寻找的。​​span>
【解决方案2】:

John 的答案是正确的,但重要的是要知道 Cache 类不是 Spring 托管的 bean,CacheManager 是。

因此,您必须引入额外的 AspectJ 依赖项并进行某种编译或编译后编织以定位 Cache.evict 方法。

【讨论】:

    【解决方案3】:

    我尝试将缓存管理器注入到一个方面,并在需要缓存驱逐的方法上设置我的切入点,如下所示:

    package com.example.aspectdemo.aop;
    
    
    import com.example.aspectdemo.domain.Customer;
    import com.example.aspectdemo.service.CustomerService;
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.*;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.cache.CacheManager;
    import org.springframework.stereotype.Component;
    
    @Component
    @Aspect
    public class CacheEvictHandler {
    
        public static Logger logger = LoggerFactory.getLogger(CacheEvictHandler.class);
        private final CacheManager cacheManager;
        private final CustomerService customerService;
    
        public CacheEvictHandler(CacheManager cacheManager, CustomerService customerService) {
            this.cacheManager = cacheManager;
            this.customerService = customerService;
        }
    
        @Pointcut(value = "execution(* com.example.aspectdemo.service.impl.CustomerServiceImpl.save(..))")
        public void loggingPointCut() {
    
        }
    
        @AfterReturning(value = "loggingPointCut()", returning = "customer")
        public void LoggAfterEviction(JoinPoint joinPoint, Customer customer) throws Throwable {
            cacheManager.getCache("customer-cache").clear();// remove cache
            logger.info("*** saved customer id : {}", customer.getId());// do what you like here, i added some logs
            logger.info("*** after eviction : {}", customerService.findAll());
            logger.info("*** cache evicted ***");
        }
    }
    

    这里是我保存的地方:

    @Transactional
      @Override
      public Customer save(Customer customer) {
        log.debug("Request to save Customer : {}", customer);
        return customerRepository.save(customer);
      }
    

    【讨论】:

      猜你喜欢
      • 2017-01-22
      • 2021-12-14
      • 1970-01-01
      • 2019-04-23
      • 2023-03-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-14
      相关资源
      最近更新 更多