一篇学会常见的代理模式

开发 前端
相信大家都听过代理模式,有静态代理,JDK动态代理,Cglib代理(Spring的内容)。接下来,千羽和大家一起学习一下这些代理模式各有优缺点和相应的使用场景。

1. 代理模式概述

2. 代理模式的结构与实现

3. 代理模式的应用实例

4. 远程代理

5. 虚拟代理

6. Java动态代理

7. 代理模式的优缺点与适用环境

“Github:https://github.com/nateshao/design-demo/tree/main/JavaDesignPatterns/15-proxy

1. 代理模式概述

相信大家都听过代理模式,有静态代理,JDK动态代理,Cglib代理(Spring的内容)。接下来,千羽和大家一起学习一下这些代理模式各有优缺点和相应的使用场景。

商品代购示意图:

分析

  • 代购商品:顾客 -> 代购网站 -> 商品
  • 软件开发:客户端 -> 代理对象 -> 真实对象

还有这种类型

定义:

代理模式:给某一个对象提供一个代理或占位符,并由代理对象来控制对原对象的访问。

  1. 引入一个新的代理对象
  2. 代理对象在客户端对象和目标对象之间起到中介的作用
  3. 去掉客户不能看到的内容和服务或者增添客户需要的额外的新服务

代理模式的结构

代理模式包含以下3个角色:

  • Subject(抽象主题角色)
  • Proxy(代理主题角色)
  • RealSubject(真实主题角色)

2. 代理模式的结构与实现

