Spring中类内部调用方法AOP失效的原因

AOP原理

我们都知道,面向切面编程(AOP)的作用是:非侵入的增强代码片段。
使用的是代理的思想。
spring中使用了两种代理方式

  1. JDK动态代理
  2. cglib代理

在spring中,如果需要代理的类实现了接口,那么就使用JDK动态代理。
反之,就使用cglib。
两者的具体区别在这里不做讨论,如果不清楚两者的原理和区别,可以先了解一下。

AOP失效

@Async注解为例。
当在方法上使用了这个注解,那么在调用此方法时,将会异步执行。这里就是spring使用AOP提供给我们的功能。
但是当代码如下所示时,在外部调用inDoSomeTing()时,方法doSomeThing()并不会以异步的方式执行。此时,AOP失效了

@Component
public class TestAop implements ITestAop {

    @Async
    @Override
    public void doSomeThing() {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("doSomething");
    }

    @Override
    public void inDoSomeTing() {
        this.doSomeThing();
    }
}

思考

我百度了一下,没有讲的特别清楚的。
于是想打断点找点线索。
Spring中类内部调用方法AOP失效的原因_第1张图片
这里确实注入了正确的代理类,但增强代码确实是没有生效。

是不是这个问题根本就与Spring无关呢?

仅使用JDK动态代理尝试

排除spring的影响,直接通过javaSE实现一个代理逻辑。

先来一个接口

public interface ITest {
    void doSomething();

    void inDoSomething();
}

随便实现一下, 加一个内部调用

public class Test implements ITest {

    @Override
    public void doSomething() {
        System.out.println("Do something");
    }

    @Override
    public void inDoSomething() {
        this.doSomething();
    }
}

实现InvocationHandler

public class TestProxy<T> implements InvocationHandler {

    private T target;

    public TestProxy(T target) {
        this.target = target;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (method.getName().equals("doSomething")) {
            System.out.println("执行方法前记录日志");
            Object res = method.invoke(target, args);
            System.out.println("执行方法后记录日志");
            return res;
        } else {
            return method.invoke(target, args);
        }
    }
}

测试一下

public class DoProxy {
    public static void main(String[] args) {
        ITest proxyTest = (ITest) Proxy.newProxyInstance(ITest.class.getClassLoader(), Test.class.getInterfaces(), new TestProxy(new Test()));
        proxyTest.doSomething();
        proxyTest.inDoSomething();
    }
}

Spring中类内部调用方法AOP失效的原因_第2张图片
果然, 内部调用的方法同样没有被增强。这个问题不使用spring同样会出现。现在只需要搞清楚JDK动态代理的原理就可以了。

我将动态代理生成的类,反编译后保存了下来。

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

import com.tianqi.code.read.proxy.ITest;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements ITest {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m4;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final void doSomething() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void inDoSomething() throws  {
        try {
            super.h.invoke(this, m4, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m3 = Class.forName("com.tianqi.code.read.proxy.ITest").getMethod("doSomething");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m4 = Class.forName("com.tianqi.code.read.proxy.ITest").getMethod("inDoSomething");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

可以看到,生成的代理类继承了Proxy类,同时实现了需要代理的接口。
接口的实现逻辑,将具体逻辑委托给了 InvocationHandlerinvoke方法。
Spring中类内部调用方法AOP失效的原因_第3张图片
也就是说,在代理类中,所有方法都会运行invoke中的逻辑。
回过头来再看一眼InvocationHandler类的逻辑
Spring中类内部调用方法AOP失效的原因_第4张图片
可以看到,最终调用方法,还是在被代理对象中执行的。也就是说,代理对象并非将增强代码插入了原有的对象中,只是在调用原方法的前后做增强。方法inDoSomething并不是目标方法,自然就无法被增强了。

结论

非被代理方法在类内部调用被代理方法,此时被代理方法并不会被增强。而spring-aop底层又使用了代理,所以当然会失效了。

你可能感兴趣的:(spring,spring-aop)