我们都知道,面向切面编程(AOP)的作用是:非侵入的增强代码片段。
使用的是代理的思想。
spring中使用了两种代理方式
在spring中,如果需要代理的类实现了接口,那么就使用JDK动态代理。
反之,就使用cglib。
两者的具体区别在这里不做讨论,如果不清楚两者的原理和区别,可以先了解一下。
以@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无关呢?
排除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同样会出现。现在只需要搞清楚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
类,同时实现了需要代理的接口。
接口的实现逻辑,将具体逻辑委托给了 InvocationHandler
的invoke
方法。
也就是说,在代理类中,所有方法都会运行invoke
中的逻辑。
回过头来再看一眼InvocationHandler
类的逻辑
可以看到,最终调用方法,还是在被代理对象中执行的。也就是说,代理对象并非将增强代码插入了原有的对象中,只是在调用原方法的前后做增强。方法inDoSomething
并不是目标方法,自然就无法被增强了。
非被代理方法在类内部调用被代理方法,此时被代理方法并不会被增强。而spring-aop底层又使用了代理,所以当然会失效了。