黑马程序员技术交流社区

标题: 关于动态代理的问题 [打印本页]

作者: 杨丽静    时间: 2013-12-4 10:26
标题: 关于动态代理的问题
本帖最后由 杨丽静 于 2014-1-7 11:11 编辑

谁能清晰的讲解下动态代理,看了好久的视频太模糊,不能理解
作者: cuilitang    时间: 2013-12-4 12:03
本帖最后由 cuilitang 于 2013-12-4 13:51 编辑

先好好看明白代理模式,把代理模式的三个角色搞明白,然后再把反射搞明白,再去看动态代理,就很简单了。
动态代理就是把代理模式中的代理角色用反射来实现:
    1.代理类要实现InvocationHandler接口,重写Invoke()方法
    2.在构造里面,传进来一个真实角色,做成员变量。在代理类的构造里面传进去一个真实角色的对象。这些跟代理模式都是一样的
    3.重写Invoke()方法:将代理对象,Method对象,方法的参数传进去,然后将方法重写就可以了。

主函数调用的时候 也是跟代理模式不同的:
    1.得到一个真实角色,并得到他的Class对象
    2.得到一个InvocationHandler对象
    3.得到一个Proxy对象,并调用方法,程序会去调用InvocationHandler的invoke()方法,这一点跟线程中Thread.start()就去调用Runnable的run()方法情况类似

  1. package cn.cuige.dynamicProxy;

  2. //接口
  3. public interface Homework {
  4.         public void doHomework();

  5. }
复制代码
  1. package cn.cuige.dynamicProxy;
  2. //真实角色
  3. public class IHomework implements Homework{

  4.         @Override
  5.         public void doHomework() {
  6.                 System.out.println("自己在做家庭作业");
  7.                
  8.         }
  9. }
复制代码
  1. package cn.cuige.dynamicProxy;

  2. //代理角色
  3. import java.lang.reflect.InvocationHandler;
  4. import java.lang.reflect.Method;

  5. public class ProxyMan implements InvocationHandler{
  6.         Object obj = null;
  7.         
  8.         ProxyMan(Object obj){  //在构造中接受一个真实角色
  9.                 this.obj = obj;
  10.         }

  11.         @Override
  12.         public Object invoke(Object proxy, Method method, Object[] args)
  13.                         throws Throwable {
  14.                 method.invoke(obj);
  15.                 helpDoHomeWork();
  16.                
  17.                
  18.                
  19.                 return null;
  20.         }
  21.         
  22.         private void helpDoHomeWork() {
  23.                 System.out.println("代理在帮忙做作业");

  24.         }

  25. }
复制代码
  1. package cn.cuige.dynamicProxy;
  2. //测试程序
  3. import java.lang.reflect.Proxy;

  4. public class Test {
  5.         public static void main(String[] args) {
  6.                 //得到一个真实角色,并得到他的Class对象
  7.                 IHomework i = new IHomework();
  8.                 Class<?> iclass = i.getClass();
  9.                
  10.                 //得到一个InvocationHandler对象
  11.                 ProxyMan pm = new ProxyMan(i);
  12.                
  13.                 //得到一个Proxy对象
  14.                 Homework proxyobj = (Homework)Proxy.newProxyInstance(iclass.getClassLoader(),
  15.                                 iclass.getInterfaces(), pm);
  16.                 proxyobj.doHomework();
  17.                
  18.                
  19.         }

  20. }
复制代码


总体上来说,这个东西不难,主要是牵扯的类多,不直观,跟静态代理不同的两点也就是难点就是:
    1.代理类的理解,首先要搞一个InvocationHandler的子类,然后在主函数里面通过Poxy.newInstance()方法得到代理 对象。代理对象不像代理模式对象直接new就可以,说到底还是反射得到对象方式不同。
    2.主函数的调用流程,这个多写几遍就记住了,主要还是反射知识的学习。

作者: QQ被盗    时间: 2013-12-4 13:45
要明白动态代理,就需要先明白静态代理模式
下面是我写的静态代理
  1. //静态代理

  2. //作为委托类的实现接口
  3. public interface BookAuthor {
  4.      public void sale();
  5. }

  6. //书店帮作者卖书,即代理作者卖书
  7. //所以代理者也有与作者相同的方法
  8. public class BookStoreProxy implements BookAuthor {

  9.      //既然是代理作者卖书,则要有作者想把书给代理者,需要有个作者的对象,代理者帮作者进行卖书操作
  10.      JavaBookAuthor bkAuthor;
  11.      public BookStoreProxy(JavaBookAuthor bkAuthor){
  12.            this.bkAuthor = bkAuthor;
  13.      }
  14.      @Override
  15.      public void sale() {
  16.            //在卖书前,代理者获得作者的图书买卖权
  17.           System. out.println("作者,你给我的书我正在帮你卖" );
  18.            this.bkAuthor .sale();
  19.            //在卖完书后,代理者与作者可能还要进行一些操作
  20.           System. out.println("作者,我帮你卖完了书,你是不是要给代理费给我呢" );
  21.      }

  22. }


  23. //作者卖自己的书
  24. public class JavaBookAuthor implements BookAuthor {

  25.      @Override
  26.      public void sale() {
  27.           System. out.println("出售java书,哪个要买" );
  28.      }

  29. }


  30. public class Client {
  31.      public static void main(String[] args) {
  32.            //先要有一个委托类(作者)
  33.           JavaBookAuthor bkAuthor = new JavaBookAuthor();
  34.            //再有个代理类(书店),因为要帮作者处理事情,所以要有作者的相关介入,否则没书卖,或者卖了没钱得
  35.           BookStoreProxy bkStoreProxy = new BookStoreProxy(bkAuthor);
  36.            //代理者帮作者卖书
  37.           bkStoreProxy.sale();
  38.      }
  39. }

