神秘代理-Proxy

前言:java

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

1.如何实现代理:

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


1)继承dom

 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)组合
ide

 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

 

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

2.灵活代理-接口切换

【假设如今特殊需求不肯定:“汽车移动以前先往左仍是先往右”】
很明显,咱们此时若使用继承的方式实现代理,则后续很不容易维护,并且会造成臃肿的继承链;但使用接口的方式咱们发现仅须要两个代理类:TurnLeft,TurnRight。并且,无论后续需求如何都只须要作简单的调整。UML图以下:spa

----------设计

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有深度学识的朋友拍砖,谢谢~

相关文章
相关标签/搜索