如何在AOP代理的情况下获取注解所在的全限定类名
在通过AOP切片来使用redis缓存加速的时候,势必会涉及key的取值,能够想到比较好的方法便是自定义注解+方法名称+参数+全限定类名。但是在获取全限定类名的时候出了一点问题记录下来
直接上最终代码
@Around("@annotation(com.ava.ww.common.cache.RedisCache)")
public Object redisAspectMethod(ProceedingJoinPoint point) {
// 先获取缓存key
MethodSignature signature = (MethodSignature) point.getSignature();
//获取Method对象
Method method = signature.getMethod();
RedisCache annotation = method.getAnnotation(RedisCache.class);
// 拼接缓存key 注解+方法名+参数+全限定类名
String redisKey =
annotation.prefix() + method.getName() + "_" + Arrays.toString(point.getArgs()) +"_"+ point.getTarget().getClass().getName();
//此处省略分布式锁访问过程
...
return dataFromDatabase;
}
正确获取被代理对象的全限定类型的方法为:
point.getTarget().getClass().getName()
为什么不能够通过以下方式获取全限定类名?
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
Class declaringClass = method.getDeclaringClass();
String fqcn = declaringClass.getName();
原因是在Spring AOP代理的情况下,JoinPoint获取到的签名对象是JDK动态代理生成的一个内部类,并不是原始类的方法签名。
Spring AOP是基于动态代理实现的,当目标对象被代理时,实际调用的是动态生成的代理类的方法,所以该方法的签名是代理类的内部方法签名,而不是原始类的方法签名。
具体来看,在Spring AOP中,ProceedingJoinPoint的getSignature()获取的是MethodSignature,而这个MethodSignature的实际类型是:
org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint$MethodSignatureImpl
这是一个JDK动态代理内部生成的类,用于描述当前被调用方法的信息,但不代表原始类本身。
所以通过getSignature()获得的签名表示的是代理类的内部方法调用,而不是原始类的方法调用,自然无法获取到原始类的全限定类名。
总结
要获取原始类的全限定类名,可以通过以下方式:
通过ProceedingJoinPoint
的getTarget()
获取被代理的原始对象,在通过originalObject.getClass().getName()
获取原始类全限定类名这种方式可以跳过代理,直接获取到目标对象的实际类型,从而获得原始类的全限定类名。
所以Spring AOP代理会影响JoinPoint获取到的方法签名,需要特殊处理才能获取到原始类信息。这是由其动态代理机制决定的。