结构型-代理模式(Proxy Pattern)
什么是代理模式
由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。
Java中的代理按照代理类生成时机不同又分为静态代理和动态代理。静态代理代理类在编译期就生成,而动态代理代理类则是在Java运行时动态生成。动态代理分为JDK代理和CGLib代理两种。
结构
- 抽象主题(Subject)类: 通过接口或抽象类声明真实主题和代理对象实还会现的业务方法。
- 真实主题(Real Subject)类: 实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。
- 代理(Proxy)类 : 提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。
UML类图
代码理解
public interface Subject {
void operation();
}
public class RealSubject implements Subject {
@Override
public void operation() {
System.out.println("RealSubject: Handling request.");
}
}
public class Proxy implements Subject {
private RealSubject realSubject;
@Override
public void operation() {
if (realSubject == null) {
realSubject = new RealSubject();
}
System.out.println("Proxy: Logging the time before executing operation.");
realSubject.operation();
System.out.println("Proxy: Logging the time after executing operation.");
}
}
public class Client {
private Subject proxy;
public void useProxy() {
proxy = new Proxy(); // 使用代理对象
proxy.operation(); // 通过代理对象调用操作
}
}
优缺点
优点:
- 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
- 可以在不修改实际对象代码的情况下,通过引入代理对象来扩展功能;
- 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度;
- 代理模式可以在实际需要时才创建实际对象,从而节省资源,特别是在创建对象成本较高的情况下。
缺点:
- 增加了系统的复杂度;
- 代理模式增加了一层间接性,可能会导致性能开销,尤其是在代理操作非常频繁的情况下。
- 如果代理模式被滥用,可能会导致系统中存在过多的代理对象,使得系统难以理解和维护。
使用场景
- 远程代理(Remote Proxy):
-
- 用于隐藏对象位于不同地址空间的事实,使得客户端代码可以像访问本地对象一样访问远程对象。
- 例如,远程服务器上的一个对象,客户端通过远程代理与之交互,无需关心网络通信的细节。
- 虚拟代理(Virtual Proxy):
-
- 用于延迟创建开销大的对象,直到真正需要时才创建。
- 例如,加载大型图像或文档时,可以先使用一个轻量级的代理对象,直到需要显示图像或文档内容时才加载实际的资源。
- 保护代理(Protection Proxy):
-
- 控制对原始对象的访问,提供权限检查。
- 例如,访问受保护的资源时,代理可以检查用户权限,确保只有授权用户才能访问。
- 智能引用代理(Smart Reference Proxy):
-
- 在访问对象时执行额外的动作,如引用计数、加载持久对象等。
- 例如,管理数据库连接池,智能引用代理可以跟踪连接的使用情况,并在适当的时候释放连接。
- 局部代理(Local Proxy):
-
- 在代码中提供一个本地代表,用于访问另一个系统或组件。
- 例如,在Web服务中,客户端代码可能通过一个局部代理与服务端通信,而无需直接处理HTTP请求和响应。
- 日志代理(Logging Proxy):
-
- 在调用实际对象的方法前后添加日志记录。
- 例如,为了监控系统性能或调试目的,代理可以在方法调用前后记录日志。
- 同步化代理(Synchronization Proxy):
-
- 控制对多线程环境中共享资源的访问,确保线程安全。
- 例如,代理可以管理对共享数据结构的并发访问,确保在任何时候只有一个线程可以修改数据。
- 防火墙代理(Firewall Proxy):
-
- 提供安全控制,防止外部系统直接访问内部系统。
- 例如,限制外部网络访问内部网络资源,只允许通过代理进行特定的操作。
- 延迟初始化代理(Lazy Initialization Proxy):
-
- 用于实现懒加载模式,即只有在需要时才初始化对象。
- 例如,初始化一个复杂的系统组件,直到实际需要使用该组件时才进行初始化。
- 缓存代理(Cache Proxy):
-
- 缓存对象的结果以避免重复计算或重复访问数据库。
- 例如,代理可以存储数据库查询的结果,并在后续请求中提供这些缓存结果。
动态代理与静态代理比较
静态代理
- 代理生成时机:在编译时生成代理类。
- 代理类创建方式:需要手动创建代理类。
- 目标类要求:目标类必须实现接口。
- 代码复用性:代码重复较多,每增加一个代理就需要编写一个新的代理类。
- 灵活性:不灵活,代理类与目标类耦合。
- 性能:性能略好,涉及到静态方法调用。
- 适用场景:适用于接口较少且不经常变动的场景,例如日志记录、安全控制等。
动态代理
- 代理生成时机:在运行时动态生成代理类。
- 代理类创建方式:自动通过反射(JDK)或字节码生成(CGLIB)。
- 目标类要求:JDK 动态代理要求目标类实现接口,CGLIB 无需接口。
- 代码复用性:代码复用性较高,可以动态地为不同的类创建代理。
- 灵活性:灵活,代理类和目标类解耦,可以在运行时创建代理。
- 性能:性能较好,尤其在 CGLIB 中,直接操作字节码,但因为使用了反射机制,性能较差。
- 适用场景:适用于接口较多且经常变动的场景,例如AOP编程、远程调用等。
JDK 动态代理与 CGLIB 动态代理比较
- 基于:JDK 动态代理基于接口实现的代理,只能代理实现了接口的类;CGlib方式是基于继承实现的代理,可以作为JDK代理方式的补充方案。
- 目标类要求:JDK 动态代理的目标类必须实现接口;CGLIB 动态代理的目标类不需要实现接口,直接代理类。
- 使用方式:JDK 动态代理使用
Proxy.newProxyInstance
;CGLIB 使用Enhancer
类来创建代理类。 - 性能:JDK 动态代理因为使用了反射机制,性能较差;CGLIB 通过字节码生成子类,性能较好。
- 是否支持
final
类:JDK 动态代理支持,接口类不需要是final
;CGLIB 不支持,目标类不能是final
类。 - 生成的代理类:JDK 动态代理动态生成的代理类实现了目标接口;CGLIB 动态生成的代理类是目标类的子类。
原文地址:https://blog.csdn.net/weixin_62661973/article/details/144202217
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!