代理模式(Proxy Pattern)是一种结构型设计模式,它允许你为其他对象提供一种代理以控制对这个对象的访问。代理对象在客户端和目标对象之间起到中介的作用,客户端通过代理对象间接地访问目标对象。通过这种方式,代理模式可以在不修改目标对象的前提下,为目标对象添加额外的功能或控制访问权限。
本文将详细介绍代理模式的概念、种类、优缺点,并通过Java代码示例展示如何在实践中应用代理模式。
一、代理模式的概念
-
定义:代理模式为其他对象提供一种代理,以控制对这个对象的访问。代理对象在客户端和目标对象之间起到中介的作用,客户端通过代理对象间接地访问目标对象。
-
结构:
- Subject:抽象主题角色,声明了代理和真实对象的共同接口。
- RealSubject:真实主题角色,实现了抽象主题接口,是代理对象所代表的真实对象。
- Proxy:代理角色,持有一个真实对象的引用,可以控制对真实对象的访问,也可以附加额外的功能。
-
种类:
- 静态代理:代理类在编译时就确定下来,代理类和目标类的关系在运行前就已经确定,且不可改变。
- 动态代理:代理类在运行时由JVM根据反射机制动态生成,代理类和目标类的关系在运行时确定。
二、静态代理的理解和实践
静态代理通过定义一个代理类,并在代理类中调用目标对象的方法来实现。这种方式需要在编译时就确定代理类。
示例代码:
定义抽象主题接口:
java
// 抽象主题接口
public interface Image {
void display();
}
实现真实主题类:
java
// 真实主题类
public class RealImage implements Image {
private String filename;
public RealImage(String filename) {
this.filename = filename;
loadFromDisk(filename);
}
private void loadFromDisk(String filename) {
System.out.println("Loading " + filename);
}
@Override
public void display() {
System.out.println("Displaying " + filename);
}
}
定义代理类:
java
// 代理类
public 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();
}
}
客户端代码:
java
public class Client {
public static void main(String[] args) {
Image image = new ProxyImage("test.jpg");
// 图像将从磁盘加载并显示
image.display();
}
}
在这个示例中,ProxyImage
是 RealImage
的代理类,它持有一个 RealImage
的引用,并在需要时才加载真实的图像对象。通过这种方式,可以在不修改 RealImage
的前提下,为其添加懒加载功能。
三、动态代理的理解和实践
动态代理通过Java反射机制在运行时动态生成代理类,而不需要在编译时确定代理类。Java提供了 java.lang.reflect.Proxy
类和 java.lang.reflect.InvocationHandler
接口来实现动态代理。
示例代码:
定义抽象主题接口:
java
// 抽象主题接口
public interface Image {
void display();
}
实现真实主题类:
java
// 真实主题类
public class RealImage implements Image {
private String filename;
public RealImage(String filename) {
this.filename = filename;
loadFromDisk(filename);
}
private void loadFromDisk(String filename) {
System.out.println("Loading " + filename);
}
@Override
public void display() {
System.out.println("Displaying " + filename);
}
}
定义动态代理处理器:
java
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
// 动态代理处理器
public class ImageInvocationHandler implements InvocationHandler {
private Object realObject;
public ImageInvocationHandler(Object realObject) {
this.realObject = realObject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("display".equals(method.getName())) {
System.out.println("Proxy: Loading image proxy...");
Object result = method.invoke(realObject, args);
return result;
}
return null;
}
}
客户端代码:
java
import java.lang.reflect.Proxy;
public class Client {
public static void main(String[] args) {
Image realImage = new RealImage("test.jpg");
Image proxyInstance = (Image) Proxy.newProxyInstance(
realImage.getClass().getClassLoader(),
realImage.getClass().getInterfaces(),
new ImageInvocationHandler(realImage)
);
// 图像将通过代理加载并显示
proxyInstance.display();
}
}
在这个示例中,ImageInvocationHandler
是动态代理处理器,它实现了 InvocationHandler
接口。Proxy.newProxyInstance
方法在运行时动态生成代理类,并返回一个实现了 Image
接口的代理对象。客户端通过代理对象调用 display
方法时,会触发 ImageInvocationHandler
的 invoke
方法,从而实现对真实对象的访问控制。
四、代理模式的优缺点
优点:
- 增强功能:代理模式可以在不修改目标对象的前提下,为目标对象添加额外的功能。
- 控制访问:代理模式可以控制对目标对象的访问权限,例如限制访问次数或时间。
- 缓存和懒加载:代理模式可以实现缓存和懒加载机制,提高系统的性能和资源利用率。
缺点:
- 复杂性:代理模式增加了系统的复杂性,特别是当代理类较多时,代码会变得难以维护。
- 性能开销:代理模式在客户端和目标对象之间增加了一层中介,可能会引入额外的性能开销。
总结
代理模式是一种结构型设计模式,它允许你为其他对象提供一种代理以控制对这个对象的访问。代理模式可以在不修改目标对象的前提下,为目标对象添加额外的功能或控制访问权限。静态代理和动态代理是代理模式的两种实现方式,静态代理在编译时确定代理类,而动态代理在运行时动态生成代理类。通过Java代码示例,我们可以更好地理解和实践代理模式。