Case:接口实现类的方法上使用AspectJ方式实现aop的异常问题

来源:互联网 发布:施温格 知乎 编辑:程序博客网 时间:2024/05/17 23:10

Case:接口实现类的方法上使用AspectJ方式实现aop的异常问题

现象:

在接口实现类的方法上定义AOP注解,但是在AOP注解执行方法内部无法获取注解类的实例

问题简化如下:
接口:

package reflect;interface TestInterface{    void print();}

接口实现类:

package reflect;class Test implements TestInterface{    public void print() {        System.out.println("I am print");    }}

注解类:

@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@Documented@interface Log{    String value() default "";} 

AOP执行方法:

@Around("@annotation(reflect.Log)")public Object process(ProceedingJoinPoint joinPoint) {    //根据切入点获取类名、方法名    MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();    Method method = methodSignature.getMethod();    String methodName = method.getName();    String className = method.getDeclaringClass().getSimpleName();    //获取注解中的参数    Log log = method.getDeclaredAnnotation(Log.class);    String value = log.value();    // 参数操作    ......    Object object = null;    try {        object = joinPoint.proceed();    } catch (Throwable throwable) {        throwable.printStackTrace();    }    return object;}

在执行到process方法中时,获取到的methodName值是print,className值是TestInterface,method.toString结果是:public abstract void reflect.TestInterface.print(),log值是null。

疑问:预想情况下,log值不应该是null,而是Log注解类的实例;className值应为Test,method执行toString方法的结果应为:public void reflect.Test.print()

分析:

在process方法中断点,观察线程执行堆栈如下:
这里写图片描述
从上图看到,process方法是被$Proxy75代理类间接调用的,说明AOP底层实现是JDK动态代理。
在JDK动态代理中,程序通过生成实现被代理类接口的代理类达到代理的目的。在生成的代理类中,被代理类方法对应的Method实例是通过被代理类实现的接口获取的。这个Method实例通过方法调用层层传递,在process方法中,被封装到MethodInvvocationProceedingJoinPoint对象的joinPoint实例中。所以在process方法中获取对应参数时才会出现之前奇怪的结果。

jdk动态代理执行流程详见:JDK动态代理执行流程解析

通过以上分析可知,传入process方法的Method实例是TestInterface接口中print方法的Method实例,不是Test实现类的print方法的Method实例,所以在process方法中无法通过调用method.getDeclaredAnnotation方法获取Log注解实例。

解决:

将applicationContext.xml中的

<aop:aspectj-autoproxy />

改成

<aop:aspectj-autoproxy proxy-target-class="true" />

问题解决。
其实质是,在AOP动态代理的实现方式上,使用cglib代理代替jdk动态代理。

(TODO:关于cglib动态代理的实现原理以后再写)

原创粉丝点击