我需要在调用@CacheEvict时调用某些功能。有没有办法调用要在春季@CacheEvict中调用的侦听器或拦截器?
通常,这是非常特定于"缓存提供程序"的,因为没有 2 个缓存提供程序具有相同的功能。
例如,我主要使用内存数据网格(IMDG)技术,如Pivotal GemFire和OSS版本Apache Geode。 两者都可以用作Spring 缓存抽象中的"缓存提供程序"。 使用 GemFire/Geode,您可以在支持 SpringCache
接口的 GemFire/GeodeRegion
(本质上是一个java.util.Map
)上注册o.a.g.cache.CacheListener
回调,并在Spring的缓存基础架构中用作后备存储的"适配器"。 正如您在SD GemFire/Geode提供程序实现中看到的那样,"逐出"触发了GemFire/GeodeRegion.remove(key)
。 随后可以在Region's
注册的 CacheListener.afterDestroy(:EntryEvent) 回调方法中捕获和处理此逐出。
但是,这只是处理应用程序中逐出通知的一种方法。
当然,正如@Borino指出的那样,您可以利用Spring 的AOP 支持来"拦截"缓存逐出操作。 此方法的优点是它更通用,并且可以在不同的缓存提供程序之间重用。
虽然,我想说你不应该像@Borino指示的那样,基于底层的"缓存提供程序"开发AOP Pointcut表达式,即......
execution(* org.springframework.cache.concurrent.ConcurrentMapCache.evict(..))
此表达式将您的 AOP 方面绑定到ConcurrentMapCache
"提供程序",这是 Spring缓存抽象(和Spring 引导)中的默认值。
当您在应用程序中使用 Ehcache、Hazelcast、Redis、GemFire/Geode 或这些"提供程序"的多种组合时会发生什么?
相反,您可以稍微调整 AOP 切入点表达式...
execution(* org.springframework.cache.Cache.evict(..))
看这里。 这是安全的,因为所有"缓存提供程序"都必须提供 2 件事:CacheManager
实现和应用程序中指定的每个缓存的Cache
实现。 同样,Cache
接口是后备存储的"适配器"。 同样,请参阅文档以获取更多详细信息。
这两种方法都有权衡。 特定于提供程序的解决方案通常为您提供更多控制功能,但使用 AOP 方法更可重用。做适合您的UC的事情。
希望这有帮助。
干杯! -John
John 的答案是正确的,但重要的是要知道 Cache 类不是 Spring 管理的 bean,而是 CacheManager。
出于这个原因,你必须引入额外的AspectJ依赖项,并进行某种编译或编译后编织来定位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);
}