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

Java代码  
  1. import java.lang.reflect.InvocationHandler;  
  2. import java.lang.reflect.Method;  
  3. import java.lang.reflect.Proxy;  
  4.   
  5. public class Test {  
  6.   
  7.     public static void main(String[] args) {  
  8.   
  9.         UserManager target = new UserManagerImpl();  
  10.           
  11.         UserManager proxy = (UserManager) Proxy.newProxyInstance(target.getClass().getClassLoader(),  
  12.                 target.getClass().getInterfaces(), new UserManagerProxy(target));  
  13.         proxy.addUser();  
  14.     }  
  15.   
  16. }  
  17.   
  18.   
  19. interface UserManager {  
  20.       
  21.     public void addUser();  
  22. }  
  23.   
  24. class UserManagerImpl implements UserManager {  
  25.   
  26.     @Override  
  27.     public void addUser() {  
  28.         System.out.println("add user...");  
  29.     }  
  30.       
  31. }   
  32.   
  33. class UserManagerProxy implements InvocationHandler {  
  34.   
  35.     private UserManager target;  
  36.       
  37.     public UserManagerProxy(UserManager target) {  
  38.         this.target = target;  
  39.     }  
  40.       
  41.     @Override  
  42.     public Object invoke(Object proxy, Method method, Object[] args)  
  43.             throws Throwable {  
  44.           
  45.         System.out.println("check privilege " + proxy);  
  46.           
  47.         method.invoke(target, args);  
  48.           
  49.         return null;  
  50.     }  
  51.       
  52. }  


在执行代理处理类的System.out.println("check privilege " + proxy);时候,出现了java.lang.StackOverflowError错误。原因可以初步定位在proxy的toString方法上。 
但是调试后发现proxy属于$Proxy0类,而$Proxy0这个Class是运行时生成的类,网上有一个牛人贴出了$Proxy0的源码: 

Java代码  
  1. import java.lang.reflect.InvocationHandler;   
  2. import java.lang.reflect.Method;  
  3. import java.lang.reflect.Proxy;   
  4. import java.lang.reflect.UndeclaredThrowableException;  
  5.    
  6. public final class $Proxy0 extends Proxy implements UserManager {  
  7.     private static Method m1;  
  8.     private static Method m0;  
  9.     private static Method m3;  
  10.     private static Method m2;  
  11.   
  12.     static {  
  13.         try {  
  14.             m1 = Class.forName("java.lang.Object").getMethod("equals",  
  15.                     new Class[] { Class.forName("java.lang.Object") });  
  16.             m0 = Class.forName("java.lang.Object").getMethod("hashCode",  
  17.                     new Class[0]);  
  18.             m3 = Class.forName("cn.edu.jlu.proxy.UserManager").getMethod("addUser",  
  19.                     new Class[0]);  
  20.             m2 = Class.forName("java.lang.Object").getMethod("toString",  
  21.                     new Class[0]);  
  22.         } catch (NoSuchMethodException nosuchmethodexception) {  
  23.             throw new NoSuchMethodError(nosuchmethodexception.getMessage());  
  24.         } catch (ClassNotFoundException classnotfoundexception) {  
  25.             throw new NoClassDefFoundError(classnotfoundexception.getMessage());  
  26.         }  
  27.     }  
  28.   
  29.     public $Proxy0(InvocationHandler invocationhandler) {  
  30.         super(invocationhandler);  
  31.     }  
  32.   
  33.     @Override  
  34.     public final boolean equals(Object obj) {  
  35.         try {  
  36.             return ((Boolean) super.h.invoke(this, m1, new Object[] { obj }))  
  37.                     .booleanValue();  
  38.         } catch (Throwable throwable) {  
  39.             throw new UndeclaredThrowableException(throwable);  
  40.         }  
  41.     }  
  42.   
  43.     @Override  
  44.     public final int hashCode() {  
  45.         try {  
  46.             return ((Integer) super.h.invoke(this, m0, null)).intValue();  
  47.         } catch (Throwable throwable) {  
  48.             throw new UndeclaredThrowableException(throwable);  
  49.         }  
  50.     }  
  51.   
  52.     @Override  
  53.     public final String toString() {  
  54.         try {  
  55.             return (String) super.h.invoke(this, m2, null);  
  56.         } catch (Throwable throwable) {  
  57.             throw new UndeclaredThrowableException(throwable);  
  58.         }  
  59.     }  
  60.   
  61.     @Override  
  62.     public void addUser() {  
  63.         try {  
  64.             super.h.invoke(this, m3, null);  
  65.             return;  
  66.         } catch (Error e) {  
  67.         } catch (Throwable throwable) {  
  68.             throw new UndeclaredThrowableException(throwable);  
  69.         }  
  70.   
  71.     }  
  72. }  

可以看到,调用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


  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值