关于JDK
与cglib
动态代理的使用不是本文关注的重点,如有不清楚的同学可以查询相关资料进行了解。本文主要是要讲一下在面对方法存在嵌套调用时JDK
与cglib
动态代理的区别以及原因。
先看下测试代码,注意下TestProxyJDKImpl
的test1
方法调用了test2
方法即可。
java
public class Test {
static interface TestProxyJDK{
void test1();
void test2();
}
static class TestProxyJDKImp implements TestProxyJDK{
@Override
public void test1() {
System.out.println("执行test1");
test2();
}
@Override
public void test2() {
System.out.println("执行test2");
}
}
public static void main(String[] args) throws NoSuchMethodException {
//输出代理类
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
//创建JDK动态代理对象
TestProxyJDKImp testProxyJDKImp = new TestProxyJDKImp();
TestProxyJDK testProxyJDK = (TestProxyJDK) Proxy.newProxyInstance(Test.class.getClassLoader(), TestProxyJDKImp.class.getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String name = method.getName();
System.out.println(name + "方法被拦截前执行方法");
method.invoke(testProxyJDKImp, args);
System.out.println(name + "方法被拦截后执行方法");
return null;
}
});
//调用test1方法
testProxyJDK.test1();
//输出代理类
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "E:\\cglib");
//创建cglib代理对象
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(TestProxyJDKImp.class);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("CGLIB拦截前");
proxy.invokeSuper(obj,args);
System.out.println("CGLIB拦截后");
return null;
}
});
TestProxyJDK testProxyCglib = (TestProxyJDK) enhancer.create();
//调用test1f方法
testProxyCglib.test1();
}
}
为了更好说明情况,我们先把生成的代理类以文件的形式保存下来。对于JDK动态代理,只需要在代码里添加如下一行代码即可。
java
//设置系统属性 1.8还是这种写法
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
生成类如下,主要看下test1
和test2
方法的代码即可:
java
final class $Proxy0 extends Proxy implements TestProxyJDK {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m4;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
// 这里的h就是我们创建的InvocationHandler对象
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 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 test1() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void test2() 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"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("com.xxx.xxx.Test$TestProxyJDK").getMethod("test1");
m4 = Class.forName("com.xxx.xxx.Test$TestProxyJDK").getMethod("test2");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
如果是cglib
那么可以使用下面的方式输出代理类
java
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "your path");
cglib
生成的相关类不止一个,这里就贴一下生成的代理类,主要看下test1
和test2
方法的代码即可:
java
public class Test$TestProxyJDKImp$$EnhancerByCGLIB$$62be832c extends TestProxyJDKImp implements Factory {
private boolean CGLIB$BOUND;
private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
private static final Callback[] CGLIB$STATIC_CALLBACKS;
private MethodInterceptor CGLIB$CALLBACK_0;
private static final Method CGLIB$test2$0$Method;
private static final MethodProxy CGLIB$test2$0$Proxy;
private static final Object[] CGLIB$emptyArgs;
private static final Method CGLIB$test1$1$Method;
private static final MethodProxy CGLIB$test1$1$Proxy;
private static final Method CGLIB$finalize$2$Method;
private static final MethodProxy CGLIB$finalize$2$Proxy;
private static final Method CGLIB$equals$3$Method;
private static final MethodProxy CGLIB$equals$3$Proxy;
private static final Method CGLIB$toString$4$Method;
private static final MethodProxy CGLIB$toString$4$Proxy;
private static final Method CGLIB$hashCode$5$Method;
private static final MethodProxy CGLIB$hashCode$5$Proxy;
private static final Method CGLIB$clone$6$Method;
private static final MethodProxy CGLIB$clone$6$Proxy;
static void CGLIB$STATICHOOK1() {
CGLIB$THREAD_CALLBACKS = new ThreadLocal();
CGLIB$emptyArgs = new Object[0];
Class var0 = Class.forName("com.xxx.xxx.Test$TestProxyJDKImp$$EnhancerByCGLIB$$62be832c");
Class var1;
Method[] var10000 = ReflectUtils.findMethods(new String[]{"finalize", "()V", "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
CGLIB$finalize$2$Method = var10000[0];
CGLIB$finalize$2$Proxy = MethodProxy.create(var1, var0, "()V", "finalize", "CGLIB$finalize$2");
CGLIB$equals$3$Method = var10000[1];
CGLIB$equals$3$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$3");
CGLIB$toString$4$Method = var10000[2];
CGLIB$toString$4$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$4");
CGLIB$hashCode$5$Method = var10000[3];
CGLIB$hashCode$5$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$5");
CGLIB$clone$6$Method = var10000[4];
CGLIB$clone$6$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$6");
var10000 = ReflectUtils.findMethods(new String[]{"test2", "()V", "test1", "()V"}, (var1 = Class.forName("com.xxx.xxx.Test$TestProxyJDKImp")).getDeclaredMethods());
CGLIB$test2$0$Method = var10000[0];
CGLIB$test2$0$Proxy = MethodProxy.create(var1, var0, "()V", "test2", "CGLIB$test2$0");
CGLIB$test1$1$Method = var10000[1];
CGLIB$test1$1$Proxy = MethodProxy.create(var1, var0, "()V", "test1", "CGLIB$test1$1");
}
final void CGLIB$test2$0() {
super.test2();
}
public final void test2() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
var10000.intercept(this, CGLIB$test2$0$Method, CGLIB$emptyArgs, CGLIB$test2$0$Proxy);
} else {
super.test2();
}
}
final void CGLIB$test1$1() {
super.test1();
}
public final void test1() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
var10000.intercept(this, CGLIB$test1$1$Method, CGLIB$emptyArgs, CGLIB$test1$1$Proxy);
} else {
super.test1();
}
}
final void CGLIB$finalize$2() throws Throwable {
super.finalize();
}
protected final void finalize() throws Throwable {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
var10000.intercept(this, CGLIB$finalize$2$Method, CGLIB$emptyArgs, CGLIB$finalize$2$Proxy);
} else {
super.finalize();
}
}
final boolean CGLIB$equals$3(Object var1) {
return super.equals(var1);
}
public final boolean equals(Object var1) {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
Object var2 = var10000.intercept(this, CGLIB$equals$3$Method, new Object[]{var1}, CGLIB$equals$3$Proxy);
return var2 == null ? false : (Boolean)var2;
} else {
return super.equals(var1);
}
}
final String CGLIB$toString$4() {
return super.toString();
}
public final String toString() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
return var10000 != null ? (String)var10000.intercept(this, CGLIB$toString$4$Method, CGLIB$emptyArgs, CGLIB$toString$4$Proxy) : super.toString();
}
final int CGLIB$hashCode$5() {
return super.hashCode();
}
public final int hashCode() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
Object var1 = var10000.intercept(this, CGLIB$hashCode$5$Method, CGLIB$emptyArgs, CGLIB$hashCode$5$Proxy);
return var1 == null ? 0 : ((Number)var1).intValue();
} else {
return super.hashCode();
}
}
final Object CGLIB$clone$6() throws CloneNotSupportedException {
return super.clone();
}
protected final Object clone() throws CloneNotSupportedException {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
return var10000 != null ? var10000.intercept(this, CGLIB$clone$6$Method, CGLIB$emptyArgs, CGLIB$clone$6$Proxy) : super.clone();
}
public static MethodProxy CGLIB$findMethodProxy(Signature var0) {
String var10000 = var0.toString();
switch(var10000.hashCode()) {
case -1574182249:
if (var10000.equals("finalize()V")) {
return CGLIB$finalize$2$Proxy;
}
break;
case -1147892426:
if (var10000.equals("test1()V")) {
return CGLIB$test1$1$Proxy;
}
break;
case -1147862635:
if (var10000.equals("test2()V")) {
return CGLIB$test2$0$Proxy;
}
break;
case -508378822:
if (var10000.equals("clone()Ljava/lang/Object;")) {
return CGLIB$clone$6$Proxy;
}
break;
case 1826985398:
if (var10000.equals("equals(Ljava/lang/Object;)Z")) {
return CGLIB$equals$3$Proxy;
}
break;
case 1913648695:
if (var10000.equals("toString()Ljava/lang/String;")) {
return CGLIB$toString$4$Proxy;
}
break;
case 1984935277:
if (var10000.equals("hashCode()I")) {
return CGLIB$hashCode$5$Proxy;
}
}
return null;
}
public Test$TestProxyJDKImp$$EnhancerByCGLIB$$62be832c() {
CGLIB$BIND_CALLBACKS(this);
}
public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] var0) {
CGLIB$THREAD_CALLBACKS.set(var0);
}
public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] var0) {
CGLIB$STATIC_CALLBACKS = var0;
}
private static final void CGLIB$BIND_CALLBACKS(Object var0) {
Test$TestProxyJDKImp$$EnhancerByCGLIB$$62be832c var1 = (Test$TestProxyJDKImp$$EnhancerByCGLIB$$62be832c)var0;
if (!var1.CGLIB$BOUND) {
var1.CGLIB$BOUND = true;
Object var10000 = CGLIB$THREAD_CALLBACKS.get();
if (var10000 == null) {
var10000 = CGLIB$STATIC_CALLBACKS;
if (var10000 == null) {
return;
}
}
var1.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])var10000)[0];
}
}
public Object newInstance(Callback[] var1) {
CGLIB$SET_THREAD_CALLBACKS(var1);
Test$TestProxyJDKImp$$EnhancerByCGLIB$$62be832c var10000 = new Test$TestProxyJDKImp$$EnhancerByCGLIB$$62be832c();
CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
return var10000;
}
public Object newInstance(Callback var1) {
CGLIB$SET_THREAD_CALLBACKS(new Callback[]{var1});
Test$TestProxyJDKImp$$EnhancerByCGLIB$$62be832c var10000 = new Test$TestProxyJDKImp$$EnhancerByCGLIB$$62be832c();
CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
return var10000;
}
public Object newInstance(Class[] var1, Object[] var2, Callback[] var3) {
CGLIB$SET_THREAD_CALLBACKS(var3);
Test$TestProxyJDKImp$$EnhancerByCGLIB$$62be832c var10000 = new Test$TestProxyJDKImp$$EnhancerByCGLIB$$62be832c;
switch(var1.length) {
case 0:
var10000.<init>();
CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
return var10000;
default:
throw new IllegalArgumentException("Constructor not found");
}
}
public Callback getCallback(int var1) {
CGLIB$BIND_CALLBACKS(this);
MethodInterceptor var10000;
switch(var1) {
case 0:
var10000 = this.CGLIB$CALLBACK_0;
break;
default:
var10000 = null;
}
return var10000;
}
public void setCallback(int var1, Callback var2) {
switch(var1) {
case 0:
this.CGLIB$CALLBACK_0 = (MethodInterceptor)var2;
default:
}
}
public Callback[] getCallbacks() {
CGLIB$BIND_CALLBACKS(this);
return new Callback[]{this.CGLIB$CALLBACK_0};
}
public void setCallbacks(Callback[] var1) {
this.CGLIB$CALLBACK_0 = (MethodInterceptor)var1[0];
}
static {
CGLIB$STATICHOOK1();
}
}
在方法1中调用方法2使用JDK
动态代理和使用cglib
动态代理有什么区别?
执行上面测试代码的main
方法,打印结果如下:
tex
test1方法被拦截前执行方法
执行test1
执行test2
test1方法被拦截后执行方法
CGLIB拦截前
执行test1
CGLIB拦截前
执行test2
CGLIB拦截后
CGLIB拦截后
可以看到,使用JDK
代理的时候,在test1
方法里调用test2
时,test2
方法并未被拦截,调用的还是被代理类的test2
方法。使用cglib
代理的时候,在test1
方法里调用test2
时,test2
也被拦截,调用的是生成的代理类的test2
方法。
这是为什么呢?
回到我们生成的代理类,使用JDK
代理的时候,调用test1
方法,最终执行的是InvocationHandler
的invoke
方法.
java
super.h.invoke(this, m3, (Object[])null);
在invoke
方法里调用被代理的test1
方法
java
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String name = method.getName();
System.out.println(name + "方法被拦截前执行方法");
method.invoke(testProxyJDKImp, args);
System.out.println(name + "方法被拦截后执行方法");
return null;
}
注意这里使用 method.invoke(testProxyJDKImp, args)
使用的是被代理对象,那么在test1
方法里调用this.test2
时,这个this
自然就是被代理对象,那么test2
走的就是原始的方法;
而使用cglib
代理的时候,调用test1
方法最终执行的是
java
public final void test1() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
//这个this就是下面的obj,是代理对象
var10000.intercept(this, CGLIB$test1$1$Method, CGLIB$emptyArgs, CGLIB$test1$1$Proxy);
} else {
super.test1();
}
}
也就是我们实现的MethodInterceptor
的intercept
方法里
java
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("CGLIB拦截前");
proxy.invokeSuper(obj,args);
System.out.println("CGLIB拦截后");
return null;
}
注意,cglib
代理时我们从头到尾并不需要被代理类的对象,这里的obj
是代理对象,那么自然在调用test1
方法时,它里面的this.test2
方法里的this
就是代理对象,自然执行的是被代理后的test2
方法。
说到这里,你有没有想起@Transactional
的一种失效的使用场景呢?其实道理都是一样的。