java代理模式和字节码的探索

简介: 文章概览一. 静态代理二. JDK动态代理三. 反编译动态字节码


文章概览


一.  静态代理


二.  JDK动态代理


三.  反编译动态字节码


静态代理


如图所示,除了维护正常的一个实现类外(被代理类),还需要而外维护一个代理类,而且这些都是需要硬编码的,感觉挺麻烦的~


网络异常,图片无法展示
|


那么咱们先来快速感受下这个小例子👇


网络异常,图片无法展示
|


如上图所示


  1. 定义一个 接口,


  1. 代理类和被代理类都去实现该接口,同时代理类要聚合被代理类,


  1. 在客户端中调用这个代理类的方法,便完成了这个静态代理


结果如下~


网络异常,图片无法展示
|


这里利用 IDEA 生成了这个 UML 图,也挺直观的~


网络异常,图片无法展示
|


那么,快速感受下这个小例子后,你是否也觉得挺麻烦的呢?

比如:


  1. 如果接口新增方法,那么就得维护多一个类 🐷


  1. 接口越多,代理类越多 😵


所以为了解决这些缺点,咱们就需要来看看这个 JDK动态代理了~ 😄

果然,懒惰让世界变得更美好~ 哈哈(谁也不想多写些✍)


JDK动态代理


如图所示~

网络异常,图片无法展示
|


JDK 动态代理的精髓就在这两个步骤


  1. 实现 InvocationHandler 接口,重写 invoke 方法


  1. 通过 Proxy.newProxyInstance(classLoader,interfaces,invocationHandler) 方法获取代理类


01.  InvocationHandler


网络异常,图片无法展示
|


02.  Proxy.newProxyInstance


网络异常,图片无法展示
|


03.  


没有,就上面两步了😝


网络异常,图片无法展示
|


结果如下图~


网络异常,图片无法展示
|


04.  注意点


