黑马程序员技术交流社区

标题: 写一个ArrayList的动态代理求助啊 [打印本页]

作者: 乐此不疲    时间: 2014-7-30 21:02
标题: 写一个ArrayList的动态代理求助啊
本帖最后由 乐此不疲 于 2014-7-31 10:29 编辑
  1. package com.itheima;
  2. import java.io.Serializable;
  3. import java.lang.reflect.InvocationHandler;
  4. import java.lang.reflect.Method;
  5. import java.lang.reflect.Proxy;
  6. /** 写一个ArrayList类的代理,实现和ArrayList中完全相同的功能,并可以计算每个方法运行的时间。
  7. *
  8. */
  9. import java.util.*;

  10. public class Test2 {

  11.         public static void main(String[] args) {
  12.                 ArrayImplInte myArray = new MyArray();
  13.                 InvocationHandler handler = new DynamicProxy(myArray);
  14.                 //调用newProxyInstance方法得到一个动态代理对象
  15.                 ArrayImplInte proxy = (ArrayImplInte)Proxy.newProxyInstance(
  16.                                 ArrayImplInte.class.getClassLoader(),
  17.                                 new Class[]{ArrayImplInte.class},
  18.                                 handler
  19.                                 );
  20.                 //测试代理对象
  21.                 System.out.println(proxy.getClass().getName());
  22.                 proxy.add("shadow");
  23.                 System.out.println(proxy.get(0));
  24.         }

  25. }
  26. class DynamicProxy implements InvocationHandler{

  27.         private Object arrayProxy ;
  28.         DynamicProxy(Object obj){
  29.                 this.arrayProxy = obj;
  30.         }
  31.         @Override
  32.         public Object invoke(Object obj, Method method, Object[] args)
  33.                         throws Throwable {
  34.                 System.out.println("method:"+method.getName());
  35.                 long beginTime = System.currentTimeMillis();
  36.                 //执行相应的方法
  37.                 method.invoke(arrayProxy, args);
  38.                 //方法花费的时间
  39.                 long costTime = System.currentTimeMillis() - beginTime;
  40.                 System.out.println("方法运行时间:"+costTime);
  41.                 return null;
  42.         }
  43.         
  44. }//定义一个类继承ArrayList实现实现所有接口的 ArrayImplInte
  45. class MyArray extends ArrayList implements ArrayImplInte{        
  46. }
  47. //定义一个接口继承ArrayList所实现的所有接口
  48. interface ArrayImplInte<E> extends Serializable, Cloneable, Iterable<E>, Collection<E>, List<E>, RandomAccess{        
  49. }
复制代码
控制台的信息是:
Exception in thread "main" java.lang.NullPointerException
        at com.itheima.$Proxy0.add(Unknown Source)
        at com.itheima.Test2.main(Test2.java:24)
com.itheima.$Proxy0
method:add
方法运行时间:0
24行内容:proxy.add("shadow");    为什么会出现空指针异常
麻烦各位了!
楼主在各位大牛同学的帮助下已找到错误并解决,谢谢了!
作者: fantacyleo    时间: 2014-7-30 22:14
问题出在你重写的InvocationHandler的invoke方法返回了null。  add方法本来返回的是boolean,属于基本类型,而invoke方法的返回值类型的Object,因此会JVM会尝试将Object拆箱为boolean。而对null拆箱就会抛空指针异常
作者: 357016138    时间: 2014-7-30 22:20
写的有点多了,可以参照高新技术里面的第三种方式写代码!
作者: 乐此不疲    时间: 2014-7-30 22:27
fantacyleo 发表于 2014-7-30 22:14
问题出在你重写的InvocationHandler的invoke方法返回了null。  add方法本来返回的是boolean,属于基本类型 ...

非常感谢,本来准备@你来着,但论坛不知道怎么弄或者没这个功能。
看来还是我对handler这个理解不够,再去脑补下
作者: 乐此不疲    时间: 2014-7-30 22:28
357016138 发表于 2014-7-30 22:20
写的有点多了,可以参照高新技术里面的第三种方式写代码!

那我去看一看
作者: fantacyleo    时间: 2014-7-30 22:39
乐此不疲 发表于 2014-7-30 22:27
非常感谢,本来准备@你来着,但论坛不知道怎么弄或者没这个功能。
看来还是我对handler这个理解不够,再 ...

不用客气。你可以看一下《Java编程思想》中对动态代理的讲解。它是从静态代理(就是直接在源文件中写好代理类而非运行时让JVM生成)切入,过渡到动态代理的,我觉得更容易理解
作者: a6511631    时间: 2014-7-31 09:36
这个,关于动态代理,有一点需要注意的是:代理类的返回类型应当与目标类的返回类型一致。
有个例子。你看看,这里它调用时rent()、hello()的方法,目标类中返回值就都是void,所以InvocationHandler中invoke()方法返回null没问题。
例子:
首先我们定义了一个Subject类型的接口:
  1. public interface Subject
  2. {
  3.     public void rent();
  4.    
  5.     public void hello(String str);
  6. }
复制代码

定义了一个类来实现这个接口,这个类就是我们的真实对象,RealSubject类:
  1. public class RealSubject implements Subject
  2. {
  3.     @Override
  4.     public void rent()
  5.     {
  6.         System.out.println("I want to rent my house");
  7.     }
  8.    
  9.     @Override
  10.     public void hello(String str)
  11.     {
  12.         System.out.println("hello: " + str);
  13.     }
  14. }
复制代码

