如何在@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.evict(..))
此表达式将您的 AOP 切面与 ConcurrentMapCache
"provider" 联系起来,Spring 的缓存抽象 中的默认值(以及 Spring开机).
当您在应用程序中使用 Ehcache、Hazelcast、Redis、GemFire/Geode 或这些 "providers" 的多种组合时会发生什么情况?
相反,您可以将 AOP 切入点表达式稍微调整为这样...
execution(* org.springframework.cache.Cache.evict(..))
有关详细信息,请参阅 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);
}
我需要在调用@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.evict(..))
此表达式将您的 AOP 切面与 ConcurrentMapCache
"provider" 联系起来,Spring 的缓存抽象 中的默认值(以及 Spring开机).
当您在应用程序中使用 Ehcache、Hazelcast、Redis、GemFire/Geode 或这些 "providers" 的多种组合时会发生什么情况?
相反,您可以将 AOP 切入点表达式稍微调整为这样...
execution(* org.springframework.cache.Cache.evict(..))
有关详细信息,请参阅 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);
}