news 2026/2/13 17:54:50

设计模式学习(14) 23-12 代理模式

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
设计模式学习(14) 23-12 代理模式

文章目录

  • 0.个人感悟
  • 1. 概念
  • 2. 适配场景
    • 2.1 适合的场景
    • 2.2 常见场景举例
  • 3. 实现方法
    • 3.1 静态代理
      • 3.1.1 实现思路
      • 3.1.2 UML类图
      • 3.1.3 代码示例
    • 3.2 动态代理-JDK代理
      • 3.2.1 实现思路
      • 3.2.2 UML类图
      • 3.2.3 代码示例
    • 3.3 动态代理-CGLIB代理
      • 3.3.1 实现思路
      • 3.3.2 UML类图
      • 3.3.3 代码示例
  • 4. 优缺点
    • 4.1 优点
    • 4.2 缺点

0.个人感悟

  • 说起代理模式,很容易想到现实生活中的中介。在客户和实际提供者之间增加了一层,最终还是完成了客户和提供者的交易,但可以做一些额外的动作,比如做服务(收费)等
  • 代理是实现方式有很多种,可以逐层演进来理解
    • 静态代理,自己去写代码实现,持有代理对象,在方法内做自己的额外动作,大家都能想到的方式
    • jdk对于这种场景进行了优化,提供了API,把通用模版提供好,提供了扩展类,我们自己填充业务代码就行,这里也体现了封装、多态的魅力。jdk代理在运行时内存中创建代理对象,不用像静态代理那样提前写代码;不过需要实现接口
    • Code Generation Library)是个功能强大、高性能、开源的代码生成库,代理是其中一个功能。对动态代理进一步进行扩展,它也是把通用模版准备好,听过API,我们只用实现自己的特异代码就行。不需要实现接口,也被称为子类代理。
  • 实际工作中多用spring aop,这个计划后面会专门整理spring学习笔记系列
  • 代理模式的uml图和装饰器模式有些像,但有本质区别:代 理控制访问,装饰器是增强功能

1. 概念

英文定义(《设计模式:可复用面向对象软件的基础》)

Provide a surrogate or placeholder for another object to control access to it.

中文翻译

为其他对象提供一种代理以控制对这个对象的访问。

理解

  • 代理模式是一种结构型设计模式,通过引入代理对象来控制对原始对象的访问
  • 代理对象在客户端和目标对象之间起到中介作用,可以在不修改目标对象的情况下增加额外功能

2. 适配场景

2.1 适合的场景

  1. 远程访问:为远程对象提供本地代理(RPC、Web Service)
  2. 延迟加载:创建开销大的对象时,使用虚拟代理延迟实例化
  3. 访问控制:保护代理控制对敏感对象的访问权限
  4. 智能引用:在对象被访问时执行额外操作(计数、日志、缓存)
  5. 缓存代理:为频繁访问的结果提供缓存,提高性能
  6. 防火墙代理:保护网络资源,控制外部访问

2.2 常见场景举例

  • 图片加载:虚拟代理延迟加载大图片,先显示缩略图
  • 数据库连接池:连接代理管理连接的生命周期
  • Spring AOP:动态代理实现面向切面编程
  • VPN:网络访问的代理服务器
  • RMI(远程方法调用):Java的远程对象代理
  • MyBatis的Mapper接口:通过动态代理实现SQL映射

3. 实现方法

3.1 静态代理

3.1.1 实现思路

  1. 定义抽象主题接口:声明真实主题和代理的共同接口
  2. 实现真实主题类:定义实际执行业务逻辑的对象
  3. 创建代理类:实现主题接口,持有真实主题的引用,并添加额外功能
  4. 客户端通过代理访问:客户端调用代理对象的方法,代理再调用真实对象

3.1.2 UML类图

角色说明

  • Subject(抽象主题):定义真实主题和代理的共同接口
  • RealSubject(真实主题):实际执行业务逻辑的对象
  • Proxy(代理):持有真实主题的引用,控制对真实主题的访问
  • Client(客户端):通过代理对象访问真实主题
    注意:
  • 代理和装饰者模式的区别:代理控制访问,装饰者增强功能

