百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

基于 Javassist 实现 Java 动态代理

haoteby 2025-01-03 16:08 2 浏览

阅读完本文你能 get 到的知识点

  • 什么是 Javassist
  • JDK 动态代理
  • 使用 Javassist 实现和 JDK 一样的效果

什么是Javassist

很多同学估计会对这个词有点陌生,但随着你关注的博主越来越多,知道的也越来越多,马上这篇文章就带你走进 Javassist 的世界

Javassist 和 ASM 一样是操作字节码的框架, Javassist 诞生于 1999 年,多少有点年头

使用 Javassist 可以在运行时定义一个新类,可以在 JVM 加载类文件时修改类文件

而且 Javassist 提供不同类型的API: 源码级别字节码级别

本文使用源码级别的 API ,所以你甚至可以在不懂字节码的前提下使用它,入手相对简单

但在性能上略逊于 ASM

JDK 动态代理

话不多说,先来回顾一下我们平时是怎么使用 JDK 动态代理的

Proxy

JDK 提供一个类 Proxy 用于生成代理类

调用类方法 newProxyInstance,传入的参数

  • ClassLoader loader : 定义代理类的类加载器
  • Class<?>[] interfaces : 代理类需要实现的所有接口
  • InvocationHandler h : 调用处理程序,方法调用都会分派到这里

InvocationHandler

InvocationHandler 是一个接口类,定义了调用方法

  • Object proxy : 生成的代理对象
  • Method method : 接口方法实例
  • Object[] args : 方法调用中传递的参数值

如何调用

调用 Proxy.newProxyInstance 生成代理对象, 传入参数接口InvocationHandler实现类的对象处理代理的逻辑

代码设计

在动手写代码之前,我们先花几分钟在脑海中设想一下我们需要生成的代理类是什么样子的?

这里先揭晓了

假设我们定义了一个接口类 LoginService

  • 定义的接口类

那么我们需要生成一个大概是这样的代理类

  • 首先必须得实现了定义的接口 LoginService
  • 接口的所有方法实现都调用都代理到 InvocationHandler 中

几个重要的类

从上面生成的代理类入手,我们生成的类

继承了父类 MObject

实现了需要代理的接口 LoginService

生成了类成员变量 LoginService_0, 这个对应 接口的定义的方法

实现了需要代理的接口方法 login

还有带参数 MInvocationHandler 的构造方法

MObject

MObject 是生成的代理类需要继承的父类,它的作用是存储了 MInvocationHandler(处理程序接口)

MInvocationHandler

同 JDK 自带的接口 InvocationHandler ,用于实现代理方法的处理逻辑

MProxy

同 JDK 自带的类 Proxy

提供生成代理对象的方法 newProxyInstance

编码开始

在经过代码设计之后,我们的脑海里应该有思路了,那就开始动手了

整个过程中比较重要的部分应该就是 MProxy 类了

在 MProxy 里面我们需要实现两大功能:

  • 生成代理类字节码
  • 根据字节码生成对象

生成代理类字节码

  1. 生成代理类名称
  2. 生成空类
  3. 给类设置需要实现的接口
  4. 添加类成员变量
  5. 实现接口方法

生成代理类名称

这一步相对简单,为了防止生成的代理类重名

这里拼接了所有需要代理的接口全限定类名,通过字符串 "_" 连接

生成空类

首先我们需要根据新的类名生成一个空的类,注意类名不要重复了,不然会污染了原有的类

  • ClassPool : 类池,存储所有类的信息,会将类名->类信息 存储到 HashTable 里, 可通过 ClassPool.getDefault() 获取实例
  • CtClass: 代表一个类
  • classPool.makeClass : 生成新的类

给类设置需要实现的接口

这里同样通过 ClassPool 的 get 方法获取到所有传入接口的 CtClass 定义

再调用 setInterfaces 方法给生成的类设置多个接口

添加类成员变量

因为我们调用 MInvocationHandler 的 invoke 方法时需要传入的第二个参数是被代理方法的 Method实例

