代理模式(Proxy Pattern)
文章目录
- 1. 概述
- 1.1 基本概念
- 1.2 为什么需要代理模式
- 1.3 代理模式的四个角色
- 2. 代理模式的类型
- 2.1 静态代理
- 2.2 JDK动态代理
- 2.3 CGLIB动态代理
- 3. 代理模式的UML类图和基本实现
- 3.1 UML类图
- 3.2 基本实现
- 3.2.1 静态代理基本实现
- 3.2.2 JDK动态代理基本实现
- 3.2.3 CGLIB动态代理基本实现
- 3.3 三种代理方式的对比
- 4. 代理模式的详细示例(Java实现)
- 4.1 场景描述
- 4.2 静态代理示例
- 4.3 JDK动态代理示例
- 4.4 CGLIB动态代理示例
- 4.5 保护代理示例
- 5. 代理模式在Spring AOP中的应用
- 5.1 Spring AOP的代理机制
- 5.2 Spring AOP示例
- 5.3 Spring AOP代理原理分析
- 6. 代理模式与其他设计模式的区别与联系
- 6.1 代理模式 vs 装饰器模式
- 示例对比:
- 6.2 代理模式 vs 适配器模式
- 示例对比:
- 6.3 代理模式 vs 中介者模式
- 7. 代理模式的实际应用场景
- 7.1 远程代理(Remote Proxy)
- 7.2 虚拟代理(Virtual Proxy)
- 7.3 保护代理(Protection Proxy)
- 7.4 缓存代理(Cache Proxy)
- 7.5 智能引用(Smart Reference)
- 8. 代理模式的优缺点总结
- 8.1 优点
- 8.2 缺点
- 9. 常见问题与解答
- 9.1 为什么要使用代理模式而不是直接修改原始类?
- 9.2 静态代理与动态代理应该如何选择?
- 9.3 Spring AOP使用的是什么代理模式?
- 9.4 CGLIB代理和JDK动态代理有什么区别?
- 10. 总结
1. 概述
代理模式是一种结构型设计模式,它允许你提供一个代理对象来控制对另一个对象(称为"被代理对象"或"目标对象")的访问。代理对象充当了客户端和目标对象之间的中介,拦截对目标对象的所有调用。
1.1 基本概念
代理模式的核心思想是:通过引入一个新的代理对象来间接访问目标对象,从而可以在不修改目标对象的前提下,增强或控制对目标对象的访问。
在代理模式中,代理类通常具有以下特点:
- 与目标对象实现相同的接口
- 持有目标对象的引用
- 可以在调用目标对象的方法前后执行额外的逻辑
1.2 为什么需要代理模式
代理模式的主要目的是在不改变目标对象的情况下,通过代理对象控制对目标对象的访问。这种控制可以出于多种原因:
- 延迟初始化:当创建目标对象的代价很高,或者目标对象可能不会被使用时,可以使用代理推迟目标对象的创建
- 访问控制:对目标对象的访问权限进行控制,例如添加身份验证
- 日志记录:记录对目标对象的调用
- 性能优化:例如通过缓存结果来减少重复操作
- 远程代理:隐藏目标对象位于不同地址空间的事实
- 智能引用:在访问对象时执行额外的操作,如引用计数和线程安全检查
1.3 代理模式的四个角色
- 抽象主题(Subject):定义代理类和真实主题的共同接口,这样在任何使用真实主题的地方都可以使用代理
- 真实主题(Real Subject):代理所代表的真实对象,是最终要引用的对象
- 代理(Proxy):包含对真实主题的引用,从而可以访问它;提供与真实主题相同的接口,以便在任何时候都可以替代真实主题;控制对真实主题的访问,并可能负责创建和删除它
- 客户端(Client):通过代理间接地访问真实主题
2. 代理模式的类型
代理模式根据实现方式主要分为三种类型:
2.1 静态代理
静态代理是在程序运行前,代理类的源码已经创建好,并且编译生成了代理类的.class文件。代理类和被代理类都实现同一个接口,并且在代理类中持有被代理对象的引用。
特点:
- 在编译期就已经确定了代理关系
- 为每个被代理类都需要创建一个代理类
- 所有的代理类都在编译期生成
缺点:
- 当接口增加方法时,目标对象与代理对象都要维护
- 当需要代理多个类时,需要创建多个代理类,代码量大,不易维护
2.2 JDK动态代理
JDK动态代理是Java语言自带的动态代理机制,它只能代理实现了接口的类。JDK动态代理通过java.lang.reflect.Proxy
类和java.lang.reflect.InvocationHandler
接口实现。
特点:
- 在运行时动态生成代理类
- 代理类实现了与目标类相同的接口
- 目标类必须实现接口
- 利用反射机制实现
2.3 CGLIB动态代理
CGLIB(Code Generation Library)是一个开源项目,它是一个强大的高性能高质量的代码生成库,可以在运行期扩展Java类和实现Java接口。CGLIB代理是针对类来实现代理的,它的原理是对指定的目标类生成一个子类,并覆盖其中的方法。
特点:
- 在运行时动态生成代理类
- 代理类是目标类的子类
- 目标类不需要实现接口
- 不能代理final类和final方法
- 性能优于JDK动态代理
3. 代理模式的UML类图和基本实现
3.1 UML类图
代理模式的UML类图如下:
+----------------+ +----------------+
| Subject |<------| Client |
+----------------+ +----------------+
| +operation() |
+-------^--------+||
+-------+--------+ +----------------+
| RealSubject |<------| Proxy |
+----------------+ +----------------+
| +operation() | | -realSubject |
+----------------+ | +operation() |+----------------+
UML类图解释:
- Subject(抽象主题):定义了RealSubject和Proxy的共同接口,这样就可以在任何使用RealSubject的地方使用Proxy
- RealSubject(真实主题):定义了Proxy所代表的真实对象
- Proxy(代理):持有对RealSubject的引用,实现了与RealSubject相同的接口,并控制对RealSubject的访问
- Client(客户端):通过Proxy间接访问RealSubject
3.2 基本实现
以下是各种代理模式的基本实现:
3.2.1 静态代理基本实现
// 抽象主题接口
interface Subject {void operation();
}// 真实主题
class RealSubject implements Subject {@Overridepublic void operation() {System.out.println("真实主题执行操作");}
}// 代理类
class Proxy implements Subject {private RealSubject realSubject;public Proxy() {this.realSubject = new RealSubject();}@Overridepublic void operation() {// 调用前的额外操作System.out.println("调用前的处理");// 调用真实主题的方法realSubject.operation();// 调用后的额外操作System.out.println("调用后的处理");}
}// 客户端
public class Client {public static void main(String[] args) {Subject subject = new Proxy();subject.operation();}
}
输出结果:
调用前的处理
真实主题执行操作
调用后的处理
3.2.2 JDK动态代理基本实现
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;// 抽象主题接口
interface Subject {void operation();
}// 真实主题
class RealSubject implements Subject {@Overridepublic void operation() {System.out.println("真实主题执行操作");}
}// 调用处理器
class DynamicProxyHandler implements InvocationHandler {private Object target; // 目标对象public DynamicProxyHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 调用前的额外操作System.out.println("调用前的处理");// 调用真实主题的方法Object result = method.invoke(target, args);// 调用后的额外操作System.out.println("调用后的处理");return result;}
}// 客户端
public class Client {public static void main(String[] args) {// 创建真实主题RealSubject realSubject = new RealSubject();// 创建调用处理器InvocationHandler handler = new DynamicProxyHandler(realSubject);// 创建动态代理Subject proxySubject = (Subject) Proxy.newProxyInstance(realSubject.getClass().getClassLoader(),realSubject.getClass().getInterfaces(),handler);// 调用代理对象的方法proxySubject.operation();// 查看代理类的类型System.out.println("代理类的类型:" + proxySubject.getClass().getName());}
}
输出结果:
调用前的处理
真实主题执行操作
调用后的处理
代理类的类型:com.sun.proxy.$Proxy0
3.2.3 CGLIB动态代理基本实现
CGLIB动态代理需要添加CGLIB库的依赖(如果使用Maven):
<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.3.0</version>
</dependency>
Java代码实现:
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;import java.lang.reflect.Method;// 真实主题(不需要实现接口)
class RealSubject {public void operation() {System.out.println("真实主题执行操作");}
}// CGLIB方法拦截器
class CglibProxyInterceptor implements MethodInterceptor {@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {// 调用前的额外操作System.out.println("调用前的处理");// 调用真实主题的方法Object result = proxy.invokeSuper(obj, args);// 调用后的额外操作System.out.println("调用后的处理");return result;}
}// 客户端
public class Client {public static void main(String[] args) {// 创建Enhancer对象Enhancer enhancer = new Enhancer();// 设置代理类的父类enhancer.setSuperclass(RealSubject.class);// 设置回调函数enhancer.setCallback(</