解决手段
观察InvocationHandler类中的invoke方法内部利用反射调用被代理类的方法时, 是否写成了这样
如果是, 修改为
如果您感兴趣, 可以看看我下面的分析, 欢迎批评与指正.
问题复现
最近在学习jdk的动态代理, 自己写了一个Demo后, 发现程序陷入了死循环, 通过分析源码后, 成功定位并解决了问题, 也加深了自己对动态代理的理解.
书写动态代理的一般步骤(可以结合下面的代码食用)
- 定义接口, 如下面代码的Task接口,确定将要被代理的方法
- 书写实现类, 实现这个接口Task, 实现类为RealTask
- 实现InvocationHandler接口. 注意这个实现类要以被代理类为的数据成员, 并利用构造函数初始化它.
- 调用Proxy.newInstance方法获得动态代理类, 第一个参数为接口的类加载器, 第二个参数为接口列表, 第三个参数为我们实现好的InvocationHandler的实现类.
- 利用这个动态代理类调用接口内的方法便实现了动态代理
interface Task{
void doSomething();
void somethingElse(String arg);
}
class RealTask implements Task{
@Override
public void doSomething() {
System.out.println("do something");
}
@Override
public void somethingElse(String arg) {
System.out.println("something else" + arg);
}
}
public class SimpleDynamicProxy implements InvocationHandler {
private Task task;
public SimpleDynamicProxy(Task task) {
this.task = task;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理:" + method.getName());
return method.invoke(proxy, args);
}
public static void main(String[] args) {
Task task = (Task) Proxy.newProxyInstance(Task.class.getClassLoader(),
new Class[]{Task.class}, new SimpleDynamicProxy(new RealTask()));
task.somethingElse("something");
}
}
问题排查
运行以后, 悲剧就发生了, 程序陷入了死循环.
排查了半天无果后, 查阅资料发现动态代理不同于静态代理, 静态代理在编译前, 我们就已经书写好的代理类, 所以可以看到代理类的class文件 . 按照一般的经验, 动态这个字眼告诉我, 文件是运行时期生成的, 所以, 我推测在运行时, jvm帮我们生成了这个代理类, 通过搜索, 我发现在利用Proxy.newInstance前加上
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
便可以看到这个动态代理类的源码, 于是修改是main函数为
public static void main(String[] args) {
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
Task task = (Task) Proxy.newProxyInstance(Task.class.getClassLoader(),
new Class[]{Task.class}, new SimpleDynamicProxy(new RealTask()));
task.somethingElse("something");
}
再次运行后, 便可以看到这个代理类的源码, 利用IDEA直接查看class文件, 是在src的同级目录下生成的
下面是源码
final class $Proxy0 extends Proxy implements Task {
private static Method m1;
private static Method m4;
private static Method m2;
private static Method m3;
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, m4, (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 somethingElse(String var1) throws {
try {
super.h.invoke(this, m3, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
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"));
m4 = Class.forName("base.reflect.Task").getMethod("doSomething");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("base.reflect.Task").getMethod("somethingElse", Class.forName("java.lang.String"));
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
分析源码
首先看到这个代理类叫做$Proxy0, 继承自Proxy, 实现了我们自定义的接口Task.
接着观察到构造方法, 传入了InvocationHandler的实现类, 也就是我们自定义的类SimpleDynamicProxy,
接着观察方法
public final void somethingElse(String var1) throws {
try {
super.h.invoke(this, m3, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
super.h就是InvocatinHandler实例SimpleDynamicProxy, 然后调用了它的invoke方法, 自然就跳转到了SimpleDynamicProxy的invoke方法, 仔细看invoke方法的第一个参数, 竟然是this, 也就是代理对象, 然后函数内利用反射调用proxy的方法, 注意这个proxy是代理对象, 就是代理对象再次调用somethingElse(String args)方法, 好了, 问题出现了, 一开始我们调用代理对象somethingElse方法, 方法内部跳到InvocatinHandler的invoke方法, 又执行了代理对象的somethingElse方法, 这就出现了递归调用 因此将return methiod.invoke(proxy, args)修改为return methiod.invoke(task, args)
public class SimpleDynamicProxy implements InvocationHandler {
private Task task;
public SimpleDynamicProxy(Task task) {
this.task = task;
}
// 代理类对象proxy 被代理类方法method 被代理类参数args
// return method.invoke(proxy, args);
// 代理类对象再次调用invoke方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理:" + method.getName());
return method.invoke(proxy, args);
}
总结
出现问题的原因还是没有理解SimpleDynamicProxy的invoke方法的参数的意思
- 第一个参数proxy是代理对象, 不是被代理对象
- 第二个参数method是接口中某个方法, 当然也有equals方法和hashcode方法
- 第三个参数为参数列表, 当函数不需要参数时, 为null
动态代理这代码也写的太好了, 代理类Proxy内部用一个InvocatinHandler类, 调用方法的时候直接调用InvocationHandler的方法, 而InvocationHandler接口的实现全部交由用户来实现, 真的写的太好了, 真心佩服.