Java的三种代理模式实现

代理模式的定义:

Provide a surrogate or placeholder for another object to control access to it.(为其他对象提供一种代理以控制对这个对象的访问。)

简单说,就是设置一个中间代理来控制访问原目标对象,达到增强原对象的功能和简化访问方式的目的。

图片

代理模式通用类图

Java实现代理模式分为两类三种,两类是静态代理动态代理,动态代理又可以分为JDK动态代理CGLIB动态代理

图片

Java实现代理模式

静态代理

静态代理比较简单,代理类需要实现和目标接口类一样的接口。

图片

Solver静态代理类图

  • 接口类:ISolver

    public interface ISolver {void solve();
    }
    
  • 目标类:Solver

    public class Solver implements ISolver {@Overridepublic void solve() {System.out.println("疯狂掉头发解决问题……");}
    }
    
  • 代理类:SolverProxy,代理类也要实现接口,并且还要维护一个目标对象。

    public class SolverProxy implements ISolver {//目标对象private ISolver target;public SolverProxy(ISolver target) {this.target = target;}@Overridepublic void solve() {System.out.println("请问有什么能帮到您?");target.solve();System.out.println("问题已经解决啦!");}
    }
    
  • 客户端;Client

    public class Client {public static void main(String[] args) {//目标对象:程序员ISolver developer = new Solver();//代理:客服小姐姐SolverProxy csProxy = new SolverProxy(developer);//目标方法:解决问题csProxy.solve();}
    }
    
  • 运行结果

    请问有什么能帮到您?
    疯狂掉头发解决问题……
    问题已经解决啦!
    

我们看到,通过静态代理,可以在不修改目标对象的前提下扩展目标对象的功能。

但是,它也有一些问题:

  • 冗余:由于代理对象要实现与目标对象一致的接口,会产生过多的代理类。

  • 维护性不佳:一旦接口增加方法,目标对象与代理对象都要进行修改。

JDK动态代理

JDK动态代理利用了JDK反射机制,动态地在内存中构建代理对象,从而实现对目标对象的代理功能。

它主要用到了两个反射类的API:

  • java.lang.reflect Proxy| static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h):返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序

  • java.lang.reflect InvocationHandler|Object invoke(Object proxy, Method method, Object[] args) :在代理实例上调用目标方法,并返回结果。

我们来看看使用JDK动态代理之后的客服代理场景。

图片

