如何在@CacheEvict 中调用监听器或拦截器

How to invoke a listener or interceptor in @CacheEvict

我需要在调用@CacheEvict 时调用一些功能。有没有一种方法可以调用要在 Spring @CacheEvict 中调用的侦听器或拦截器?

通常,这是非常 "cache provider" 具体的,因为没有 2 个缓存提供程序具有相同的功能。

例如,我主要使用内存数据网格 (IMDG) 技术,例如 Pivotal GemFire and the OSS version Apache Geode. Both, of which, can be used as a "caching provider" in Spring's Cache Abstraction. With GemFire/Geode, you can register a o.a.g.cache.CacheListener callback on the GemFire/Geode Region (essentially a java.util.Map) that is backing the Spring Cache interface, and used in Spring's Caching infrastructure as the "Adapter" to the backing store. As you can see with SD GemFire/Geode provider implementation, the "eviction" triggers a GemFire/Geode Region.remove(key). This eviction can subsequently be captures and handled in the Region's registered CacheListener.afterDestroy(:EntryEvent) 回调方法。

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

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

尽管如此,我会说您不应该按照 @Borino 的指示开发基于底层 "caching provider" 的 AOP 切入点表达式,即 ...

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

此表达式将您的 AOP 切面与 ConcurrentMapCache "provider" 联系起来,Spring 的缓存抽象 中的默认值(以及 Spring开机).

当您在应用程序中使用 Ehcache、Hazelcast、Redis、GemFire/Geode 或这些 "providers" 的多种组合时会发生什么情况?

相反,您可以将 AOP 切入点表达式稍微调整为这样...

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

有关详细信息,请参阅 here. This is safe because all "caching providers" must supply 2 things: a CacheManager implementation and a Cache implementation for each cache specified in the application. Again, the Cache interface is a "Adapter" to the backing store. Again, see the docs

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

希望对您有所帮助。

干杯! -约翰

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

出于这个原因,您必须引入额外的 AspectJ 依赖项并进行某种编译或 post-compile 编织以针对 Cache.evict 方法。

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

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);
  }