所以将这个方法的存储到类成员变量中

CtField 代表着一个变量

传入类型、变量名 生成一个 CtField 实例

通过 setModifiers 方法设置变量的修饰符为 static + private

因为这里还要设置变量的值

调用 getFieldInitCode 生成初始化代码

为了获取 Method 对象 ,这里生成了反射的代码去获取

实例: Class.forName(类名).getMethod(方法名, Class<?>... 方法的参数类型);

生成了类成员变量之后,接下来该到实现接口的方法了

实现接口方法

这里需要实现接口的方法

可以通过 CtNewMethod.copy 方法去拷贝需要实现的方法,不要直接使用原来的 CtMethod , 防止污染

拿到新的 CtMethod ,我们需要设置它的方法体、设置修饰符为 public

重点来看看怎么生成方法体代码

这里根据方法返回的类型调用不同的方法

  • void : 没有返回值的 调用 getMethodBodyCodeByVoid
  • 基本数据类型 : 调用 getMethodBodyCodeByPrimitive
  • 其它类型 : 调用 getMethodBodyCode

那按顺序来看,不需要返回值的

那我们需要生成的代码是长这样的

super.h.invoke(this, 对应的类成员变量, new Object[]{方法参数});

这个有个语法需要知道: $0 代表这方法的第一个参数,懂字节码的应该知道非构造方法的第一个入参是 一个隐式的 this ,指向对象本身

new Object[]{} 里面就可以用 11 12 代表着方法的参数了

返回值是基本数据类型的,需要调用调用包装类型对应的拆箱方法 如

Boolean.parseBoolean()

所以和上面生成步骤的区别在于 前后生成了对于基本类型的 parse 代码

最后的返回其它类型的也比较简单

直接生成强转的代码 如 (String)

以上步骤走完,前期准备工作算是做完了,接下来就要根据生成的字节码来实例化对象了

根据字节码生成对象

要根据字节码来生成对象,第一步我们需要编写自定义的类加载器,通过类加载器加载字节码

  1. 编写自定义加载器 MClassLoader ,继承类 ClassLoader
  2. 提供 add 方法将类名映射到对应的字节数组
  3. 重写 ClassLoader 类的 findClass 方法,使用我们生成的字节数组生成类

在 MProxy 中调用 MClassLoader 加载并实例化对象

  1. 加载类 mClassLoader.loadClass(clasName)
  2. 获取带 MInvocationHandler 参数类型的构造
  3. 实例化对象 constructor.newInstance(h)

效果演示

好了,上面的代码已经编写完了,那么现在就来对比一下 JDK 自带的 Proxy 和我们自己实现的 Proxy 的效果

我们定义一个需要代理的接口 LoginService

这里按照 Java 的基本数据类型 以及它们对应的包装类 定义了16个接口方法

分别实现了代理类 CusMInvocationHandler 和 CusJdkInvocationHandler

这两个代理类的实现是一样的

区别在于实现的接口一个是我们定义的 MInvocationHandler

另一个是 JDK 的 InvocationHandler

执行入口类

Main 类分别生成了 Proxy 和 MProxy 的代理对象

然后执行代理对象的各个方法

来看看实现的效果,左边是 JDK 的动态代理,右边是使用 Javassist 实现的动态代理


作者:MinXie
链接:https://juejin.cn/post/7168030376080703495

相关推荐

Python的RSA操作(私钥与公钥)(python rsa 公钥解密)

RSA是1977年由罗纳德·李维斯特(RonRivest)、阿迪·萨莫尔(AdiShamir)和伦纳德·阿德曼(LeonardAdleman)一起提出的。当时他们三人都在麻省理工学院工作。RSA...

RSA在日益互联的世界网络中安全性能如何?

KeyFactor公司(美国一家领先的安全数字身份管理解决方案提供商及网络安全行业权威机构)研究表明,许多物联网设备制造商正在生成不安全的RSA密钥,182个RSA证书里就有一个可能会被破解,由于不正...

