设计模式 代理模式
本文将介绍设计模式中的代理模式。
一、什么是代理模式?
在某些情况下,不希望或无法直接访问对象,此时便可以使用代理模式,增加一个第三方代理对象,通过它作为中介代为调用。
常见的代理形式有:远程代理、保护代理、虚拟代理、缓冲代理、智能引用代理等。
二、实现
1. 结构
2. 角色
- Subject 抽象主题角色:
- 声明了 Proxy 和 RealSubject 的共同接口
- 客户端针对 Subject 进行编程,从而可以一致地对待 Proxy 和 RealSubject
- Proxy 代理主题角色:
- 包含了对 RealSubject 的引用,因此可以进行 “代理调用”
- 还可以负责对 RealSubject 的创建和删除
- RealSubject 真实主题角色:
- 实现真正的业务操作
3. 简单示例
Subject:
1 |
|
Proxy:
1 |
|
RealSubject:
1 |
|
三、不同代理类型
1. 远程代理
为一个处于不同地址空间的对象提供本地的代理服务。
远程代理可以将网络的细节隐藏起来,使得客户端不必考虑网络的存在。客户端不用关心被代理的远程对象是否在本地,由远程代理对象承担网络通信工作,负责对远程对象的调用。
2. 虚拟代理
如果需要创建一个资源耗费较大的对象,先窜关键一个消耗相对较小的代理对象来表示,待使用时再创建真实对象。
3. 保护代理
控制对一个对象的访问,可以给不同的用户提供不同的使用权限。
4. 缓冲代理
缓存操作的结果以便复用,避免重复操作。
5. 智能引用代理
提供一些额外操作,例如将对象被调用的次数记录下来。
四、动态代理
1. 静态代理和动态代理
传统的代理模式又称静态代理。在静态代理下,代理类需要事先编写,代理类所能代理的真实主题类是固定的,代理类的增加会导致代码中类的增加。
动态代理可以在运行时根据实际需要动态创建代理类,让同一个代理类能够代理多个不同的真实主题类。
动态代理类是一种较为高级的代理模式,它在事务管理、AOP 等领域都发挥了重要的作用。
2. JDK 对动态代理的支持
(1) 获取代理对象
通过 Proxy 获取代理对象,获取方法有以下两种:
需要注意的是:
loader:即类加载器,可以通过
任意对象.getClass().getClassLoader()
获取在没有自定义类加载器的情况下,除了 Java 核心类和 Java 扩展类之外,其它类都由系统类加载器进行加载。因此,使用任意类获取类加载器即可。
具体请看:
handler:调用处理者
InvocationHandler 接口是对调用处理者的抽象,声明了处理代理对象方法调用的方法。
应该实现该接口并书写处理逻辑。如果希望处理时调用被代理的实例,应该维护一个成员变量以存放被代理的实例,在 invoke 方法中通过反射机制调用被代理的实例的方法。
1
2
3
4
5
6
7
8
9
10
11
12public interface InvocationHandler {
/**
* 处理代理对象方法调用
* <p>当代理对象的方法被调用时,将通过此方法进行处理
* @param proxy 代理对象
* @param method 需要代理的方法
* @param args 由方法参数所构成的数组
*/
public Object invoke(Object proxy,
Method method,
Object[] args) throws Throwable;
}
(2) 基本步骤
编写调用处理者类,实现 InvocationHandler 接口,在其中书写处理逻辑
实例化调用处理者类,获取调用处理者
如果希望处理时调用被代理的实例,应该实例化调用处理者类并放入调用处理者
获取代理对象
调用代理对象的方法
当代理对象的方法被调用时,调用请求会自动转发给 InvocationHandler 对象的 invoke 方法,由 invoke 方法对请求做处理
3. 简单示例
接口:
1 |
|
接口实现类:
1 |
|
调用处理类:
1 |
|
代理对象的获取及调用:
1 |
|
4. 通用调用处理类
- 将成员变量的设为 Object 类型,使其可以适用于任意类型
- 提供 getProxy() 方法,可以直接获取代理对象
1 |
|
五、优缺点
1. 优点
- 协调调用者和被调用者,降低系统耦合性
2. 缺点
- 增加了代码量
- 减缓了程序运行速度