定义一个动态代理类了,前面说个,每一个动态代理类都必须要实现 InvocationHandler 这个接口,因此我们这个动态代理类也不例外:
  1. public class DynamicProxy implements InvocationHandler
  2. {
  3.     // 这个就是我们要代理的真实对象
  4.     private Object subject;
  5.    
  6.     //    构造方法,给我们要代理的真实对象赋初值
  7.     public DynamicProxy(Object subject)
  8.     {
  9.         this.subject = subject;
  10.     }
  11.    
  12.     @Override
  13.     public Object invoke(Object object, Method method, Object[] args)
  14.             throws Throwable
  15.     {
  16.         //  在代理真实对象前我们可以添加一些自己的操作
  17.         System.out.println("before rent house");
  18.         
  19.         System.out.println("Method:" + method);
  20.         
  21.         //    当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
  22.         method.invoke(subject, args);
  23.         
  24.         //  在代理真实对象后我们也可以添加一些自己的操作
  25.         System.out.println("after rent house");
  26.         
  27.         return null;
  28.     }

  29. }
复制代码


最后,来看看Client类:
  1. public class Client
  2. {
  3.     public static void main(String[] args)
  4.     {
  5.         //    我们要代理的真实对象
  6.         Subject realSubject = new RealSubject();

  7.         //    我们要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法的
  8.         InvocationHandler handler = new DynamicProxy(realSubject);

  9.         /*
  10.          * 通过Proxy的newProxyInstance方法来创建我们的代理对象,我们来看看其三个参数
  11.          * 第一个参数 handler.getClass().getClassLoader() ,我们这里使用handler这个类的ClassLoader对象来加载我们的代理对象
  12.          * 第二个参数realSubject.getClass().getInterfaces(),我们这里为代理对象提供的接口是真实对象所实行的接口,表示我要代理的是该真实对象,这样我就能调用这组接口中的方法了
  13.          * 第三个参数handler, 我们这里将这个代理对象关联到了上方的 InvocationHandler 这个对象上
  14.          */
  15.         Subject subject = (Subject)Proxy.newProxyInstance(handler.getClass().getClassLoader(), realSubject
  16.                 .getClass().getInterfaces(), handler);
  17.         
  18.         System.out.println(subject.getClass().getName());
  19.         subject.rent();
  20.         subject.hello("world");
  21.     }
  22. }
复制代码

作者: a6511631    时间: 2014-7-31 09:43
如果要简写的话,像直接用匿名内部类new InvocationHandler{...}的话,就直接传一个final修饰的目标进去好了final Object subject。
作者: 草鱼狂飙    时间: 2014-7-31 09:56
45行返回值有问题了,需要返回值的。
我这里有自己理解的代码,我贴出来吧
  1. public class CreatProxy {
  2.         public static void main(String[] args) throws Exception,
  3.                         NoSuchMethodException {
  4.                 Class clazzproxy = Proxy.getProxyClass(Collection.class
  5.                                 .getClassLoader(), Collection.class);
  6.                 Constructor constructor = clazzproxy
  7.                                 .getConstructor(InvocationHandler.class);
  8.                 class MyInvocationHandler implements InvocationHandler{
  9.                         @Override
  10.                         public Object invoke(Object proxy, Method method, Object[] args)
  11.                                         throws Throwable {
  12.                                 // TODO Auto-generated method stub
  13.                                 return null;
  14.                         }
  15.                 }
  16.                 Collection proxy1= (Collection) constructor.newInstance(new MyInvocationHandler());
  17.                
  18.                 Collection proxy2= (Collection) constructor.newInstance(new InvocationHandler(){
  19.                         @Override
  20.                         public Object invoke(Object proxy, Method method, Object[] args)
  21.                                         throws Throwable {
  22.                                 // TODO Auto-generated method stub
  23.                                 return null;
  24.                         }
  25.                 });
  26.                 Collection proxy3=(Collection)Proxy.newProxyInstance(Collection.class.getClassLoader(),
  27.                                 new Class[]{Collection.class},
  28.                                 new InvocationHandler(){
  29.                         @Override
  30.                         public Object invoke(Object proxy, Method method, Object[] args)
  31.                                         throws Throwable {
  32.                                 // TODO Auto-generated method stub
  33.                                 List target =new  ArrayList();
  34.                                 long begintime =System.currentTimeMillis();
  35.                                 Object retValue=method.invoke(target, args);
  36.                                 long endtime =System.currentTimeMillis();
  37.                                 System.out.println(method.getName()+"  "+(endtime-begintime));
  38.                                 return retValue;
  39.                         }
  40.                 });
  41.                 proxy3.add("dsfgsdf");
  42.                 proxy3.add("dsfgsdf");
  43.                 proxy3.add("dsfgsdf");
  44.                 proxy3.add("dsfgsdf");
  45.                 proxy3.add("dsfgsdf");
  46.                 proxy3.add("dsfgsdf");
  47.                 proxy3.add("dsfgsdf");
  48.        
  49.                
  50.         }
  51. }
复制代码


作者: 乐此不疲    时间: 2014-7-31 10:21
a6511631 发表于 2014-7-31 09:43
如果要简写的话,像直接用匿名内部类new InvocationHandler{...}的话,就直接传一个final修饰的目标进去好 ...

感谢分享哈  看过类似的一篇blog  当时没认真看  导致理解不够深入
作者: 乐此不疲    时间: 2014-7-31 10:27
草鱼狂飙 发表于 2014-7-31 09:56
45行返回值有问题了,需要返回值的。
我这里有自己理解的代码,我贴出来吧

已经搞清楚是handler的返回值问题了   3Q~
作者: ninjaes    时间: 2016-2-26 19:05
最后 楼主怎么解决的
作者: freshnboy    时间: 2016-3-31 17:34
这个题太麻烦了。。继承ArrayList不说,还得先知道ArrayList所实现的所有接口,以确保代理可以“.出”ArrayList的所有方法。。。

不过有个问题啊,如果某个方法是ArrayList特有而不是实现了哪个接口,不就代理不到了嘛!




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2