jdk动态代理中的问题——调用proxy的toString方法引起的栈溢出

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的源码:

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类,构造出该类的对象,并返回。















你可能感兴趣的:(jdk动态代理中的问题——调用proxy的toString方法引起的栈溢出)