JDK动态代理类图

  • 接口类:ISolver

    public interface ISolver {void solve();
    }
    
  • 目标类:Solver,目标类需要实现接口类。

    public class Solver implements ISolver {@Overridepublic void solve() {System.out.println("疯狂掉头发解决问题……");}
    }
    
  • 动态代理工厂:ProxyFactory,这里的动态代理工厂,不需要实现接口,直接采用反射的方式生成一个目标对象的代理对象实例。

    ps:这里用了一个匿名内部类的方法,还有一种方法,动态代理类实现InvocationHandler接口,大体上类似,就不再给出例子了。

    public class ProxyFactory {// 维护一个目标对象private Object target;public ProxyFactory(Object target) {this.target = target;}// 为目标对象生成代理对象public Object getProxyInstance() {return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("请问有什么可以帮到您?");// 调用目标对象方法Object returnValue = method.invoke(target, args);System.out.println("问题已经解决啦!");return null;}});}
    }
  • 客户端:Client

    客户端生成一个代理对象实例,通过代理对象调用目标对象方法的时候,就会进入invoke()方法,最后是通过反射的方式调用目标对象的方法。

    public class Client {public static void main(String[] args) {//目标对象:程序员ISolver developer = new Solver();//代理:客服小姐姐ISolver csProxy = (ISolver) new ProxyFactory(developer).getProxyInstance();//目标方法:解决问题csProxy.solve();}
    }
    
  • 运行结果:

    请问有什么可以帮到您?
    疯狂掉头发解决问题……
    问题已经解决啦!
    

我们简单总结一下静态代理和动态代理的主要区别:

图片

静态代理动态代理最主要区别

  • 静态代理在编译时就已经实现,编译完成后代理类是一个实际的class文件

  • 动态代理是在运行时动态生成的,即编译完成后没有实际的class文件,而是在运行时动态生成类字节码,并加载到JVM中

我们也观察到,JDK动态代理,目标对象必须得实现接口,也就是说它是面向接口的,假如我们不想要接口怎么办呢?

Cglib动态代理

CGLIB(Code Generation Library)是一个基于ASM的字节码生成库,它允许我们在运行时对字节码进行修改和动态生成,它是通过继承来实现的。

我们来看看使用Cglib之后,我们的客服代理是什么样的:

图片

Cglib动态代理类图

  • 引入依赖:Cglib是第三方类库,需要引入依赖

            <dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.2.5</version></dependency>
    
  • 目标类:Solver,这里目标类不用再实现接口。

    public class Solver {public void solve() {System.out.println("疯狂掉头发解决问题……");}
    }
    
  • 动态代理工厂:

    public class ProxyFactory implements MethodInterceptor {//维护一个目标对象private Object target;public ProxyFactory(Object target) {this.target = target;}//为目标对象生成代理对象public Object getProxyInstance() {//工具类Enhancer en = new Enhancer();//设置父类en.setSuperclass(target.getClass());//设置回调函数en.setCallback(this);//创建子类对象代理return en.create();}@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("请问有什么可以帮到您?");// 执行目标对象的方法Object returnValue = method.invoke(target, args);System.out.println("问题已经解决啦!");return null;}}
    
  • 客户端:Client

    public class Client {public static void main(String[] args) {//目标对象:程序员Solver developer = new Solver();//代理:客服小姐姐Solver csProxy = (Solver) new ProxyFactory(developer).getProxyInstance();//目标方法:解决问题csProxy.solve();}
    }
    
  • 运行结果

    请问有什么可以帮到您?
    疯狂掉头发解决问题……
    问题已经解决啦!
    

我们可以看到Cglib动态代理和JDK动态代理最大的区别就是:

  • 使用JDK动态代理的对象必须实现一个或多个接口

  • 使用Cglib动态代理的对象则无需实现接口,达到代理类无侵入。

我们还需要注意:

  • CGLib不能对声明为final的方法进行代理,因为是通过继承父类的方式实现,如果父类是final的,那么就无法继承父类。

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

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

相关文章

领域驱动架构(DDD)建模

一、背景 常见的软件开发方式是拿到产品需求后&#xff0c;直接考虑数据库中表应该如何设计&#xff0c;这种方式已经将设计与业务需求脱节&#xff0c;而更多的是直接考虑应该如何实现了&#xff0c;这有点本末倒置。而DDD是从领域(问题域)为出发点进行的设计方法。 领域驱动…

记录 | centos源码编译bazel

tensorflow的源码编译依赖于 bazel 这里进行 bazel 的源码编译 1、安装依赖 sudo yum install -y java-11-openjdk sudo yum install -y java-11-openjdk-devel sudo yum install -y protobuf-compiler zip unzip2、知悉要安装的 bazel 的版本 务必安装受支持的 Bazel 版本…

Linux下c开发

编程环境 Linux 下的 C 语言程序设计与在其他环境中的 C 程序设计一样&#xff0c; 主要涉及到编辑器、编译链接器、调试器及项目管理工具。编译流程 编辑器 Linux 中最常用的编辑器有 Vi。编译连接器 编译是指源代码转化生成可执行代码的过程。在 Linux 中&#xff0c;最常用…

网络安全行业大模型调研总结

随着人工智能技术的发展&#xff0c;安全行业大模型SecLLM&#xff08;security Large Language Model&#xff09;应运而生&#xff0c;可应用于代码漏洞挖掘、安全智能问答、多源情报整合、勒索情报挖掘、安全评估、安全事件研判等场景。 参考&#xff1a; 1、安全行业大模…

uniapp 打开文件管理器上传(H5、微信小程序、android app三端)文件

H5跟安卓APP 手机打开的效果图&#xff1a; Vue页面&#xff1a; <template><view class"content"><button click"uploadFiles">点击上传</button></view> </template><script>export default {data() {return…

web:[GXYCTF2019]BabyUpload(文件上传、一句话木马、文件过滤)

题目 页面显示为文件上传 随便上传一个文件看看 上传一个文本文件显示 上传了一个图片显示 上传包含一句话木马的图片 上传了一个包含php一句话木马的文件&#xff0c;显示如上 换一个写法 上传成功 尝试上传.htaccess&#xff0c;上传失败&#xff0c;用抓包修改文件后缀 …

LangChain 23 Agents中的Tools用于增强和扩展智能代理agent的功能

LangChain系列文章 LangChain 实现给动物取名字&#xff0c;LangChain 2模块化prompt template并用streamlit生成网站 实现给动物取名字LangChain 3使用Agent访问Wikipedia和llm-math计算狗的平均年龄LangChain 4用向量数据库Faiss存储&#xff0c;读取YouTube的视频文本搜索I…

Apache Flink(四):Flink 其他实时计算框架对比

🏡 个人主页:IT贫道_大数据OLAP体系技术栈,Apache Doris,Clickhouse 技术-CSDN博客 🚩 私聊博主:加入大数据技术讨论群聊,获取更多大数据资料。 🔔 博主个人B栈地址:豹哥教你大数据的个人空间-豹哥教你大数据个人主页-哔哩哔哩视频 根据前文描述我们知道Flink主要处…

LNMP网站架构分布式搭建部署(编译安装)

目录 一、数据库编译安装 二、nginx编译安装 三、php编译安装 三、通过nfs将三台不同的主机资源共享 四、基础测试 五、完成WordPress站点部署 六、完成bbs论坛站点部署 一、数据库编译安装 1、先下载安装包到/opt目录中&#xff0c;最好选择mysql-boost-5.7.44.tar.gz版…

步进电机驱动芯片TB6600HG部分翻译

有些参数没看懂。一边设计&#xff0c;一边修正。 目录 1.芯片梗概 2.引脚配置 1)引脚含义 2)内部逻辑 3.功能详解 1&#xff09;励磁模式设置 2&#xff09;功能设置 3&#xff09;初始模式 4&#xff09;100% 电流设置(电流值) 5&#xff09;OSC 6&#xff09;衰减…

python 使用 AppiumService 类启动appium server

一、前置说明 在Appium的1.6.0版本中引入了AppiumService类&#xff0c;可以很方便的通过该类来管理Appium服务器的启动和停止。 二、操作步骤 import osfrom appium.webdriver.appium_service import AppiumService as OriginalServerfrom libs import pathclass AppiumSer…

windows判断exe应用程序是否在使用的bat脚本

脚本 REM 查询进程是否存在 tasklist|findstr /i "mysqld.exe">nul &&echo y >2.log ||echo n >2.log REM 读取文本内容赋值给变量 set /P resu<2.log if %resu% y (echo process in use ) else (echo process not in use )我们已mysqld.exe…

C++ day58 每日温度 下一个更大元素

题目1&#xff1a;739 每日温度 题目链接&#xff1a;每日温度 对题目的理解 temperature[i]表示每天的温度&#xff0c;返回数组answer&#xff0c;answer[i]指对于第i天&#xff0c;下一个更高温度最近出现在几天后&#xff0c;如果气温在这之后都不会升高&#xff0c;用0…

720度vr虚拟家居展厅提升客户的参观兴致

VR虚拟展厅线上3D交互展示的优势有以下几点&#xff1a; 打破了场馆的展示限制&#xff0c;可展示危险性制品、珍贵稀有物品、超大型设备等&#xff0c;同时提供了更大的展示空间和更丰富的展示内容。 可提供企业真实环境的实时VR全景参观&#xff0c;提升潜在客户信任度。 提供…

JAVA导出PDF(一)

思路一&#xff1a;直接导出pdf 使用freemarker和itext把html转pdf&#xff0c;存在中文乱码&#xff0c;宋体不识别&#xff0c;需下载simsun.ttc字体&#xff0c;空白占位符不识别等等问题&#xff0c;对前端依赖性较大&#xff0c;不推荐&#xff1b; 适用范围&#xff1a;…

批量操作Excel的四个方法(求和、移动、对比、合并)

Excel文件肯定少不了保存大量数据&#xff0c;那么在使用excel的时候会不会要大批量数据进行操作&#xff1f;今天分享4个快速使用excel操作的小技巧。希望能够帮大家提高excel制作效率。 技巧一&#xff1a;快速求和 当你想要分别得到行列的总和&#xff0c;我们可以选中表格…

看图学源码之 CopyOnWriteArrayList 源码分析

基本简介&#xff1a; 是Java中的一个线程安全的List实现&#xff0c;也是ArrayList 的安全版本&#xff0c;所以就不会有ArrayList 的并发修改异常 的问题产生了 原理 每次在对 List 进行修改时&#xff0c;创建一个新的副本&#xff08;即拷贝&#xff09;&#xff0c;而不…

计算机组成原理—存储系统

文章目录 存储系统分层RAM存储器DRAM刷新地址线复用 ROM存储器疑问 提升主存访问速度双端口RAM多模块存储器单体多字存储器多体并行存储器 主容量扩展cache高速缓存器cache与主存映射&#xff08;读策略&#xff09;替换策略cache写策略 存储方式页式存储器虚拟存储器 计组真的…

【EI会议征稿】第三届密码学、网络安全和通信技术国际会议(CNSCT 2024)

第三届密码学、网络安全和通信技术国际会议&#xff08;CNSCT 2024&#xff09; 2024 3rd International Conference on Cryptography, Network Security and Communication Technology 随着互联网和网络应用的不断发展&#xff0c;网络安全在计算机科学中的地位越来越重要&…