普通对象所需要完成的任务就是通过公共接口为外界提供自己所承诺的服务。然而,有时候合法的对象可能会因为各种原因而无法完成自己常规的任务。尤其是当对象需要长时候才能载入内存、对象正运行在另一台计算机上或者需要获取对象消息的时候,这种情况会频繁出现。在这种情况下,我们可以使用一个代理对象,由它来承担客户期待的责任,并将请求转发给其背后的目标对象。

Proxy模式的意图在于为对象提供一个代理或者占位来控制该对象的访问.

下面对代理对象的各种情况进行一一分析,它分别有

图像代理,远程代理,动态代理

1)图像代理

    代理通常拥有与实际对象基本相同的接口。代理的工作方式是:把服务请求明智地转发给代理控制的底层对象,最终完成任务。避免将较大的图像加载到内存就是一个典型的例子。假定我们要加载一张较大的图像,我们需要为这张图像建立代理,通过先加载一张显示loading的小图片,然后当大图像已完全加载到内存时,代理通知替换这张loading小图片。

代码如下:

public class ImageProxy implements Runnable{static final ImageIcon BLACK = new ImageIcon("images/black.jpg");static final ImageIcon LOADING = new ImageIcon("images/loading.jpg");protected String filename;protected JFrame jf;ImageIcon currentImage = BLACK;public ImageProxy(String filename){this.filename = filename;}public void load(JFrame jf){this.jf = jf;currentImage = LOADING;jf.repaint();new Thread(this).start();}public void run(){currentImage = new ImageIcon(ClassLoader.getSystemResource(filename));jf.pack();}....
}

代理类ImageProxy通过线程方式,当大图片加载完成时就替换Loading状态的图片

2)远程代理

   如果我们期望调用正在另一台计算机上运行的对象的方法,那么必须找到一种方法来与该远程对象进行通信,而不能直接调用其方法。我们可以在远程机器上打开一个套接字,并设计一种协议用于向该远程对象发送消息。理想情况下,这种方案可以让我们自由地与远程对象进行通信,就像与本地对象进行通信一样。在这种方案下使用代理模式,可直接调用位于本地的代理对象的方法,该代理对象将调用请求发给远程对象。实际上,著名的公共对象请求代理架构(CORBA),ASP.NET以及JAVA 的远程方法调用(RMI)已经实现了这种方案。

在RMI中代理对象用于将调用请求转发给另一台计算机上运行的指定对象,客户可以很容易地获得这种代理对象。企业JavaBeans(EJB)规范是业务新兴的一个重要标准,它的基础之一便是RMI;要理解EJB,必须先了解RMI。无论业界标准如何发展,我们可以预见未来的分布计算仍将离不开代理模式。RMI为代理模式的实践提供了一个很好的例子。

下面我们简单介绍一下它的实现方式:

首先我们的接口必须要继承Remote接口才能够被远程接收的到同时还要扩展UnicastRemoteObject类。

我们的接口定义如下:

/*** 远程代理接口* @author Administrator**/
public interface ProxyRemote extends Remote {public String getName() throws RemoteException;
}

接口实现如下:

/*** 远程代理实现* @author Administrator**/
public class RemoteImpl extends UnicastRemoteObject implements ProxyRemote{private String name;public RemoteImpl(String name) throws RemoteException{this.name = name;}public String getName() {return "remote name is:"+name;}
}

RemoteImpl 运行在一台机器上,为了能够让其它机子访问它,我们需要为RemoteImpl 对象提供一个代理对象。这个代理对象必须实现 Remote接口,并且提供用于与远程对象通信的附加特性。RMI的最大便利之一就是它能够自动的创建这个代理类。为了自动生成代理类,我们必须把RemoteImpl.java 与ProxyRemote.java 接口文件放在RMI注册的运行目录下.

 >javac packagename.RemoteImpl.java>rmic packagename.RemoteImpl                         //RMI编译器,生成用于简化通信的存根

rmic命令编译了它所需要的所有类,并创建 了RemoteImpl_Stub类。

在对象能够被访问之前,我们必须运行在服务器的上的RIM注册程序注册该对象。

>rmiregistry 5000

在端口5000进行监听,注册完之后我们可以开始创建注册RemoteImpl对象.

