jdk动态代理中的问题——调⽤proxy的toString⽅法引起的栈溢
出
import flect.InvocationHandler;
import flect.Method;
import flect.Proxy;
public class Test {
public static void main(String[] args) {
UserManager target = new UserManagerImpl();
UserManager proxy = (UserManager) Class().getClassLoader(),
proxy.addUser();
}
}
java的tostring方法interface UserManager {
public void addUser();
}
class UserManagerImpl implements UserManager {
@Override
public void addUser() {
System.out.println("");
}
}
class UserManagerProxy implements InvocationHandler {
private UserManager target;
public UserManagerProxy(UserManager target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("check privilege " + proxy);
method.invoke(target, args);
return null;
}
}
在执⾏代理处理类的System.out.println("check privilege " + proxy);时候,出现了java.lang.StackOverflowError错误。原因可以初步定位在proxy的toString⽅法上。
但是调试后发现proxy属于$Proxy0类,⽽$Proxy0这个Class是运⾏时⽣成的类,⽹上有⼀个⽜⼈贴出了$Proxy0的源码:
import flect.InvocationHandler;
import flect.Method;
import flect.Proxy;
import flect.UndeclaredThrowableException;
public final class $Proxy0 extends Proxy implements UserManager {
private static Method m1;
private static Method m0;
private static Method m3;
private static Method m2;
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals",
new Class[] { Class.forName("java.lang.Object") });
m0 = Class.forName("java.lang.Object").getMethod("hashCode",
new Class[0]);
m3 = Class.forName("cn.edu.jlu.proxy.UserManager").getMethod("addUser",
new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString",
new Class[0]);
} catch (NoSuchMethodException nosuchmethodexception) {
throw new Message());
} catch (ClassNotFoundException classnotfoundexception) {
throw new Message());
}
}
public $Proxy0(InvocationHandler invocationhandler) {
super(invocationhandler);
}
@Override
public final boolean equals(Object obj) {
try {
return ((Boolean) super.h.invoke(this, m1, new Object[] { obj }))
.booleanValue();
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
@Override
public final int hashCode() {
try {
return ((Integer) super.h.invoke(this, m0, null)).intValue();
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
@Override
public final String toString() {
try {
return (String) super.h.invoke(this, m2, null);
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
@Override
public void addUser() {
try {
super.h.invoke(this, m3, null);
return;
} catch (Error e) {
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
}
可以看到,调⽤toString⽅法的时候,调⽤了h的invoke⽅法,⽽h就是InvocationHandler的实例,所以是递归调⽤,所以就会出现上述所说的java.lang.StackOverflowError错误。
这⾥顺便说⼀下简单编写java动态代理的过程:
1.定义⼀个接⼝I
2.编写该接⼝I的实现类Impl
3.编写InvocationHandler接⼝的实现类H,构造H类对象的时候可以把要代理的对象target传⼊,target完成实际的动作。在⾥⾯的invoke⽅法⾥编写⾃⼰想实现的逻辑,然后再调⽤实际要完成的动作就可以。
4.调⽤wProxyInstance⽅法,传递的三个参数分别是代理类的类加载器(可以⽤Impl实例的g
etClass().getClassLoader()) 、代理类要实现的接⼝列表(可以⽤Impl实例getClass().getInterfaces())、InvocationHandler实现类的实例。
这样就⽣成了$Proxy0类的对象,由于$Proxy0类实现了I接⼝,所以可以将对象强制转型成I。
再说⼀下wProxyInstance⽅法的实际过程:
1.使⽤传⼊的InvocationHandler实例参数将Proxy类的h实例初始化,注意,如果传⼊空对象的话,会抛出空指针错误,即h不能为空。
2.运⾏时⽣成代理Class,即$Proxy0
3.利⽤上⾯动态⽣成的$Proxy0类,构造出该类的对象,并返回。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论