前言:java
代理模式做为常见的设计模式之一,在项目开发中不可或缺。本文就尝试着揭开代理的神秘面纱,也欢迎各路人批评指正!设计模式
【假设有个关于汽车移动(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 }
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 }
客户端代码:工具
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
【假设如今特殊需求不肯定:“汽车移动以前先往左仍是先往右”】
很明显,咱们此时若使用继承的方式实现代理,则后续很不容易维护,并且会造成臃肿的继承链;但使用接口的方式咱们发现仅须要两个代理类: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 }
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 }
客户端代码:
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...
========================
其实,无论是继承仍是组合的方式,咱们上面实现的都仅仅是“静态代理”,也是咱们平时用的比较多的。如今,咱们开始聊聊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有深度学识的朋友拍砖,谢谢~