/*** 注册远程代理* @author Administrator**/
public class RegisterRemote {public static void main(String[] args) {try {RemoteImpl remoteImpl = new RemoteImpl("it's a good test!");Naming.rebind("rmi://localhost:5000/Remote", remoteImpl);System.out.println("register remoteImpl ok");} catch (RemoteException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (MalformedURLException e) {// TODO Auto-generated catch blocke.printStackTrace();}}
}

对象被注册完成之后,我们可以通过其他机子访问并得到这个对象,这里本地我们就用localhost来访问并获得这个对象

/*** 接收远程代理 * @author Administrator**/
public class ShowRegisterClient {public static void main(String[] args) {Object obj;try {obj = java.rmi.Naming.lookup("rmi://localhost:5000/Remote");ProxyRemote proxyRemote = (ProxyRemote)obj;System.out.println("the result is:"+proxyRemote.getName());} catch (MalformedURLException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (RemoteException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (NotBoundException e) {// TODO Auto-generated catch blocke.printStackTrace();}}
}

当这个程序运行的时候,它首先会通过注册名Remote来查找远程对象。该远程对象代表着远程的一个RemoteImpl对象;lookup返回的对象是RemoteImpl_Stub类的一个实例,RemoteImpl_Stub实现了接口ProxyRemote接口,因此我们可以将这个强制转换为ProxyRemote接口的一个对象,从而可以调用它获得数据。

最后结果,屏幕上将会打印如下信息:

the result is:remote name is:it's a good test!

远程对象代理总结:

RMI优点在于它使得客户端程序只需要与本地代理对象进行交互便可达到与远程对象通信的目的,RMI用户定义了客户端与服务器端共享对象的接口。RMI为客户端和服务器端分别提供一个ProxyRemote接口的实现类;这两个实现类相协作,从机时可完成进程间的无缝通信。服务器端和客户端则不必关心这些细节。

3)动态代理

我们可能借助动态代理,使用代理对象来包装其他对象,使用代理对象截获对被包装对象的调用请求,然后代理继续把这些请求转发给被包装对象。在执行被截获的调用之前或者之后,我们可以编写相关的加强代码。

动态代理需要使用对象的类所实现的接口,代理对象可以截获的调用就是这些接口定义的调用。也就是说如果某个类可以实现要截获的接口,动态代理就可以包装这个类的实例。

为了创建动态代理,必须具有要截获的的接口列表,如用如下代理就可以获取这个列表:

Class[] classes = obj.getClass().getInterfaces();

借助于上述代码,我们可以获得希望截获的方法列表,这些方法属于对象的类实现接口。为构建动态代理,还需要:类加载器,以及包含当代理捕获某些调用时你希望执行的行为的类。对于接口列表,通过使用与希望包装的对象相关的对象,就可以获合适的类加载器。

ClassLoader loader = obj.getClass().getClassLoader();

最后一个就是代理对象本身,这个对象必须是实现java.lang.reflect包中的InvocationHandler接口的类实例,接口声明如下:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable ;

在动态代理中馐某对象时,对被包装对象的调用会转发给动态代理对象的invoke(),invoke()方法会继续把这个调用转发给被包装对象,可以使用如下进行转发调用:

result = m.invoke(obj,args);

下面举出一个例子添加一些对象所需要的时候,如下在添加这个对象花费太多时间时就把这个对象打印出来.

public class BadPro {private String name;public BadPro(String name) {this.name = name;}@Overridepublic int hashCode() {// TODO Auto-generated method stubtry {Thread.sleep(20);} catch (Exception e) {// TODO: handle exceptione.printStackTrace();}return super.hashCode();}public String toString(){return name;}
}
public class GoodPro {private String name;public GoodPro(String name) {this.name = name;}@Overridepublic int hashCode() {return super.hashCode();}public String toString(){return name;}
}
/*** 动态代理* @author Administrator**/
public class ImpatientProxy implements InvocationHandler {private Object obj;private ImpatientProxy(Object obj){this.obj = obj;}public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object result;long t1 = System.currentTimeMillis();result = method.invoke(obj, args);long t2 = System.currentTimeMillis();if(t2-t1>10){System.out.println(">It takes"+(t2-t1)+" millions to invoke with:"+method.getName());for(int i =0;i<args.length;i++){System.out.println("> arg["+i+"]"+"   name:"+args[i]+"   class name:"+args[i].getClass().getName());}}return result;}public static Object newInstance(Object obj){ClassLoader loader = obj.getClass().getClassLoader();Class[] classes = obj.getClass().getInterfaces();return Proxy.newProxyInstance(loader, classes, new ImpatientProxy(obj));}public static void main(String[] args) {Set s = new HashSet();s = (Set)ImpatientProxy.newInstance(s);s.add(new BadPro("BadPro"));s.add(new GoodPro("GoodPro"));s.add(new Object());System.out.println("the set contains :"+s.size()+" things.");}}

这个类实现invoke方法,以检查被包装对象执行某调用操作所花费的时间太长时,会打印报警信息。

为保证ImpatientProxy对象有效,需要使用Proxy类来简化动态代理的创建过程,所以我们加入了newInstance方法:

 public static Object newInstance(Object obj){ClassLoader loader = obj.getClass().getClassLoader();Class[] classes = obj.getClass().getInterfaces();return Proxy.newProxyInstance(loader, classes, new ImpatientProxy(obj));}

能够在被截获方法调用执行前和执行后创建其他有意义的操作是面向方面编辑(AOP)的念之一。在AOP中,方法aspect就是建议和切入点的组合。

在JAVA中,动态代理技术使你能够使用代理对象包装其他对象,截获被包装对象的调用,在调用传递前后增加其他操作等,这样可以比较随意地给任何对象增加可复用的行为,从这点来讲,与AOP非常类似。

小结:

代理模式的实现要求建立一个占位对象,用于控制对目标对象的访问, 这样客户端就无需了解目标对象的状态变化。就像加载一个图像需要一定时间时,我们可能使用代理模式改善用户体验。但代理模式本身存在代理对象与被代理对象之间的偶合程序过紧的问题。在JAVA中动态代理有进可以提供一种增加可复用功能的机制。如果某对象的类可实现要截获的接口,可以使用动态代理包装该对象,增加自己的处理逻辑,以增加或者替换被包装对象代码的功能。

参与资料:Java设计模式

[美] Steven John Metsker

William C.Wake

设计模式之略见一斑(代理模式Proxy)相关推荐

  1. Java24种设计模式(第二种)--代理模式(Proxy Pattern)

    Java24种设计模式 (第二种) 一.代理模式(Proxy Pattern) 模式逻辑: 什么是代理模式呢?我很忙,忙的没空理你,那你要找我呢就先找我的代理人吧,那代理人总要知道 被代理人能做哪些事 ...

  2. [设计模式-结构型]代理模式(Proxy)

    概括 名称 Proxy 结构 动机 为其他对象提供一种代理以控制对这个对象的访问. 适用性 在需要用比较通用和复杂的对象指针代替简单的指针的时候,使用P r o x y 模式.下面是一 些可以使用P ...

  3. 二十三种设计模式(第十二种)-----代理模式(Proxy)

    二十三种设计模式(第十二种)-----代理模式(Proxy) 尚硅谷视频连接https://www.bilibili.com/video/BV1G4411c7N4?from=search&se ...

  4. 设计模式——代理模式(Proxy Pattern)之为别人做嫁衣

    代理模式Proxy Pattern 代理模式 1.背景 2.定义 3.特征 4.应用场景 5.实验案例 参考 代理模式 1.背景 假如说我现在想租一间房子,虽然我可以自己去找房源,做卫生检测等一系列的 ...

  5. 设计模式-代理模式(Proxy Pattern)

    设计模式-代理模式(Proxy Pattern) 文章目录 设计模式-代理模式(Proxy Pattern) 一.定义 二.概念解释 三.场景 四.实现 1.类图 2.代码实现 五.小结 六.动态代理 ...

  6. js设计模式——代理模式proxy

    什么是代理模式 代理模式是为一个对象提供一个代用品或占位符,以便控制对它的访问. (可以想象一下明星与经纪人的关系,明星是请求的本体,经纪人就是代理proxy) 如何实现代理模式 代理对象内部含有对本 ...

  7. 二十四种设计模式:代理模式(Proxy Pattern)

    代理模式(Proxy Pattern) 介绍 为其他对象提供一个代理以控制对这个对象的访问. 示例 有一个Message实体类,某对象对它的操作有Insert()和Get()方法,用一个代理来控制对这 ...

  8. 设计模式之蝉——代理模式上中

    代理模式的扩展 1 普通代理 :这种代理就是客户端只能访问代理角色,而不能访问真实角色.与设计模式之蝉--代理模式上 片基本差不多. (1)Subject抽象主题角色: (2)RealSubject具 ...

  9. 代理模式(Proxy)简介

    代理模式相对来讲比较简单和容易理解. 一, 代理模式(Proxy)的定义 教材里定义很简单: 所谓代理模式, 就是为其他对象提供一种代理控制对这个对象的访问. 上面出现了3个名词: 1.其他对象 2. ...

最新文章

  1. python编写用户输入的是q么代码_Python课 #01号作业
  2. C# 线程的定义和使用
  3. Git中的日常使用 码云
  4. KubeCon 2018 参会记录 —— FluentBit Deep Dive
  5. 阿里云linux服务器到期后续费,网站打不开解决方法之一
  6. 并发编程之美(1)并发编程基础
  7. 设计模式之组合模式(十四)
  8. idea的阿里代码规范检查
  9. itools电脑显示服务器维护,win10系统iTools无法打开且服务无法启动的具体技巧
  10. iPhone游戏开发纵谈
  11. [译] 海量视频时代下的内容发现之旅
  12. python编码及初体验
  13. [总结]视频质量评价技术零基础学习方法
  14. 如何在 Excel VBA 中插入行
  15. TCP通信转HTTP桥接器(转发zabbix数据为例)
  16. JAVA线上故障排查全套路
  17. ValueError: [E030] Sentence boundaries unset. You can add the 'sentencizer' component to the pipelin
  18. 《移动软件开发》实验1:第一个微信小程序 实验报告
  19. IT市场:英文求职信模板二
  20. 【netcore基础】wwwroot下静态资源文件访问权限控制

热门文章

  1. 基于ADC0832模数转换+数码管/LCD显示+proteus仿真
  2. 云Ubuntu 20.04 系统下 erpnext v13 安装部署(图文并茂按步骤基本成功)
  3. Word 2016 交叉引用 章节的题注与交叉引用
  4. 知乎进阶玩法:打造精准被动引流管道
  5. Win10查看图片dpi。将低dpi的改为高dpi格式。
  6. 为什么宝宝腹泻就一定要用中性乳糖酶?
  7. dos winrar压缩文件
  8. google原生 u7_安卓7.0有惊喜?谷歌原生相机或集成AR功能
  9. 时钟日历c语言源代码,日历时钟DS12887或146818的C语言源程序.doc
  10. CAD 实验4 二维图形变换