抽象主题类典型代码:

  1. public abstract class Subject { 
  2.  public abstract void request(); 

真实主题类典型代码:

  1. public class RealSubject extends Subject{ 
  2.     public void request() { 
  3.           //业务方法具体实现代码 
  4.     } 

代理类典型代码:

  1. public class Proxy extends Subject { 
  2.     private RealSubject realSubject = new RealSubject();  //维持一个对真实主题对象的引用  
  3.     public void preRequest() { 
  4.         …... 
  5.     } 
  6.   
  7.     public void request() { 
  8.         preRequest(); 
  9.         realSubject.request();  //调用真实主题对象的方法 
  10.         postRequest(); 
  11.     } 
  12.   
  13.     public void postRequest() { 
  14.         …… 
  15.     } 

几种常见的代理模式

  • 远程代理(Remote Proxy):为一个位于不同的地址空间的对象提供一个本地的代理对象,这个不同的地址空间可以在同一台主机中,也可以在另一台主机中,远程代理又称为大使(Ambassador)
  • 虚拟代理(Virtual Proxy):如果需要创建一个资源消耗较大的对象,先创建一个消耗相对较小的对象来表示,真实对象只在需要时才会被真正创建
  • 保护代理(Protect Proxy):控制对一个对象的访问,可以给不同的用户提供不同级别的使用权限
  • 缓冲代理(Cache Proxy):为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果
  • 智能引用代理(Smart Reference Proxy):当一个对象被引用时,提供一些额外的操作,例如将对象被调用的次数记录下来等

3. 代理模式的应用实例

某软件公司承接了某信息咨询公司的收费商务信息查询系统的开发任务,该系统的基本需求如下:

在进行商务信息查询之前用户需要通过身份验证,只有合法用户才能够使用该查询系统;

在进行商务信息查询时系统需要记录查询日志,以便根据查询次数收取查询费用。

该软件公司开发人员已完成了商务信息查询模块的开发任务,现希望能够以一种松耦合的方式向原有系统增加身份验证和日志记录功能,客户端代码可以无区别地对待原始的商务信息查询模块和增加新功能之后的商务信息查询模块,而且可能在将来还要在该信息查询模块中增加一些新的功能。

现使用代理模式设计并实现该收费商务信息查询系统。

实例分析及类图:

商务信息查询系统设计方案示意图

商务信息查询系统结构图

实例代码

  • AccessValidator:身份验证类,业务类
  • Logger:日志记录类,业务类
  • Searcher:抽象查询类,充当抽象主题角色
  • RealSearcher:具体查询类,充当真实主题角色
  • ProxySearcher:代理查询类,充当代理主题角色
  • Client:客户端测试类

结果分析

  • 保护代理和智能引用代理
  • 在代理类ProxySearcher中实现对真实主题类的权限控制和引用计数

4. 远程代理

动机

  1. 客户端程序可以访问在远程主机上的对象,远程主机可能具有更好的计算性能与处理速度,可以快速地响应并处理客户端的请求
  2. 可以将网络的细节隐藏起来,使得客户端不必考虑网络的存在
  3. 客户端完全可以认为被代理的远程业务对象是在本地而不是在远程,而远程代理对象承担了大部分的网络通信工作,并负责对远程业务方法的调用

结构

5. 虚拟代理

动机

对于一些占用系统资源较多或者加载时间较长的对象,可以给这些对象提供一个虚拟代理

在真实对象创建成功之前虚拟代理扮演真实对象的替身,而当真实对象创建之后,虚拟代理将用户的请求转发给真实对象

使用一个“虚假”的代理对象来代表真实对象,通过代理对象来间接引用真实对象,可以在一定程度上提高系统的性能

应用

由于对象本身的复杂性或者网络等原因导致一个对象需要较长的加载时间,此时可以用一个加载时间相对较短的代理对象来代表真实对象(结合多线程技术)

一个对象的加载十分耗费系统资源,让那些占用大量内存或处理起来非常复杂的对象推迟到使用它们的时候才创建,而在此之前用一个相对来说占用资源较少的代理对象来代表真实对象,再通过代理对象来引用真实对象(用时间换取空间)

6. Java动态代理

  • 动态代理(Dynamic Proxy)可以让系统在运行时根据实际需要来动态创建代理类,让同一个代理类能够代理多个不同的真实主题类而且可以代理不同的方法
  • Java语言提供了对动态代理的支持,Java语言实现动态代理时需要用到位于java.lang.reflect包中的一些类

Proxy类

  • public static Class< ? > getProxyClass(ClassLoader loader, Class... interfaces):该方法用于返回一个Class类型的代理类,在参数中需要提供类加载器并需要指定代理的接口数组(与真实主题类的接口列表一致)
  • public static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h):该方法用于返回一个动态创建的代理类的实例,方法中第一个参数loader表示代理类的类加载器,第二个参数interfaces表示代理类所实现的接口列表(与真实主题类的接口列表一致),第三个参数h表示所指派的调用处理程序类

InvocationHandler接口

  • InvocationHandler接口是代理处理程序类的实现接口,该接口作为代理实例的调用处理者的公共父类,每一个代理类的实例都可以提供一个相关的具体调用处理者(InvocationHandler接口的子类)
  • public Object invoke(Object proxy, Method method, Object[] args):该方法用于处理对代理类实例的方法调用并返回相应的结果,当一个代理实例中的业务方法被调用时将自动调用该方法。invoke()方法包含三个参数,其中第一个参数proxy表示代理类的实例,第二个参数method表示需要代理的方法,第三个参数args表示代理方法的参数数组
  • 动态代理类需要在运行时指定所代理真实主题类的接口,客户端在调用动态代理对象的方法时,调用请求会将请求自动转发给InvocationHandler对象的invoke()方法,由invoke()方法来实现对请求的统一处理。

动态代理实例

“某软件公司欲为公司OA系统数据访问层DAO增加方法调用日志,记录每一个方法被调用的时间和调用结果,现使用动态代理进行设计和实现。

实例代码

  • AbstractUserDAO:抽象用户DAO类,抽象主题角色
  • AbstractDocumentDAO:抽象文档DAO类,抽象主题角色
  • UserDAO:用户DAO类,具体主题角色
  • DocumentDAO:文档DAO类,具体主题角色
  • DAOLogHandler:自定义请求处理程序类
  • Client:客户端测试类

7. 代理模式的优缺点与适用环境

模式优点

  • 能够协调调用者和被调用者,在一定程度上降低了系统的耦合度
  • 客户端可以针对抽象主题角色进行编程,增加和更换代理类无须修改源代码,符合开闭原则,系统具有较好的灵活性和可扩展性

模式优点——逐个分析

  • 远程代理:可以将一些消耗资源较多的对象和操作移至性能更好的计算机上,提高了系统的整体运行效率
  • 虚拟代理:通过一个消耗资源较少的对象来代表一个消耗资源较多的对象,可以在一定程度上节省系统的运行开销
  • 缓冲代理:为某一个操作的结果提供临时的缓存存储空间,以便在后续使用中能够共享这些结果,优化系统性能,缩短执行时间
  • 保护代理:可以控制对一个对象的访问权限,为不同用户提供不同级别的使用权限

模式缺点

由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢(例如保护代理)

实现代理模式需要额外的工作,而且有些代理模式的实现过程较为复杂(例如远程代理)

模式适用环境

当客户端对象需要访问远程主机中的对象时可以使用远程代理

当需要用一个消耗资源较少的对象来代表一个消耗资源较多的对象,从而降低系统开销、缩短运行时间时可以使用虚拟代理

当需要为某一个被频繁访问的操作结果提供一个临时存储空间,以供多个客户端共享访问这些结果时可以使用缓冲代理

当需要控制对一个对象的访问,为不同用户提供不同级别的访问权限时可以使用保护代理 

当需要为一个对象的访问(引用)提供一些额外的操作时可以使用智能引用代理

 

责任编辑:武晓燕 来源: 程序员千羽
相关推荐

2024-04-02 12:36:01

2021-05-11 08:54:59

建造者模式设计

2021-07-06 08:59:18

抽象工厂模式

2022-02-07 11:01:23

ZooKeeper

2022-01-02 08:43:46

Python

2021-12-04 22:05:02

Linux

2022-05-17 08:02:55

GoTryLock模式

2022-06-30 22:53:18

数据结构算法

2021-08-01 07:19:16

语言OpenrestyNginx

2022-04-12 08:30:52

回调函数代码调试

2021-07-02 08:51:29

源码参数Thread

2023-03-13 21:38:08

TCP数据IP地址

2021-07-16 22:43:10

Go并发Golang

2021-09-28 08:59:30

复原IP地址

2021-10-14 10:22:19

逃逸JVM性能

2021-10-27 09:59:35

存储

2022-03-11 10:21:30

IO系统日志

2023-11-01 09:07:01

Spring装配源码

2021-10-29 07:35:32

Linux 命令系统

2021-04-29 10:18:18

循环依赖数组
点赞
收藏

51CTO技术栈公众号