设计模式学习(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(代理)模式

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1175350.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

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

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

Z-Image-Turbo广告设计案例:海报素材批量生成部署教程

Z-Image-Turbo广告设计案例&#xff1a;海报素材批量生成部署教程 1. 引言 随着AI图像生成技术的快速发展&#xff0c;自动化内容创作在广告、电商、社交媒体等领域的应用日益广泛。阿里通义推出的Z-Image-Turbo模型凭借其高效的推理速度和高质量的图像输出能力&#xff0c;成…

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

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

DeepSeek-R1模型微调入门:云端低成本实践

DeepSeek-R1模型微调入门&#xff1a;云端低成本实践 你是不是也遇到过这样的情况&#xff1f;作为学生&#xff0c;想动手做点AI项目、练练模型微调技术&#xff0c;但手头只有一台轻薄本&#xff0c;连本地跑个大模型都卡得不行。显存不够、训练太慢、环境配置复杂……这些问…

阿里通义实验室技术突破:CosyVoice-300M Lite原理解析

阿里通义实验室技术突破&#xff1a;CosyVoice-300M Lite原理解析 1. 引言&#xff1a;轻量级语音合成的技术演进 近年来&#xff0c;语音合成&#xff08;Text-to-Speech, TTS&#xff09;技术在智能助手、有声阅读、虚拟主播等场景中广泛应用。然而&#xff0c;传统TTS模型…

如何高效实现语音降噪?FRCRN单麦-16k镜像一键推理指南

如何高效实现语音降噪&#xff1f;FRCRN单麦-16k镜像一键推理指南 1. 引言&#xff1a;语音降噪的现实挑战与技术演进 在真实场景中&#xff0c;语音信号常常受到环境噪声、设备干扰和多人说话等因素影响&#xff0c;导致语音质量下降。尤其在远程会议、智能录音、安防监控等…

为什么推荐用云端跑MinerU?5大优势全面解读

为什么推荐用云端跑MinerU&#xff1f;5大优势全面解读 你是不是也遇到过这样的情况&#xff1a;团队里有人坚持“买服务器才靠谱”&#xff0c;觉得长期来看更省钱&#xff1b;而另一些人则主张“按需付费才是未来”&#xff0c;但又拿不出足够有说服力的数据来说服领导&…

避坑指南!使用IndexTTS 2.0时这些细节要注意

避坑指南&#xff01;使用IndexTTS 2.0时这些细节要注意 在AI语音合成技术飞速发展的今天&#xff0c;B站开源的 IndexTTS 2.0 凭借其“零样本音色克隆”、“毫秒级时长控制”和“音色-情感解耦”三大核心能力&#xff0c;迅速成为内容创作者、虚拟主播和开发者的新宠。只需上…

如何用文本精准抠图?sam3大模型镜像让分割一切更简单

如何用文本精准抠图&#xff1f;sam3大模型镜像让分割一切更简单 1. 引言&#xff1a;从手动标注到语义驱动的图像分割革命 图像分割作为计算机视觉中的基础任务&#xff0c;长期以来依赖于人工标注或半自动工具&#xff08;如框选、点选等&#xff09;来提取目标区域。尽管传…

es连接工具与Kibana联动配置入门必看

从零构建可观测性系统&#xff1a;打通 Elasticsearch 数据链路与 Kibana 可视化闭环你有没有遇到过这样的场景&#xff1f;服务器日志堆成山&#xff0c;出问题时却像大海捞针&#xff1b;监控告警响了&#xff0c;打开界面却发现数据断更半小时&#xff1b;新同事问“最近接口…

LobeChat最佳实践:生产环境中稳定性调优策略

LobeChat最佳实践&#xff1a;生产环境中稳定性调优策略 1. 引言 1.1 业务场景描述 随着大语言模型&#xff08;LLM&#xff09;在企业服务、智能客服和内部知识助手等场景中的广泛应用&#xff0c;构建一个稳定、高效且可扩展的对话系统成为技术团队的核心需求。LobeChat 作…

无需复杂命令!Z-Image-Turbo_UI界面图形化操作入门

无需复杂命令&#xff01;Z-Image-Turbo_UI界面图形化操作入门 1. 引言&#xff1a;让AI绘图变得简单直观 随着AI图像生成技术的快速发展&#xff0c;越来越多用户希望在本地设备上运行高性能模型。然而&#xff0c;复杂的命令行操作、环境配置和参数调试常常成为初学者的障碍…

UI-TARS-desktop部署教程:多模态Agent环境搭建指南

UI-TARS-desktop部署教程&#xff1a;多模态Agent环境搭建指南 1. 教程目标与适用场景 随着多模态AI Agent技术的快速发展&#xff0c;如何快速部署一个具备图形界面交互、视觉理解与工具调用能力的本地化Agent系统成为开发者关注的重点。本教程旨在为开发者提供一套完整、可…

AutoGLM-Phone企业定制:私有化部署与二次开发指南

AutoGLM-Phone企业定制&#xff1a;私有化部署与二次开发指南 1. 引言 1.1 技术背景与行业需求 随着移动智能终端的普及&#xff0c;用户对手机操作自动化的需求日益增长。传统脚本化或规则驱动的自动化工具&#xff08;如Auto.js&#xff09;在面对复杂界面变化和多任务逻辑…

无需联网的TTS解决方案|Supertonic助力音乐术语语音化学习

无需联网的TTS解决方案&#xff5c;Supertonic助力音乐术语语音化学习 1. 引言&#xff1a;乐理学习中的语音需求与挑战 在音乐理论学习过程中&#xff0c;大量专业术语以英文形式出现&#xff0c;如 Adagio&#xff08;柔板&#xff09;、Crescendo&#xff08;渐强&#xf…

BAAI/bge-m3功能实测:多语言文本匹配表现如何?

BAAI/bge-m3功能实测&#xff1a;多语言文本匹配表现如何&#xff1f; 1. 引言&#xff1a;多语言语义匹配的行业挑战 在构建全球化AI应用的过程中&#xff0c;跨语言语义理解能力正成为核心竞争力。传统中文专用嵌入模型&#xff08;如bge-large-zh系列&#xff09;虽在单语…

音乐节目制作:精准标注现场演出掌声与欢呼时间点

音乐节目制作&#xff1a;精准标注现场演出掌声与欢呼时间点 在音乐节目、演唱会或现场直播的后期制作中&#xff0c;如何高效、准确地标注观众的掌声、欢呼声等关键声音事件&#xff0c;一直是音视频编辑团队面临的挑战。传统的人工听辨方式不仅耗时耗力&#xff0c;还容易因…

SenseVoice Small智能笔记:语音转结构化数据

SenseVoice Small智能笔记&#xff1a;语音转结构化数据 1. 技术背景与核心价值 在智能语音交互日益普及的今天&#xff0c;传统的语音识别系统大多停留在“语音转文字”的初级阶段&#xff0c;难以满足复杂场景下的语义理解需求。SenseVoice Small 的出现打破了这一局限&…

从Photoshop到Rembg:AI智能抠图技术演进之路

从Photoshop到Rembg&#xff1a;AI智能抠图技术演进之路 1. 引言&#xff1a;图像去背景的技术演进与现实需求 在数字内容创作日益普及的今天&#xff0c;图像去背景&#xff08;Image Background Removal&#xff09;已成为设计、电商、广告等领域的基础操作。传统方式依赖人…

IndexTTS-2-LLM + 阿里Sambert双引擎容灾架构实战案例

IndexTTS-2-LLM 阿里Sambert双引擎容灾架构实战案例 1. 引言&#xff1a;智能语音合成的高可用挑战 随着AIGC技术的快速发展&#xff0c;文本到语音&#xff08;Text-to-Speech, TTS&#xff09;系统在有声读物、智能客服、播客生成等场景中广泛应用。然而&#xff0c;在实际…