复制代码









作者: QQ被盗    时间: 2013-12-4 13:48
明白了静态代理后,动态代理就很好理解了,动态代理只是在静态代理中加入了反射进制

下面是我通过静态代理,来理解动态代理的笔记

在java中的jdk中与动态代理相关的一个接口(InvocationHandler)和一个类(Proxy)
1.InvocationHandler接口
          java.lang.reflect.InvocationHandler
    这个接口是代理对象需要实现的接口,即作为代理的处理器,通过这个接口中的Invoke()方法来调用被代理对象中的方法
[size=-1]Object
[url=mk:java/lang/reflect/InvocationHandler.html#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])]invoke[/url](Object proxy, Method method, Object[] args)
在代理实例上处理方法调用并返回结果。


          参数:proxy:指的是实现InvocationHandler接口的实例
                    method:是通过反射获得委托类实现的接口中的方法,即代理类要处理的方法对象
                    args:是通过反射获得委托类实现的接口中的方法,如果方法有参数则用这个数组接收,如果没有参数则为null.

2.Proxy类
     java.lang.reflect.Proxy.Proxy  一定要记得是反射下的Proxy而不是net下的Proxy
这个类的构造方法是受保护的,只有通过这个类中的newProxyInstance()方法来获得被代理的对象的实例
[size=-1]staticObject
[url=mk:java/lang/reflect/Proxy.html#newProxyInstance(java.lang.ClassLoader, java.lang.Class[], java.lang.reflect.InvocationHandler)]newProxyInstance[/url](ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。

参数:loader - 通过反射机制获取代理对象或代理类的类加载器及ClassLoaderinterfaces -通过反射机制设置代理类要实现的接口即与委托类相同的实现接口,(委托类对象.getClass().getInterfaces()) ,这里是数组,说明可以接收多个代理处理接口h - 指派方法调用的调用处理程序,即代理的处理器(即实现了InvocationHandler接口的子类对象)
          返回值:
                    Object:返回值必须是要使用委托类实现的接口来接收,而不能使用其子类(委托类)来接收

动态代理中涉及到:(1)委托者实现的接口(2)委托者类(3)代理者处理类(4)代理者

例:

1.委托者实现的接口
          例:

  1. //出售书的接口
  2.                public interface SaleBook {
  3.                 public void saleBook();
  4.                }
复制代码

2.委托者类
          例:

  1. //书的作者出售自己的书,所以实现出售接口,就可以出售书
  2.                public class BookAuthor implements SaleBook {
  3.                 // 书本作者名
  4.                 private String authorName;
  5.                 public BookAuthor(){}
  6.                 public BookAuthor(String authorName){
  7.                 this.authorName = authorName;
  8.                   }
  9.                 @Override
  10.                 public void saleBook() {
  11.                System. out.println("帮我" );
  12.                   }
  13.                 public String getAuthorName() {
  14.                 return authorName ;
  15.                 }
  16.                 public void setAuthorName(String authorName) {
  17.                 this.authorName = authorName;
  18.                 }
  19.                }
复制代码

3.代理者的业务处理类
          例:

  1. //书店帮作者出售书,所以书店作为代理方,作者作为委托方
  2.                public class BookStore implements InvocationHandler{
  3.                     SaleBook saleBook;      
  4.   // 书店指明要为哪个作者出售书      public SaleBook HelpSaleForBookAuthor(SaleBook saleBook){
  5.                      this.saleBook = saleBook;
  6.                      // 这里接收的必须是委托类实现的接口,因为这里返回的是一个代理对象,代理对象$proxy,代理对象在底层实现了与委托类相同的接口,所以可以使用该接口来接收
  7.                      return (SaleBook) Proxy.newProxyInstance(this.getClass().getClassLoader(), saleBook.getClass().getInterfaces(), this );
  8.               }
  9.      
  10.                      private void saleBefore(Method method){
  11.                          System. out.println(method.getName()+"开始" );
  12.                          }
  13.      
  14.                      private void saleAfter(Method method){
  15.                          System. out.println(method.getName()+"结束" );
  16.                          }
  17.      
  18.                      // 书店对出售书进行处理,如:加价(比作者买贵些)等操作
  19.                      // 这个方法在该对象实例化时,不会去自动调用,而是在使用了代理后就会去调用
  20.                      @Override
  21.                      public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {
  22.          
  23.                           this.saleBefore(method);
  24.                           // 反射中method调用invoke方法去调用真正的那个方法时,必须要传入那个的实例对象才能调用,如这里的saleBook
  25.                     //同时需要传入参数(如果有),没有参数则 args为null
  26.                          method.invoke( this.saleBook , args);
  27.                           this.saleAfter(method);
  28.                           return null ;
  29.                     }

  30.                }

复制代码

4.测试类
          例:

  1. public static void main(String[] args) {
  2.            // 既然是代理,就必须要有一位委托者
  3.           SaleBook saleBook = new BookAuthor("张三" );
  4.            // 同时需要至少需要一位代理者
  5.           BookStore bookStore = new BookStore();
  6.            // 指明代理者代理的委托者,并且返回代理者对象
  7.           SaleBook bookAuthorProxy = bookStore.HelpSaleForBookAuthor(saleBook);
  8.          
  9.            // 代理者调用方法在底层会自动调用invoke方法
  10.           bookAuthorProxy.saleBook();
  11.           }
复制代码

5.测试结果:




作者: QQ被盗    时间: 2013-12-4 13:49
下面是我对动态代理的总体理解图




作者: 杨丽静    时间: 2013-12-6 10:49
我再查查去




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