JDK动态代理中的问题——调用proxy的toString方法引起的栈溢出,jdktostring
分享于 点击 2185 次 点评:214
JDK动态代理中的问题——调用proxy的toString方法引起的栈溢出,jdktostring
Java代码- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.Method;
- import java.lang.reflect.Proxy;
- public class Test {
- public static void main(String[] args) {
- UserManager target = new UserManagerImpl();
- UserManager proxy = (UserManager) Proxy.newProxyInstance(target.getClass().getClassLoader(),
- target.getClass().getInterfaces(), new UserManagerProxy(target));
- proxy.addUser();
- }
- }
- interface UserManager {
- public void addUser();
- }
- class UserManagerImpl implements UserManager {
- @Override
- public void addUser() {
- System.out.println("add user...");
- }
- }
- 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的源码:
Java代码
- 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 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 NoSuchMethodError(nosuchmethodexception.getMessage());
- } catch (ClassNotFoundException classnotfoundexception) {
- throw new NoClassDefFoundError(classnotfoundexception.getMessage());
- }
- }
- 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.调用Proxy.newProxyInstance方法,传递的三个参数分别是代理类的类加载器(可以用Impl实例的getClass().getClassLoader())
、代理类要实现的接口列表(可以用Impl实例getClass().getInterfaces())、InvocationHandler实现类的实例。
这样就生成了$Proxy0类的对象,由于$Proxy0类实现了I接口,所以可以将对象强制转型成I。
再说一下Proxy.newProxyInstance方法的实际过程:
1.使用传入的InvocationHandler实例参数将Proxy类的h实例初始化,注意,如果传入空对象的话,会抛出空指针错误,即h不能为空。
2.运行时生成代理Class,即$Proxy0
3.利用上面动态生成的$Proxy0类,构造出该类的对象,并返回。
来源:http://chaisencs.iteye.com/blog/1649826
相关文章
- 暂无相关文章
用户点评