代理模式:控制对象访问的利器!
大家好!今天我们来聊聊设计模式中的代理模式 (Proxy Pattern)。如果你曾经需要控制对某个对象的访问,或者在访问对象时添加额外的逻辑(如权限检查、日志记录等),那么代理模式就是你的救星!本文基于《Head First 设计模式》的代理模式章节,通过生动的故事和 Java 代码示例 ,带你轻松掌握代理模式的精髓。我们还会探讨代理模式在 JDK 和 Spring 等框架中的应用,让你真正理解它的实际价值。

1. 代理模式是什么?
代理模式是一种结构型设计模式,它允许通过一个代理对象来控制对另一个对象的访问。代理模式的核心思想是在不改变原始类的情况下,通过引入代理类来增强或控制对原始类的访问。
适用场景
- 需要控制对某个对象的访问(如权限检查、延迟加载等)。
- 需要在访问对象时添加额外的逻辑(如日志记录、性能监控等)。
- 需要为远程对象提供本地代理(如远程方法调用 RMI)。
2. 代理模式的实现
故事背景
小明开发了一个图片查看器系统,系统中有一个 Image
类用于加载和显示图片。由于图片加载是一个耗时的操作,小明希望在图片真正显示之前,先显示一个占位符(如加载中...),并在图片加载完成后再显示实际图片。
问题出现
如果直接在 Image
类中实现加载逻辑,会导致图片查看器的性能下降,因为每次加载图片时都需要等待图片加载完成。
解决方案:代理模式
小明决定使用代理模式,通过引入一个代理类 ImageProxy
来控制对 Image
对象的访问。代理类可以在图片加载完成前显示占位符,并在图片加载完成后显示实际图片。
3. 代码实现
1. 定义接口
首先,我们定义一个接口 Image
,包含图片显示的方法。
java
// 图片接口
interface Image {
void display();
}
2. 实现真实对象
接下来,我们实现真实的图片类 RealImage
,用于加载和显示图片。
java
// 真实图片类
class RealImage implements Image {
private String filename;
public RealImage(String filename) {
this.filename = filename;
loadFromDisk();
}
private void loadFromDisk() {
System.out.println("Loading image: " + filename);
}
@Override
public void display() {
System.out.println("Displaying image: " + filename);
}
}
3. 实现代理对象
然后,我们实现代理类 ImageProxy
,用于控制对 RealImage
对象的访问。
java
// 图片代理类
class ImageProxy implements Image {
private String filename;
private RealImage realImage;
public ImageProxy(String filename) {
this.filename = filename;
}
@Override
public void display() {
if (realImage == null) {
realImage = new RealImage(filename); // 延迟加载
}
realImage.display();
}
}
4. 客户端代码
最后,我们编写客户端代码来测试代理模式。
java
public class ImageViewer {
public static void main(String[] args) {
Image image = new ImageProxy("photo.jpg");
// 图片未加载时,代理类可以显示占位符
System.out.println("Image will be loaded on demand.");
// 第一次显示图片,触发加载
image.display(); // 输出: Loading image: photo.jpg, Displaying image: photo.jpg
// 第二次显示图片,无需重新加载
image.display(); // 输出: Displaying image: photo.jpg
}
}
4. 代理模式的优缺点
优点
- 职责清晰:代理类负责控制对真实对象的访问,真实对象只需关注核心逻辑。
- 延迟加载:可以通过代理类实现延迟加载,提高系统性能。
- 增强功能:可以在不修改真实对象的情况下,通过代理类添加额外的功能(如权限检查、日志记录等)。
缺点
- 增加复杂度:引入代理类会增加系统的复杂度。
- 性能开销:代理类可能会引入额外的性能开销(如方法调用的间接性)。
5. 代理模式在 JDK 和 Spring 中的应用
1. JDK 中的应用
- 动态代理 :JDK 提供了
java.lang.reflect.Proxy
类,用于创建动态代理对象。动态代理可以在运行时生成代理类,常用于实现 AOP(面向切面编程)。 - RMI(远程方法调用):JDK 的 RMI 框架使用代理模式来实现远程对象的本地代理。
动态代理示例
java
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// 接口
interface Greeting {
void sayHello();
}
// 真实对象
class GreetingImpl implements Greeting {
@Override
public void sayHello() {
System.out.println("Hello, World!");
}
}
// 动态代理处理器
class LoggingHandler implements InvocationHandler {
private Object target;
public LoggingHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before method: " + method.getName());
Object result = method.invoke(target, args);
System.out.println("After method: " + method.getName());
return result;
}
}
// 客户端代码
public class DynamicProxyDemo {
public static void main(String[] args) {
Greeting realObject = new GreetingImpl();
Greeting proxy = (Greeting) Proxy.newProxyInstance(
realObject.getClass().getClassLoader(),
realObject.getClass().getInterfaces(),
new LoggingHandler(realObject)
);
proxy.sayHello(); // 输出: Before method: sayHello, Hello, World!, After method: sayHello
}
}
2. Spring 中的应用
- AOP(面向切面编程):Spring 的 AOP 模块使用代理模式来实现切面功能(如日志记录、事务管理等)。Spring 支持 JDK 动态代理和 CGLIB 代理。
- 事务管理:Spring 的事务管理器通过代理模式来控制事务的开启、提交和回滚。
Spring AOP 示例
java
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.aop.support.NameMatchMethodPointcut;
// 接口
interface Service {
void perform();
}
// 真实对象
class ServiceImpl implements Service {
@Override
public void perform() {
System.out.println("Performing service...");
}
}
// 切面类
class LoggingAspect {
public void before() {
System.out.println("Before method execution.");
}
public void after() {
System.out.println("After method execution.");
}
}
// 客户端代码
public class SpringAOPDemo {
public static void main(String[] args) {
Service realObject = new ServiceImpl();
ProxyFactory proxyFactory = new ProxyFactory(realObject);
// 创建切点
NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
pointcut.addMethodName("perform");
// 创建切面
LoggingAspect aspect = new LoggingAspect();
proxyFactory.addAdvisor(new DefaultPointcutAdvisor(pointcut, (method, args1, target) -> {
aspect.before();
Object result = method.invoke(target, args1);
aspect.after();
return result;
}));
// 获取代理对象
Service proxy = (Service) proxyFactory.getProxy();
proxy.perform(); // 输出: Before method execution., Performing service..., After method execution.
}
}
6. 总结
代理模式通过引入代理类来控制对真实对象的访问,从而在不修改原始类的情况下增强或控制其行为。它的核心优势在于:
- 职责分离:代理类负责控制访问,真实对象专注于核心逻辑。
- 延迟加载:通过代理类实现延迟加载,提升系统性能。
- 增强功能:通过代理类添加额外功能(如日志记录、权限检查等)。
希望本文能帮助你更好地理解代理模式,并在实际项目中灵活运用!
互动话题
你在项目中使用过代理模式吗?遇到过哪些有趣的问题?欢迎在评论区分享你的经验!