zoukankan      html  css  js  c++  java
  • 神秘代理-Proxy

    前言:

      代理模式作为常见的设计模式之一,在项目开发中不可或缺。本文就尝试着揭开代理的神秘面纱,也欢迎各路人批评指正!

    1.如何实现代理:

    【假设有个关于汽车移动(move)的计时需求】
    设计:Moveable接口,一个Car的实现类;两个代理CarTimer,TimeHandler.UML图如下:


    1)继承

     1 package com.gdufe.proxy;
     2 
     3 import java.util.Random;
     4 
     5 public class CarTimer extends Car {
     6     
     7     @Override
     8     public void move() {
     9         long start=System.currentTimeMillis();
    10         super.move();        //调用父类的move()方法
    11         try{
    12             Thread.sleep(new Random().nextInt(10000));
    13         }catch(Exception e){
    14             e.printStackTrace();
    15         }
    16         long end=System.currentTimeMillis();
    17         System.out.println("I'm time machine.Time for moving:"+(end-start));
    18     }
    19 }
    View Code


    2)组合

     1 package com.gdufe.proxy;
     2 
     3 import java.util.Random;
     4 
     5 public class TimeHandler implements Moveable {
     6     private Moveable m;
     7     public TimeHandler(Moveable m) {
     8         this.m = m;
     9     }
    10     @Override
    11     public void move() {
    12         long start=System.currentTimeMillis();
    13         m.move();
    14         try{
    15             Thread.sleep(new Random().nextInt(10000));
    16         }catch(Exception e){
    17             e.printStackTrace();
    18         }
    19         long end=System.currentTimeMillis();
    20         System.out.println("I'm time machine.Time for moving:"+(end-start));
    21     }
    22 
    23 }
    View Code

    客户端代码:

     1 package com.gdufe.proxy;
     2 
     3 public class Client {
     4     public static void main(String[] args) {
     5         System.out.println("继承实现代理:");
     6         new CarTimer().move();
     7         System.out.println("组合实现代理:");
     8         new TimeHandler(new Car()).move();
     9     }
    10 }


    输出结果:

    继承实现代理:
    Car moving...
    I'm time machine.Time for moving:7080
    组合实现代理:
    Car moving...
    I'm time machine.Time for moving:5169

    分析:从上述例子实现当中,我们第一感觉自然是分不出两种代理的实现方式孰优孰劣。且继续往下面看。

    2.灵活代理-接口切换

    【假设现在特殊需求不确定:“汽车移动之前先往左还是先往右”】
    很明显,我们此时若使用继承的方式实现代理,则后续很不容易维护,而且会形成臃肿的继承链;但使用接口的方式我们发现仅需要两个代理类:TurnLeft,TurnRight。而且,不管后续需求如何都只需要做简单的调整。UML图如下:

    ----------

    TurnLeft.java

     1 package com.gdufe.proxy;
     2 
     3 public class TurnLeft implements Moveable {
     4     private Moveable m;
     5     public TurnLeft(Moveable m) {
     6         this.m = m;
     7     }
     8     @Override
     9     public void move() {
    10         System.out.println("turn left...");
    11         m.move();
    12     }
    13 
    14 }
    View Code

    TurnRight.java

     1 package com.gdufe.proxy;
     2 
     3 public class TurnRight implements Moveable {
     4 
     5     private Moveable m;
     6     public TurnRight(Moveable m) {
     7         this.m = m;
     8     }
     9     @Override
    10     public void move() {
    11         System.out.println("turn right");
    12         m.move();
    13     }
    14 
    15 }
    View Code

    客户端代码:

     1 package com.gdufe.proxy;
     2 
     3 public class Client0 {
     4 
     5     /**
     6      * @param args
     7      */
     8     public static void main(String[] args) {
     9         System.out.println("Turn right,then left before moving:");
    10         test1();
    11         
    12         System.out.println("Turn left,then right before moving:");
    13         test2();
    14     }
    15     //对接口的实现内外包装
    16     private static void test1() {
    17         Car car  = new Car();
    18         Moveable m1 = new TurnLeft(car);
    19         Moveable m2 = new TurnRight(m1);
    20         m2.move();
    21     }
    22     public static void test2(){
    23         Car car = new Car();
    24         Moveable m1 = new TurnRight(car);
    25         Moveable m2 = new TurnLeft(m1);
    26         m2.move();
    27     }
    28 
    29 }

    输出结果:

    Turn right,then left before moving:
    turn right
    turn left...
    Car moving...
    Turn left,then right before moving:
    turn left...
    turn right
    Car moving...

    ======================== 

    3.动态代理:

    其实,不管是继承还是组合的方式,我们上面实现的都仅仅是“静态代理”,也是我们平时用的比较多的。现在,我们开始聊聊Java的神器---“动态代理”。
    【假设现在需要实现一个“万能”的日志工具,即不管对任何类的任何方法都可以动态对其进行日志操作】
    例如:上面的例子中,请思考如何实现在汽车移动之前进行日志操作?
    常规的静态代理方式当然可以实现,下面我们就利用Java中的Proxy类进行实现。UML图如下:

    添加关键类:LogHandler.java

     1 package com.gdufe.proxy;
     2 
     3 import java.lang.reflect.InvocationHandler;
     4 import java.lang.reflect.Method;
     5 
     6 public class LogHandler implements InvocationHandler {
     7     
     8     //被代理对象
     9     private Object proxied;
    10     
    11     public LogHandler(Object proxied) {
    12         this.proxied = proxied;
    13     }
    14     @Override
    15     public Object invoke(Object proxy, Method method, Object[] args)
    16             throws Throwable {
    17         System.out.println("I'm log machine.");
    18         return method.invoke(proxied);
    19     }
    20 
    21 }

    客户端代码:

     1 package com.gdufe.proxy;
     2 
     3 import java.lang.reflect.InvocationHandler;
     4 import java.lang.reflect.Proxy;
     5 
     6 public class Client1 {
     7 
     8     /**
     9      * @param args
    10      */
    11     public static void main(String[] args) {
    12         
    13         //单独创建Moveable接口对象
    14         Moveable m=null;
    15         m = new Car();
    16         m = new TimeHandler(m);
    17         
    18         //初始化LogHandler实现的InvacationHandler接口
    19         InvocationHandler h =  new LogHandler(m);
    20         m = (Moveable) Proxy.newProxyInstance(
    21                 Moveable.class.getClassLoader(),
    22                 new Class[] {Moveable.class},h);
    23         
    24         //m在动态代理处理完后,move()方法已被修改
    25         m.move();
    26     }
    27 
    28 }

    输出结果:

    I'm log machine.
    Car moving...
    I'm time machine.Time for moving:110

    分析:

      上述的实现代码对于刚接触Java的朋友来说估计比较费解。要理解其过程,至少对java的反射机制有一定的理解。看穿了的话,其实动态代理的关键环节,就在newProxyInstance()操作上。代码实现的关键步骤:

    Step1:初始化被代理的对象(如上图中的TimeHandler);

    Step2:创建一个实现了InvocationHandler接口的类,在invoke()方法进行你希望执行的代理操作(如上图的LogHandler);

    Step3:将通过Proxy拿到的新对象赋给最终要实现的接口,最后调用该接口方法(如代码中的“m.move()”)。

    ---------------------------------------------------------------------

    有朋友可能犯迷糊了,动态代理的内部实现过程呢?Proxy是怎样一步一步识别到Car的呢?

    请看图:

    注意:$Proxy类是虚拟存在的,在Java API中是找不到的。也就是说,它只存在于中间过程。所以,为方便大家理解就加上了。)

      
      那么,到底动态代理适用于什么情形呢?从上面汽车的简单日志实例也许还难以看出。下面我们再引入一个测试。
    【假设现在增加一个司机(Driver)类,其实现了Speakable接口;很明显他跟汽车没有很直接的关联,那么现在我们利用动态代理的方式将上面的LogHandler加到司机的speak()方法上】
    ----------
    客户端代码:

     1 package com.gdufe.proxy;
     2 
     3 import java.lang.reflect.InvocationHandler;
     4 import java.lang.reflect.Proxy;
     5 
     6 public class Client2 {
     7 
     8     /**
     9      * @param args
    10      */
    11     public static void main(String[] args) {
    12         Moveable m=null;
    13         m = new Car();
    14 
    15         Speakable s =null;
    16         s = new Driver();
    17         
    18         InvocationHandler h = null;
    19         h=new LogHandler(m);
    20         m = (Moveable) Proxy.newProxyInstance(Moveable.class.getClassLoader(),new Class[] {Moveable.class},h);
    21         m.move();
    22         
    23         //司机被代理
    24         h = new LogHandler(s);
    25         s = (Speakable)Proxy.newProxyInstance(Speakable.class.getClassLoader(),new Class[]{Speakable.class}, h);
    26         s.speak();
    27         }
    28 
    29 }

    输出结果:

    I'm log machine.
    Car moving...
    I'm log machine.
    Driver speak...

      在汽车move()跟司机speak()之前,都自动实现LogHandler操作!

    -----------------------------------------------------

    后语:

    通过上述例子,总结动态代理优点:
    ·适用任何类的任何方法;
    ·使用灵活,可随时将代理工具类加入或抽出。

      动态代理是Java语言的精华所在,很多的开发框架都是基于其内部原理。本人目前对动态代理的理解也仅限于此,欢迎对Java有深度学识的朋友拍砖,谢谢~

  • 相关阅读:
    精英程序员
    C语言(2)
    C语言练习
    C语言工具---Code::Blocks
    C语言(1)
    【udacity】机器学习-神经网络
    【udacity】机器学习-回归
    【udacity】机器学习-决策树
    【术语记录】机器学习
    python入门(九):目录操作
  • 原文地址:https://www.cnblogs.com/SeaSky0606/p/4732550.html
Copyright © 2011-2022 走看看