3.1.3 代码示例

suject接口和实现

publicinterfaceISubject{/** * @description 行为方法 * @author bigHao * @date 2026/1/17 **/voiddoAction();/** * @description 行为方法2 * @author bigHao * @date 2026/1/17 **/voiddoAction2();}publicclassRealSubjectimplementsISubject{@OverridepublicvoiddoAction(){System.out.println("do action");}@OverridepublicvoiddoAction2(){System.out.println("do acton2");}}

静态代理

publicclassSubjectProxyimplementsISubject{privateISubjectsubject;publicSubjectProxy(){subject=newRealSubject();}@OverridepublicvoiddoAction(){// 代理行为System.out.println("proxy acton start");subject.doAction();// 代理行为System.out.println("proxy acton end");}@OverridepublicvoiddoAction2(){// 代理行为System.out.println("proxy acton start");subject.doAction2();// 代理行为System.out.println("proxy acton end");}}

测试:

publicclassClient{staticvoidmain(){ISubjectproxy=newSubjectProxy();System.out.println("=== proxy acton1 ===");proxy.doAction();System.out.println("=== proxy acton2 ===");proxy.doAction2();}}

输出:

=== proxy acton1 === proxy acton start do action proxy acton end === proxy acton2 === proxy acton start do acton2 proxy acton end

3.2 动态代理-JDK代理

3.2.1 实现思路

  1. 定义接口:与静态代理相同,需要抽象主题接口
  2. 实现真实主题:实现业务逻辑的真实类
  3. 实现InvocationHandler:编写调用处理器,定义代理的增强逻辑
  4. 使用Proxy.newProxyInstance创建代理:运行时动态生成代理类
  5. 客户端通过动态代理访问:客户端使用代理对象执行方法

3.2.2 UML类图

3.2.3 代码示例

java.lang.reflect提供了核心API,方法和参数说明:

/** * Proxy类提供用于创建动态代理类和实例的静态方法。 * 它是所有由Proxy创建的动态代理类的父类。 */publicclassProxyimplementsjava.io.Serializable{/** * * @param loade 定义代理类的类加载器。通常使用目标接口的类加载器, * 或者使用当前线程的上下文类加载器(Thread.currentThread().getContextClassLoader()) * * @param interfaces 代理类要实现的接口列表 * * @param h 调用处理器,当代理实例的方法被调用时,调用处理器的invoke方法会被调用 * */publicstaticObjectnewProxyInstance(ClassLoaderloader,Class<?>[]interfaces,InvocationHandlerh)}
/** * InvocationHandler是由代理实例的调用处理器实现的接口。 */publicinterfaceInvocationHandler{/** * 处理代理实例上的方法调用并返回结果。 * 当在与之关联的代理实例上调用方法时,将在调用处理器上调用此方法。 * * @param proxy 调用该方法的代理实例。 * 这是代理对象本身,不是真实的目标对象。 * * @param method 对应于在代理实例上调用的接口方法的Method实例。 * Method对象的声明类将是在该方法声明的接口, * 该接口可能是代理类继承该方法的代理接口的超接口。 * * @param args 包含在代理实例的方法调用中传递的参数值的对象数组。 * */publicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable;}

suject接口和实现

publicinterfaceISubject{/** * @description 行为方法 * @author bigHao * @date 2026/1/17 **/voiddoAction();/** * @description 行为方法2 * @author bigHao * @date 2026/1/17 **/voiddoAction2();}publicclassRealSubjectimplementsISubject{@OverridepublicvoiddoAction(){System.out.println("do action");}@OverridepublicvoiddoAction2(){System.out.println("do acton2");}}

动态代理

publicclassProxyFactory{privateObjecttarget;publicProxyFactory(Objecttarget){this.target=target;}publicObjectgetProxyInstance(){returnProxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),newInvocationHandler(){@OverridepublicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{System.out.println("proxyName: "+proxy.getClass().getName());System.out.println("methodName: "+method.getName());// 代理行为System.out.println("jdk proxy acton start");// 反射调用目标对象方法Objectinvoke=method.invoke(target,args);// 代理行为System.out.println("jdk proxy acton end");returninvoke;}});}}

测试:

publicclassClient{staticvoidmain(){ISubjectproxyInstance=(ISubject)newProxyFactory(newRealSubject()).getProxyInstance();System.out.println("=== jdk proxy acton1 ===");proxyInstance.doAction();System.out.println("=== jdk proxy acton2 ===");proxyInstance.doAction2();}}

输出;

===jdk proxy acton1===proxyName:jdk.proxy1.$Proxy0methodName:doAction jdk proxy acton startdoaction jdk proxy acton end===jdk proxy acton2===proxyName:jdk.proxy1.$Proxy0methodName:doAction2 jdk proxy acton startdoacton2 jdk proxy acton end

3.3 动态代理-CGLIB代理

3.3.1 实现思路

  1. 无需接口:CGLIB可以代理没有接口的类
  2. 创建MethodInterceptor:实现方法拦截器,定义增强逻辑
  3. 使用Enhancer创建代理:配置Enhancer生成代理类
  4. 生成子类代理:CGLIB通过生成目标类的子类来实现代理
  5. 客户端通过CGLIB代理访问:与JDK代理使用方式类似

3.3.2 UML类图

3.3.3 代码示例

Cglib(Code Generation Library)是个功能强大、高性能、开源的代码生成包,被广泛使用,比如spring等
它可以为没有实现接口的类提供代理
这里示例是用的spring集成的org.springframework.cglib.proxy。
核心API是包下面的Enhancer、MethodInterceptor,感兴趣可以跟一下源码
需要代理的类(不需要实现接口)

publicclassRealSubject{publicvoiddoAction(){System.out.println("do action");}publicvoiddoAction2(){System.out.println("do acton2");}}

代理类

publicclassProxyFactoryimplementsMethodInterceptor{privateObjecttarget;publicProxyFactory(Objecttarget){this.target=target;}publicObjectgetProxyInstance(){// 1.使用apiEnhancerenhancer=newEnhancer();// 2.设置父类enhancer.setSuperclass(target.getClass());// 3.设置回调函数enhancer.setCallback(this);// 4.创建代理对象returnenhancer.create();}@OverridepublicObjectintercept(Objectobj,Methodmethod,Object[]args,MethodProxyproxy)throwsThrowable{System.out.println("proxyName: "+proxy.getClass().getName());System.out.println("methodName: "+method.getName());// 代理行为System.out.println("cglib proxy acton start");// 反射调用目标对象方法Objectinvoke=method.invoke(target,args);// 代理行为System.out.println("cglib proxy acton end");returninvoke;}}

测试

publicclassClient{publicstaticvoidmain(String[]args){RealSubjectsubject=(RealSubject)newProxyFactory(newRealSubject()).getProxyInstance();System.out.println("=== cglib acton1 ===");subject.doAction();System.out.println("=== cglib acton2 ===");subject.doAction2();}}

输出

=== cglib acton1 === proxyName: org.springframework.cglib.proxy.MethodProxy methodName: doAction cglib proxy acton start do action cglib proxy acton end === cglib acton2 === proxyName: org.springframework.cglib.proxy.MethodProxy methodName: doAction2 cglib proxy acton start do acton2 cglib proxy acton end

4. 优缺点

4.1 优点

高内聚低耦合

  • 代理对象将附加功能与核心业务逻辑分离
  • 真实对象只需关注核心功能,代理对象处理访问控制、日志等
    提高扩展性
  • 符合开闭原则:可以方便地添加新的代理功能而不修改真实对象
  • 可以动态地添加或移除代理层
    增强安全性
  • 保护代理可以控制对敏感对象的访问权限
  • 可以隐藏真实对象的复杂性和实现细节
    提高性能
  • 虚拟代理实现延迟加载,减少系统启动时间和内存占用
  • 缓存代理避免重复计算,提高响应速度
    提高复用性
  • 代理逻辑可以被多个真实对象复用
  • 动态代理可以通用地处理一类对象的代理需求
    增强可维护性
  • 代理模式将横切关注点(日志、事务、安全)集中管理
  • 便于实现AOP(面向切面编程)

4.2 缺点

增加系统复杂度

  • 引入额外的代理层,增加了系统的复杂性
  • 静态代理需要为每个真实类创建对应的代理类
    可能降低性能
  • 代理调用增加了一层间接层,可能带来轻微的性能开销
  • 动态代理基于反射,性能低于直接调用
    代码可读性降低
  • 调试时堆栈跟踪更复杂,增加了调试难度
  • 代理模式可能使代码流程不够直观
    实现复杂性
  • 动态代理涉及反射和字节码操作,实现较复杂
  • CGLIB代理需要处理final方法、构造函数等特殊情况
    可能的循环调用
  • 如果代理不当,可能导致无限递归调用
  • 需要正确处理代理链的调用顺序

参考:

  • 韩顺平 Java设计模式
  • 张维鹏 Java设计模式之结构型:代理模式
  • clover_toeic Java设计模式:Proxy(代理)模式
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/12 9:36:21

Qwen3-Embedding-0.6B与Nomic对比:代码检索任务实战评测

Qwen3-Embedding-0.6B与Nomic对比&#xff1a;代码检索任务实战评测 1. 背景与评测目标 在现代软件开发和AI辅助编程场景中&#xff0c;代码检索&#xff08;Code Retrieval&#xff09;能力正成为衡量嵌入模型实用价值的关键指标。其核心任务是将自然语言查询&#xff08;如…

作者头像 李华
网站建设 2026/2/12 1:25:36

ACE-Step长音频生成:突破时长限制的分段拼接优化策略

ACE-Step长音频生成&#xff1a;突破时长限制的分段拼接优化策略 1. 引言&#xff1a;长音频生成的技术挑战与ACE-Step的定位 在当前AI音乐生成领域&#xff0c;生成高质量、结构完整且具备情感表达的长时音频&#xff08;如完整歌曲、背景配乐等&#xff09;仍面临诸多挑战。…

作者头像 李华
网站建设 2026/2/13 12:07:48

超详细版VHDL入门:实体与架构全面讲解

从零开始搞懂VHDL&#xff1a;实体与架构的“硬件思维”启蒙课你有没有试过用软件的方式去写硬件&#xff1f;刚接触FPGA时&#xff0c;我曾把if-else当成C语言那样层层嵌套&#xff0c;结果综合出来的电路满屏锁存器&#xff08;latch&#xff09;&#xff0c;时序一塌糊涂。后…

作者头像 李华
网站建设 2026/2/8 6:41:15

SenseVoice Small镜像应用实践|精准识别语音内容、情感及声音事件

SenseVoice Small镜像应用实践&#xff5c;精准识别语音内容、情感及声音事件 1. 引言 1.1 业务场景描述 在智能客服、会议记录、情感分析和内容审核等实际应用场景中&#xff0c;传统的语音识别技术往往仅关注“说了什么”&#xff0c;而忽略了“怎么说”以及“周围发生了什…

作者头像 李华
网站建设 2026/2/9 14:53:20

Qwen3-Embedding-4B调用不了?本地服务启动问题解决指南

Qwen3-Embedding-4B调用不了&#xff1f;本地服务启动问题解决指南 1. 背景与问题定位 在使用大模型进行文本嵌入任务时&#xff0c;Qwen3-Embedding-4B 因其强大的多语言支持、高维度可配置性以及优异的性能表现&#xff0c;成为许多开发者构建检索系统、语义匹配和分类任务…

作者头像 李华
网站建设 2026/2/3 18:06:33

【20岁沉思录】解放自我,对抗虚无。

全文约八千字 2026年1月17日&#xff0c;这一天是我的21岁生日。 而我将在这里尽可能记录下我20岁所有的挣扎与思考&#xff0c;来迎接崭新的一岁。 我从一个多月前就开始构思这篇文章&#xff0c;但直到两三天前才真正落笔&#xff0c;并且在写作过程中又诞生了大量新的想法&a…

作者头像 李华