代理模式
1 概述
- 代理模式(Proxy Pattern)是一种结构型设计模式,它为其他对象提供一种代理以控制对这个对象的访问。代理模式通过引入代理对象来间接访问目标对象,从而增强了对目标对象的控制。
- 代理模式可以分为多种类型,例如静态代理、动态代理、远程代理、虚拟代理、保护代理等,具体类型取决于代理对象的用途。
- 静态代理:工程师编辑代理类代码,实现代理模式;在编译期就生成了代理类。
- 基于 JDK 实现动态代理:通过 jdk 提供的工具方法 Proxy.newProxyInstance 动态构建全新的代理类(继承 Proxy 类,并持有 InvocationHandler 接口引用)字节码文件并实例化对象返回。(jdk 动态代理是由 java 内部的反射机制来实例化代理对象,并代理的调用委托类方法)。
2 优缺点及应用场景
2.1 优点
- 1)控制访问:代理模式可以在不修改目标对象的前提下,控制对目标对象的访问。
- 2)增强功能:代理对象可以在调用目标对象之前或之后,添加额外的功能。
- 3)延迟实例化:通过使用虚拟代理,可以在需要时才创建目标对象,实现延迟实例化。
2.2 缺点
- 1)增加系统复杂性:代理模式引入了额外的代理对象,增加了系统的复杂性。
- 2)可能影响性能:由于增加了一层间接访问,代理模式可能会影响系统的性能。
2.3 应用场景
- 1)Android 的 Binder、AIDL 等。
- 2)AOP:使用代理模式来实现面向切面编程。
- 3)远程代理:为一个位于不同地址空间的对象提供局部代表,以控制对这个远程对象的访问。
- 4)虚拟代理:根据需要创建开销很大的对象,通过代理延迟实例化。
- 5)保护代理:控制对目标对象的访问,提供不同级别的使用权限。
- 6)智能指引:在访问对象时执行一些附加操作,例如日志记录、性能统计等。
3 结构
- 1)抽象主题(Subject):定义目标对象和代理对象的接口。
- 2)真实主题(RealSubject):实现抽象主题的类,定义了代理对象所代表的真实对象。
- 3)代理(Proxy):实现抽象主题的类,包含对真实主题实例的引用,并通过调用真实主题的方法来实现控制访问。
4 实现
4.1 UML 类图

4.2 代码示例
4.2.1 静态代理代码示例
interface Image {
void display();
}
class RealImage implements Image {
private String fileName;
public RealImage(String fileName) {
this.fileName = fileName;
loadFromDisk(fileName);
}
@Override
public void display() {
System.out.println("Displaying " + fileName);
}
private void loadFromDisk(String fileName) {
System.out.println("Loading " + fileName);
}
}
class ProxyImage implements Image {
private RealImage realImage;
private String fileName;
public ProxyImage(String fileName) {
this.fileName = fileName;
}
@Override
public void display() {
if (realImage == null) {
realImage = new RealImage(fileName);
}
realImage.display();
}
}
public class ProxyPatternDemo {
public static void main(String[] args) {
Image image = new ProxyImage("test_10mb.jpg");
image.display();
System.out.println("");
image.display();
}
}
Loading test_10mb.jpg
Displaying test_10mb.jpg
Displaying test_10mb.jpg
4.2.2 基于 JDK 动态代理代码示例
interface Image {
void display();
}
class RealImage implements Image {
private String fileName;
public RealImage(String fileName) {
this.fileName = fileName;
loadFromDisk(fileName);
}
@Override
public void display() {
System.out.println("Displaying " + fileName);
}
private void loadFromDisk(String fileName) {
System.out.println("Loading " + fileName);
}
}
class ImageInvocationHandler implements InvocationHandler {
private RealImage realImage;
private String fileName;
public ImageInvocationHandler(String fileName) {
this.fileName = fileName;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (realImage == null) {
realImage = new RealImage(fileName);
}
return method.invoke(realImage, args);
}
}
public class ProxyPatternDemo {
public static void main(String[] args) {
Image image = (Image) Proxy.newProxyInstance(
Image.class.getClassLoader(),
new Class[]{Image.class},
new ImageInvocationHandler("test_10mb.jpg")
);
image.display();
System.out.println("");
image.display();
}
}
Loading test_10mb.jpg
Displaying test_10mb.jpg
Displaying test_10mb.jpg
5 总结
- 代理模式通过引入代理对象来控制对目标对象的访问,提供了一种间接访问目标对象的方式。代理模式可以增强对目标对象的控制,添加额外功能,并实现延迟实例化。尽管代理模式增加了系统的复杂性,但它在控制访问、增强功能和延迟实例化等方面具有重要的应用价值。在实际应用中,需要根据具体需求选择合适的代理类型,以充分发挥代理模式的优势。