要实现JDK的动态代理的时候,我们就需要定义动态代理类,如下:
public class JDKProxy implements InvocationHandler {
Object object; //被代理类
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object object = null;
System.out.println("qian");
object= method.invoke(this.object,args);
System.out.println("hou");
return object;
}
public <T> T getProxy(){ //获得代理对象
T o =(T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), object.getClass().getInterfaces(), //代理需要实现的接口
this);
return o;
}
public JDKProxy(Object object) {
this.object = object;
}
}
c JD
此处我们可以看到我们的JDKProxy 实现了InvocationHandler 接口,并重写了invoke方法,在我们初学的时候很容易把我们method.invoke(this.object,args);写成method.invoke(proxy,args);然后我么还觉得大功告成,兴冲冲的点击运行,结果造成了死循环,然后检查半天代码才发现是自己写错了赶紧修复改成正确,这时你有纳闷了,这个参数proxy对象到底是啥呀,传进来也不用,不是浪费内存嘛,然后你就想将其输出到控制台看看,改成了如下:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("proxy"+proxy); Object object = null; System.out.println("qian"); object= method.invoke(this.object,args); System.out.println("hou"); return object; }
点击运行,然后控制台就出现一片红,并附上StackOverFlowError,栈溢出了,又死循环,然后百思不得其解,为什么呀?就只是想输出来看看,还会死循环!!?报错信息如下:
然后你想断点进去看看,到底哪里死循环了,结果调试半天只看到疯狂的在String和StringBuilder两个类之间来来回回,心想也没有使用StringBuiler啊,为什么一直在调用append?在此毫无头绪之时你突然想到JDK动态代理是会帮我们生成一个代理类的
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
然后你又在代码上加上这句代码,若为生成选择如下:
System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles","true");
点击查看我们的代理类
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package com.sun.proxy; import com.proxy.Tea; 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 Tea { private static Method m1; private static Method m3; private static Method m2; 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 water() 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 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.proxy.Tea").getMethod("water"); m2 = Class.forName("java.lang.Object").getMethod("toString"); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } }
前置知识:
System.out.println(a) //是会调用a的toString方法的
你看到JDK帮我们生成的代理类居然重写了toString(),hashcode等方法,里面几乎相同,都干了super.h.invoke(this, m2, (Object[])null);这么一件事然后捕捉异常,看到$Proxy0继承了Proxy,你点进去看,看到h是类InvocationHandler,这不就是我们自定义代理类实现的接口吗,这个又调用了invoke方法,难道这个super.h就是我们的自定义动态代理类JDKProxy吗?但你也没看到那里有给他赋值的地方,自己书写的代码相关的也只有Proxy.newProxyInstance()这里了,
然后又断点,运行程序,进入到newProxyInstance()方法
可以看到此处调用了ca.newInstance()方法,再进
可以看到此处调用cons.newInstance()方法,并将JDKProxy传递了过去,再点进去
然后再进
此处我们可以看到,调用了native关键字修饰的方法,表示是本地方法调用了其他语言类库的方法,详细用法请百度,大致用法是将传递进去的参数进行一系列操作然后通过构造器生成一个对象,然后我们点击下一步(Step Over)可以看到将我们自定义的代理类赋值给了Proxy的属性,之后再一路下一步便回到了我们最初的Proxy.newProxyInstance()方法。
这里的this.h也就是Proxy的InvocationHandler属性了
代理类$Proxy0继承Proxy实现了我们的自定义代理接口Tea,
他的super.h,也就是Proxy.h,也就是我们自定义的JDK代理类,然后便在此调用了JDKProxy的invoke()方法,传递过来的参数中的Object proxy也就是JDK动态生成的$Proxy0类了,如此递归调用invoke()方法,便造成了死循环出现了栈溢出,这就是为什么JDK动态代理会有死循环出现的原因了。
谢谢大家耐下心来观看到这里,若有错误还请友善指出,大家一起互励共勉,一起成长!
也许你还会问,你这不是只解释了toString的原因吗?另一个还没说呢
method.invoke(proxy,args);
眼尖的同学相信已经看到了$Proxy0一样也重写了我们需要被代理的方法