代理模式
00 分钟
2024-11-18
标签
💡
代理模式在控制对象访问和扩展对象功能方面非常有用,适用于对目标对象访问进行控制的场景。在实现代理模式时,可以根据需求选择不同的代理方式,灵活地为系统增加权限、日志、性能监控等功能。不过,代理模式增加了一层间接性,需要在性能和可维护性之间进行权衡。

定义

代理模式(静态代理)是一种结构型设计模式,它通过代理对象来控制对目标对象的访问。代理对象提供了与目标对象相同的接口或方法,使得客户端可以间接地访问目标对象,同时能够在访问目标对象的过程中进行控制或增强,例如添加访问权限控制、延迟加载、日志记录等功能。

结构

代理模式主要包含以下角色:
  • 抽象接口(Subject):定义目标对象和代理对象的公共接口,客户端通过该接口访问目标对象。
  • 真实对象(RealSubject):实际处理请求的对象。
  • 代理对象(Proxy):持有对真实对象的引用,控制对真实对象的访问,并可以在请求前后加入额外的逻辑。

类图


代码实现

假设一个银行账户服务 BankAccount,提供了 depositwithdraw 方法。我们希望通过代理对象来控制访问,以进行日志记录和权限控制。

1. 抽象接口

2. 真实对象

3. 代理对象

4. 客户端代码


注意事项

  1. 选择合适的代理类型:代理模式有多种实现类型,包括静态代理动态代理CGLIB代理等,选择时应根据需求决定使用哪一种实现。例如,动态代理可以在运行时动态生成代理类,非常适合 AOP 场景。
  1. 性能影响:代理模式会在每次方法调用时增加一层间接访问的开销。对于性能要求高的场景,要谨慎使用代理,以免影响系统性能。
  1. 代码可维护性:代理类通常与真实对象实现相同的接口,若接口发生变更,代理和真实对象都需要同步修改,可能增加维护成本。因此,在设计代理模式时,尽量保证接口的稳定性。
  1. 权限控制:在代理模式中,通常会引入权限控制或访问限制逻辑,确保用户或调用方只能访问其有权限的操作。在这种情况下,要注意权限配置的安全性和适用范围。

扩展

JDK 动态代理

JDK 提供了动态代理机制,通过 java.lang.reflect.ProxyInvocationHandler 实现。动态代理可以在运行时生成代理类,避免手动编写静态代理类。它适用于代理实现了接口的目标对象。

优势:

  • 无需显式实现代理类,代码量较少。
  • 灵活性高,可动态定义代理行为。

示例代码

 

静态代理与动态代理

  • 静态代理:编译时创建代理类,代理类在代码中显式实现接口,代码量较多且不灵活,适合简单代理需求。
  • 动态代理:运行时生成代理类,Java 提供了 java.lang.reflect.Proxy 支持动态代理,适合在运行时需要代理类的场景。

CGLIB 代理

CGLIB 代理是一种基于字节码的代理方式,可以代理没有实现接口的类,适合于需要代理类扩展的情况。Spring AOP 中就使用了 CGLIB 代理来实现对目标对象的增强。

应用场景

  • 远程代理:代理对象代表另一个远程对象,客户端可以像调用本地对象一样进行操作。
  • 虚拟代理:代理对象在需要时才创建真实对象,常用于延迟加载资源密集的对象。
  • 保护代理:通过代理控制对对象的访问权限,例如设置不同用户权限控制。

优势:

  • 能够代理没有实现接口的目标类。
  • 性能优于 JDK 动态代理,尤其是在代理方法较多时。

注意:

  • 被代理的类不能是 final,否则无法生成子类。
  • Spring AOP 在目标对象未实现接口时,会使用 CGLIB。

示例代码

比较总结

特点
JDK 动态代理
CGLIB 动态代理
代理对象限制
仅支持实现了接口的类
支持未实现接口的类
实现方式
基于 Java 反射和接口实现
基于字节码生成子类
性能
方法较少时性能接近或优于 CGLIB
方法较多时性能优于 JDK 动态代理
代理类限制
无法代理 final 方法
无法代理 final 类和 final 方法
Spring 使用场景
默认使用,目标类实现了接口
目标类未实现接口时,Spring 自动切换为 CGLIB
在实际应用中:
  • 若目标对象实现了接口,优先使用 JDK 动态代理。
  • 若目标对象未实现接口,选择使用 CGLIB 动态代理。
上一篇
很鸡儿扯的报错
下一篇
说明

评论
Loading...