• 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();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    思考

    我百度了一下,没有讲的特别清楚的。
    于是想打断点找点线索。
    在这里插入图片描述
    这里确实注入了正确的代理类,但增强代码确实是没有生效。

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

    仅使用JDK动态代理尝试

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

    先来一个接口

    public interface ITest {
        void doSomething();
    
        void inDoSomething();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

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

    public class Test implements ITest {
    
        @Override
        public void doSomething() {
            System.out.println("Do something");
        }
    
        @Override
        public void inDoSomething() {
            this.doSomething();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    实现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);
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    测试一下

    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();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在这里插入图片描述
    果然, 内部调用的方法同样没有被增强。这个问题不使用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());
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87

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

    结论

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

  • 相关阅读:
    [C/C++]数据结构 LeetCode:用栈实现队列
    JVM区域划分
    vue项目优化
    生信云实证Vol.12:王者带飞LeDock!开箱即用&一键定位分子库+全流程自动化,3.5小时完成20万分子对接
    面试百问:App的Push推送原理&测试点
    《向量数据库指南》——提高向量数据库Milvus Cloud 2.3的运行效率
    Flask Run运行机制剖析
    procast模拟浇注时没有出现金属液体怎么解决
    【异构知识蒸馏:IVIF】
    DSP篇--C6678功能调试系列之SPI调试
  • 原文地址:https://blog.csdn.net/qq_43585377/article/details/127573863