浅析代理模式(转)

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 前言:   代理模式作为常见的设计模式之一,在项目开发中不可或缺。本文就尝试着揭开代理的神秘面纱,也欢迎各路人批评指正! 1.如何实现代理: 【假设有个关于汽车移动(move)的计时需求】 设计:Moveable接口,一个Car的实现类;两个代理CarTimer,TimeHandler.

前言:

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

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 }
复制代码

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 }
复制代码

 

客户端代码:

复制代码
 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 }
复制代码

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...
复制代码

 

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

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

http://www.cnblogs.com/SeaSky0606/p/4732550.html

 

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
23天前
|
设计模式 缓存 C#
C# 一分钟浅谈:装饰者模式与代理模式
【10月更文挑战第12天】本文介绍了面向对象编程中的两种常见设计模式:装饰者模式和代理模式。装饰者模式允许在运行时动态地给对象添加功能,而代理模式则通过代理对象控制对另一个对象的访问。文章详细讲解了这两种模式的概念、常见问题、如何避免问题以及代码示例,帮助读者更好地理解和应用这些设计模式。
35 13
|
4月前
|
设计模式
对于装饰器模式与代理模式的个人理解
摘要: 代理模式与装饰器模式虽相似,但目的不同。装饰器动态增强对象功能,如添加新特性,而不改变原有类。代理模式则用于控制访问,如优化昂贵操作或添加辅助服务(如日志),它可能在内部初始化原对象。用法上,装饰器由外部决定是否应用,允许链式创建,而代理通常内部调用,外部直接与代理交互,被代理对象可能独立不可用。
|
6月前
|
缓存 数据安全/隐私保护 C++
【C++】—— 代理模式
【C++】—— 代理模式
|
6月前
|
设计模式 缓存 安全
设计模式-代理模式(静态代理、动态代理、cglib代理)、代理模式和装饰者模式的区别
设计模式-代理模式(静态代理、动态代理、cglib代理)、代理模式和装饰者模式的区别
代理模式——为他人做嫁衣裳
代理模式——为他人做嫁衣裳
|
6月前
|
设计模式 Java
代理模式与动态代理
代理模式与动态代理
44 0
|
6月前
|
Java Spring
代理模式
代理模式
53 0
|
Java 网络安全 Maven
代理模式的运用
代理模式的运用
53 0
|
设计模式 JavaScript
关于代理模式我所知道的
关于代理模式我所知道的
81 0
|
Java Spring
代理模式你了解多少
代理模式你了解多少
76 0