如何在AspectJ切面关联中获取Pointcut上下文的注解参数
解决方案
核心限制说明
AspectJ的pertarget/percflow这类切面实例化子句,仅支持绑定目标对象、this这类类级别的上下文,无法直接绑定方法级别的注解实例。而且你原有的方案存在逻辑缺陷:pertarget是按目标类实例创建切面,同一个类的多个@Memoized方法如果配置了不同的过期时间,是无法共用同一个切面实例的缓存的。
推荐实现方案
改用单例切面,维护一个方法级别的缓存映射表,第一次调用标注了@Memoized的方法时,自动读取注解的过期配置创建对应缓存,后续调用直接复用该缓存。代码实现如下:
import java.time.Duration; import java.util.Arrays; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; import com.google.common.util.concurrent.ExecutionError; import com.google.common.util.concurrent.UncheckedExecutionException; public aspect Memoize { // 存储每个方法对应的专属缓存,key为方法签名 private final ConcurrentMap<String, Cache<String, Object>> methodCaches = new ConcurrentHashMap<>(); public pointcut memoized(Memoized memoized) : call(@Memoized * *.*()) && @annotation(memoized) ; Object around(Memoized memoized): memoized(memoized) { String methodSignature = thisJoinPoint.getSignature().toShortString(); // 首次调用时初始化对应过期时间的缓存 Cache<String, Object> cache = methodCaches.computeIfAbsent(methodSignature, sig -> { Duration expireAfter = Duration.parse(memoized.value()); return CacheBuilder.newBuilder() .expireAfterWrite(expireAfter) .build(); }); try { // 无参方法直接用方法签名作为缓存key即可 return cache.get(methodSignature, () -> proceed(memoized)); } catch (ExecutionException | UncheckedExecutionException | ExecutionError e) { throw new RuntimeException("缓存执行失败", e.getCause()); } } }
优化提示
如果后续需要支持带参数的方法缓存,可以将缓存key调整为「方法签名 + 参数值哈希」的组合;如果需要隔离不同对象实例的缓存,可以在key中加入目标对象的哈希值:String cacheKey = thisJoinPoint.getTarget().hashCode() + "_" + methodSignature + "_" + Arrays.hashCode(thisJoinPoint.getArgs());
内容的提问来源于stack exchange,提问作者Haf
相关产品推荐
相关产品推荐

