动态代理中的死循环导致的java.lang.StackOverflowError
目录
四、引申:$Proxy0其他可能出现死循环问题的代码及问题:
一、问题:循环打印直到栈溢出
...
拒绝中间商赚差价
拒绝中间商赚差价
拒绝中间商赚差价
Exception in thread "main" java.lang.StackOverflowError
at sun.nio.cs.UTF_8.updatePositions(UTF_8.java:77)
at sun.nio.cs.UTF_8.access$200(UTF_8.java:57)
at sun.nio.cs.UTF_8$Encoder.encodeArrayLoop(UTF_8.java:636)
at sun.nio.cs.UTF_8$Encoder.encodeLoop(UTF_8.java:691)
at java.nio.charset.CharsetEncoder.encode(CharsetEncoder.java:579)
at sun.nio.cs.StreamEncoder.implWrite(StreamEncoder.java:271)
at sun.nio.cs.StreamEncoder.write(StreamEncoder.java:125)
at java.io.OutputStreamWriter.write(OutputStreamWriter.java:207)
at java.io.BufferedWriter.flushBuffer(BufferedWriter.java:129)
at java.io.PrintStream.write(PrintStream.java:526)
at java.io.PrintStream.print(PrintStream.java:669)
at java.io.PrintStream.println(PrintStream.java:806)
at designPattern.proxy.dynamic.DynamicProxyHandler.invoke(DynamicProxyHandler.java:13)
at com.sun.proxy.$Proxy0.toString(Unknown Source)
at java.lang.String.valueOf(String.java:2994)
at java.io.PrintStream.println(PrintStream.java:821)
at designPattern.proxy.dynamic.DynamicProxyHandler.invoke(DynamicProxyHandler.java:14)
at com.sun.proxy.$Proxy0.toString(Unknown Source)
at java.lang.String.valueOf(String.java:2994)
at java.io.PrintStream.println(PrintStream.java:821)
at designPattern.proxy.dynamic.DynamicProxyHandler.invoke(DynamicProxyHandler.java:14)
二、代码:
1、接口及实现类
public interface HouseRenting {
void rent(int money);
}
/**
* 被代理的类
* @author KentChiang
*
*/
public class HouseOwnerRenting implements HouseRenting {
@Override
public void rent(int money) {
System.out.println("房东以" + money + "元/月向外出租");
}
}
2、动态代理类
public class DynamicProxyHandler implements InvocationHandler{
private Object proxied;
public DynamicProxyHandler(Object proxied) {
this.proxied = proxied;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("拒绝中间商赚差价");
System.out.println(proxy);//导致死循环的代码
//return method.invoke(proxy, args);//这样写也会出问题
return method.invoke(proxied, args);
}
}
3、测试类
public class SimpleDynamicProxy {
public static void consumer(HouseRenting renting) {
renting.rent(1500);
}
public static void main(String[] args) {
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
HouseRenting renting = new HouseOwnerRenting();
HouseRenting proxy = (HouseRenting) Proxy.newProxyInstance(HouseRenting.class.getClassLoader(),
new Class[] { HouseRenting.class }, new DynamicProxyHandler(renting));
consumer(proxy);
}
}
三、原因分析:
动态代理类中加了System.out.println(proxy);才出现的问题,必然是这句话导致的,需要结合生成的com.sun.proxy.$Proxy0源码分析,通过设置即可保存生成的$Proxy0源码,保存在子工程目录下com\sun\proxy。
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
部分Proxy代码如下。
public final class $Proxy0 extends Proxy
implements HouseRenting {
...
public final String toString()
throws
{
try
{
return (String)this.h.invoke(this, m2, null);
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void rent(int paramInt)
throws
{
try
{
this.h.invoke(this, m3, new Object[] { Integer.valueOf(paramInt) });
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}...
static
{
try
{
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m3 = Class.forName("designPattern.proxy.service.HouseRenting").getMethod("rent", new Class[] { Integer.TYPE });
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
可以看到toString中调用了 (String)this.h.invoke(this, m2, null);其中this就是$Proxy0,h是动态代理类,调用动态代理类重写的public Object invoke(Object proxy, Method method, Object[] args)方法,里面又调用$Proxy0的toString()方法,进入了死循环。
四、引申:$Proxy0其他可能出现死循环问题的代码及问题:
类似 this.h.invoke(this, mX, XX);这种形式的都可能因为我们编码不当出问题,把this作为参数想起来如果在动态代理类中把$Proxy0对象作为invoke方法的参数如return method.invoke(proxy, args)同样会引起死循环。
Caused by: java.lang.reflect.UndeclaredThrowableException
at com.sun.proxy.$Proxy0.rent(Unknown Source)
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at designPattern.proxy.dynamic.DynamicProxyHandler.invoke(DynamicProxyHandler.java:15)
at com.sun.proxy.$Proxy0.rent(Unknown Source)
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at designPattern.proxy.dynamic.DynamicProxyHandler.invoke(DynamicProxyHandler.java:15)