这里还要注意一点,Proxy.newProxyInstance 中的第二个参数,接口! 要用接口的实现类去获取接口(不能直接写通过 接口.class.getInterfaces() ,不然会抛出下面这个异常~ 🐖


动态代理异常 com.sun.proxy.$Proxy0 cannot be cast to


这里再多做一层封装即可~,就不用担心写错了😄


优化后如下👇


网络异常,图片无法展示
|


不过这样会代理实现类的所有接口,如果需要特殊定制的话,也可以将第二接口参数替换为 new Class[]{ISayService.class}, 即可。


本来还想继续探索下,看看底层到底怎么实现这个动态代理,结果看到又是调用 native 方法, 想了下还是算了先 哈哈哈 不然都更不出文章了😄


网络异常,图片无法展示
|


不过源码注释还是很重要的!😝  记录下这个 InvocationHandler 接口源码~

可以发现这最后一句介绍很有意思~


当在这个代理类上调用方法时,会将调用的方法编码后发到  InvocationHandler  实现类中进行处理


网络异常,图片无法展示
|


反编译


本以为到这就愉快的进入下一个小结~,结果突然想到 反编译动态代理class 文件 ,于是又开始折腾了 哈哈😝


网络异常,图片无法展示
|


一.  arthas


于是乎,我下了个 arthas ,在 window 上运行有点忐忑……

结果它真的一直运行不起来,没有报错,就一直卡在那里,没有出现  arthas  那个五颜六色的标志…… 让我一度怀疑我电脑坏了


先看下步骤吧~


demo 说明,如图,只是一个简单的 main 方法


  1. 打个断点


  1. 通过 jps 查看这个进程 ID


网络异常,图片无法展示
|


根据操作手册说明,window 上直接运行 as.bat 54792  命令就行了 这个 54792 就是这个 java 程序的 pid ,结果画面就变成这样了,一动不动的……


网络异常,图片无法展示
|


网络异常,图片无法展示
|


我也跟着瞎琢磨了大半天,后面想着会不会是因为我打了断点的原因,但是不打断点就结束了呀~


于是我尝试着将代码放到之前的 Springboot WEB 项目中,再次运行起来,结果真的可以了~


(中间还把内存搞崩了 哈哈哈,连  arthas 都启动不起来,提示我 pagesize 太小)🙃


网络异常,图片无法展示
|


开心滴打开浏览器 ~


哇瑟,终于正常了 🐷


网络异常,图片无法展示
|


我迫不及待的运行了这个 反编译代码语句: jad com.sum.proxy.$Proxy0 看看效果,结果


看到下面这一幕,有种恍然大悟的感觉~


难怪以前 debug 看到 JDK动态代理 时,都会出现一个 h 变量!😝


网络异常,图片无法展示
|


但是呢,我还没有看到 我自己写的动态代理类,主要是忘打断点查看~ 无奈 我又想着用别的办法折腾下,总感觉这么写太浪费了~ 为了测试这个 jdk动态代理 ,居然得把代码拷贝到 web 项目中运行,实在太扯了。


网络异常,图片无法展示
|


于是又开始折腾了 哈哈


二.  HSDB


搜素一番后,我又找到一个神器! jdk 自带的 HSDB 全名 HotSpot Debugger 😝

这个使用也比较简单,


  1. 启动命令:java -classpath "%JAVA_HOME%/lib/sa-jdi.jar" sun.jvm.hotspot.HSDB


  1. 通过 pid 找到对应的 java进程


  1. 点击 Tools 》class Browser 就可以打开下面这个窗口


  1. 输入 $Proxy 就可以查找了


  1. 点击目标类,然后点击 Create .class File  就会在你使用 该命令的目录下 创建这个字节码文件了


网络异常,图片无法展示
|


将该 class 文件 拷贝到 IDEA 中,打开即可


网络异常,图片无法展示
|


从这份代码中可以看到,该代理类不仅仅代理了 ISayService 这个接口中的 say  方法,连 ObjecttoStringhashCodeequals 这三个方法也都被代理了!

于是我尝试着运行下,果然没有骗我~ 哈哈哈😄


网络异常,图片无法展示
|


那么为啥要代理 Object 的这三个方法呢?


发现我们平时最多也就重写这三个方法了,其他方法基本都被  final 修饰,不能被重写,剩下可以被重写的方法就finalizeclone 了~ (更没用过了 😅)


三.  ProxyGenerator


这个是最简单的方法了,直接将 JDK动态代理 生成的字节码文件保存到工程目录下~

IDEA 的启动配置参数(VM options)中,加入下面的描述~


jdk8


-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true


jdk8之后的版本


-Djdk.proxy.ProxyGenerator.saveGeneratedFiles=true


如图所示~


网络异常,图片无法展示
|


运行后可以发现工程中多了下面这份字节码文件!


网络异常,图片无法展示
|


居然还有这操作!原理也简单的,小伙伴们在 IDEA 中双击 shift 键,搜索 ProxyGenerator 类就可以啦


网络异常,图片无法展示
|



总结


由于篇幅的原因,下文再来说说这个 Cglib 动态代理~ ,现在先来小结下~😝


一.静态代理


【优点】👉:


  1. 容易理解


  1. 编写简单


【缺点】👉:


  1. 随着接口方法的增加,或者接口的增加,需要同时维护代理类和被代理类,成本高


二.JDK动态代理


【优点】👉:


  1. 不用创建和维护代理类


【缺点】👉:


  1. 只有实现了接口的类才能被代理,有局限性


三.反编译


可以直接使用 JDK 自带的 HSDB 来完成反编译HotSpot Debugger ),也可以通过这个arthas ,或者通过 ProxyGenerator 类的 saveGeneratedFiles 属性来将生成的字节码保存到工程文件中,就可以快速地看到这个动态代理的字节码文件


四.本文小结图


这里 4ye 重新整理了下上面相关的知识点,分成下面两张图,小伙伴们如果需要可以直接在公众号后台回复 "代理模式1",或者微信我就行了~ 😝