让频谱分析更高效,澄清RSA使用中的一些误解

从事射频应用的研究人员、工程师和技术人员通常都能充分理解频谱分析仪的用途和优点,无论是传统的扫频分析仪(TSA)还是更现代的矢量信号分析仪(VSA)。他们熟练掌握这些重要射频仪器的关键规范和工作...

微软公告:Win10/Win11将不再支持短于2048位的RSA密钥证书

IT之家3月16日消息,微软近日发布公告,表示即将放弃短于2048位的RSA密钥证书。在公告中微软并未明确弃用时间,对于用户来说,这其实有利于构建更安全的上网环境。IT之家翻译微软公告...

目前已知的最强加密算法RSA(rsa加密算法的优点)

前面有人让我讲解一下RSA算法,今天我就用我所学的知识讲解一下,首先我们先了解一下RSARSA是一种非对称加密算法,1977年由罗纳德·李维斯特(RonRivest)、阿迪·萨莫尔(AdiSha...

韩国 CryptoLab 将在 2025年 RSA 大会发布加密人脸识别解决方案

据美通社4月23日报道,韩国同态加密网络安全企业CryptoLab宣布,将于4月24日在2025年RSA大会上,首次发布加密人脸识别(EFR)方案,为生物识别安全难题提供创新解法。当前,人脸识...

应对变化!盘点RSA2015十大热门产品

4月20日-24日,全球知名信息安全峰会RSAConference2015在美国旧金山召开。作为IT安全领域的权威科技大会,RSA大会不仅会邀请各地区著名安全专家出席与分享,更吸引汇集了全球众多顶...

RSA 2015主题:变化挑战当今的安全理念

1“变化”成为RSA2015主题4月20日-24日,全球知名信息安全峰会RSAConference2015在美国旧金山召开。作为IT安全领域的权威科技大会,RSA大会不仅会邀请各地区著名安全专家出...

非对称加密——一文看懂RSA(非对称加密详解)

非对称加密----RSA的使用"非对称加密也叫公钥密码:使用公钥加密,使用私钥解密"在对称密码中,由于加密和解密的密钥是相同的,因此必须向接收者配送密钥。用于解密的密钥必须被配送给...

RSA算法详解(rsa算法图解)

什么是RSA前面文章我们讲了AES算法,AES算法是一种是对称加密算法,本文我们来介绍一个十分常用的非对称加密算法RSA。非对称加密算法也叫公钥密码算法,通过生成的公私钥来对明文密文进行加密解密。R...

升级SSH后ssh-rsa失效?一文带你轻松解决!

背景今天刚给Linux桌面系统完成升级,结果SSH连接突然“罢工”了,还弹出了这个报错信息:...

历史回顾RSA大会:25年,十个瞬间(rsa conference)

国家安全局、Clipper芯片、苹果对决FBI、禁止ShowGirl——RSA大会都经历过。RSA需要你RSA这个词代表一家密码及安全厂商,也代表着世界上最大的网络安全展会,它今年在旧...

RSA 加密技术详解(rsa的加密原理是什么)

RSA的安全性基于数学难题的理论安全:RSA的安全性主要基于大质数分解和离散对数问题这两个数学难题。在RSA加密算法中,公钥包含一个大整数N,它是两个大质数p和q的乘积。攻击者如果想要破解RSA加密,...

「游戏开发」请别再说Unity不如Unreal:Unity室内场景 + 光照练习 3

关注“indienova”,挖掘独立游戏的更多乐趣引言上两节慢吞吞的补了很多技术实现的细节,感觉要是把用到的所有技术细节都过一遍可能还需要若干篇文章。所以决定先把整体的流程这篇好玩的写了,以后再慢慢补...

再做一个Android!Google发布第二代VR眼镜Cardboard

在去年的GoogleI/O上,Google向所有与会者发放了一款名为Cardboard的纸盒版虚拟现实眼镜,相比OculusRift等颇为酷炫的VR头盔,第一代Cardboard着实糙得很。不过,...