代理图


网络异常,图片无法展示
|


反编译图


网络异常,图片无法展示
|



目录
相关文章
|
16天前
|
Arthas Java 测试技术
Java字节码文件、组成,jclasslib插件、阿里arthas工具,Java注解
Java字节码文件、组成、详解、分析;常用工具,jclasslib插件、阿里arthas工具;如何定位线上问题;Java注解
Java字节码文件、组成,jclasslib插件、阿里arthas工具,Java注解
|
4月前
|
设计模式 Java
Java一分钟之-设计模式:装饰器模式与代理模式
【5月更文挑战第17天】本文探讨了装饰器模式和代理模式,两者都是在不改变原有对象基础上添加新功能。装饰器模式用于动态扩展对象功能,但过度使用可能导致类数量过多;代理模式用于控制对象访问,可能引入额外性能开销。文中通过 Java 代码示例展示了两种模式的实现。理解并恰当运用这些模式能提升代码的可扩展性和可维护性。
49 1
|
15天前
|
Java API 开发者
【Java字节码操控新篇章】JDK 22类文件API预览:解锁Java底层的无限可能!
【9月更文挑战第6天】JDK 22的类文件API为Java开发者们打开了一扇通往Java底层世界的大门。通过这个API,我们可以更加深入地理解Java程序的工作原理,实现更加灵活和强大的功能。虽然目前它还处于预览版阶段,但我们已经可以预见其在未来Java开发中的重要地位。让我们共同期待Java字节码操控新篇章的到来!
|
13天前
|
Java API 开发者
【Java字节码的掌控者】JDK 22类文件API:解锁Java深层次的奥秘,赋能开发者无限可能!
【9月更文挑战第8天】JDK 22类文件API的引入,为Java开发者们打开了一扇通往Java字节码操控新世界的大门。通过这个API,我们可以更加深入地理解Java程序的底层行为,实现更加高效、可靠和创新的Java应用。虽然目前它还处于预览版阶段,但我们已经可以预见其在未来Java开发中的重要地位。让我们共同期待Java字节码操控新篇章的到来,并积极探索类文件API带来的无限可能!
|
4月前
|
设计模式 Java 数据库连接
【重温设计模式】代理模式及其Java示例
【重温设计模式】代理模式及其Java示例
|
4月前
|
设计模式 Java 程序员
【设计模式】JAVA Design Patterns——Bytecode(字节码模式)
【设计模式】JAVA Design Patterns——Bytecode(字节码模式)
|
1月前
|
设计模式 缓存 Java
【十一】设计模式~~~结构型模式~~~代理模式(Java)
文章详细介绍了代理模式(Proxy Pattern),这是一种对象结构型模式,用于给对象提供一个代理以控制对它的访问。文中阐述了代理模式的动机、定义、结构、优点、缺点和适用环境,并探讨了远程代理、虚拟代理、保护代理等不同代理形式。通过一个商务信息查询系统的实例,展示了如何使用代理模式来增加身份验证和日志记录功能,同时保持客户端代码的无差别对待。此外,还讨论了代理模式在分布式技术和Spring AOP中的应用,以及动态代理的概念。
【十一】设计模式~~~结构型模式~~~代理模式(Java)
|
30天前
|
监控 Java API
分布式链路监控系统问题之对Java应用实现字节码增强的方式的问题如何解决
分布式链路监控系统问题之对Java应用实现字节码增强的方式的问题如何解决
|
2月前
|
存储 Java
JAVA程序运行问题之编译生成的字节码在不同的平台上是否相同如何解决
JAVA程序运行问题之编译生成的字节码在不同的平台上是否相同如何解决
|
2月前
|
存储 设计模式 Java
Java面试题:解释代理模式的概念,并举例说明其应用场景。
Java面试题:解释代理模式的概念,并举例说明其应用场景。
29 0