Javassist

2023-11-18

1、简介

Javassist(JAVA programming ASSISTant)是在Java中编辑字节码的类库;它使Java程序能够在运行时定义一个新类,并在JVM加载是修改类文件。

我们常用到的动态特性主要是反射,在运行时查找对象属性、方法,修改作用域,通过方法名称调用方法等。在线的应用不会频繁使用反射,因为反射的性能开销较大。其实还有一种和反射一样强大的特性,但是开销却很低,他就是Javassist。

与其他类似的字节码编辑器不同,Javassist提供了两个级别的API:源级别和字节码级别。如果用户使用源级API,他们可以编辑类文件,而不知道Java字节码的规格。整个API只用Java语言的词汇来设计。您甚至可以以源文本的形式指定插入的字节码;Javassist在运行中编译它。另一方面,字节码级API允许用户直接编辑类文件作为其他编辑器。

在Javassist中,进行类表述的基本单元是CtClass(即“编译时的类”,compile time class)。组成程序的这些类会存储在一个ClassPool中,它本质上就是CtClass实例的一个容器。

ClassPool的实现使用了一个HashMap,其中key是类的名称,而value是对应的CtClass对象。

正常的Java类都会包含域、构造器以及方法。在CtClass中,分别与之对应的是CtField、CtConstructor和CtMethod。要定位某个CtClass,我们可以根据名称从ClassPool中获取,然后通过CtClass得到任意的方法,并做出我们的修改。如下所示:

Javassist提供的javassist.util.HotSwapper(3.1之前则是javassist.tools.HotSwapper,BTrace也是使用HotSwapper机制)类能够更加方便的动态重新加载类

虽然Javassist能够提供动态重新加载类的功能,不过由于它要求启用JPDA



作者:阿术和薇薇安
链接:https://www.jianshu.com/p/27337d7ca4c7
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

2、读取和写入字节码

类Javassist.CtClass是类文件的抽象表示形式。CtClass(编译时类)对象是处理类文件的句柄。下面的程序是一个非常简单的示例:

ClassPool pool = ClassPool.getDefault();

CtClass cc = pool.get("test.Rectangle");

cc.setSuperclass(pool.get("test.Point"));

cc.writeFile();

该程序首先获得一个ClassPool对象,它通过Javassist对象字节码修改。ClassPool对象是表示类文件的CtClass对象的容器。它根据需要读取类文件以构造CtClass对象,并记录构造对象以响应以后的访问。若要修改类的定义,用户必须首先从ClassPool对象获取对表示该类的CtClass对象的引用。ClassPool对象获得的,它被分配给一个变量cc。getDefault返回的ClassPool对象搜索默认的系统搜索路径。

可以修改从ClassPool对象获得CtClass对象(稍后将介绍如何修改CtClass的详细信息)。在上面的例子中,它被修改以便测试的超类。将矩形更改为类测试点。当最终调用CtClass()中的writeFile()时,此更改将反映在原始类文件中。

writeFile()时,此更改将反映在原始类文件中。

writeFile()将CtClass对象转换为类文件,并将其写入本地磁盘。Javassist还提供了一种直接获取修改后的字节码的方法。要获取字节码,请调用toBytecode():

byte[] b = cc.toBytecode();

您还可以直接加载CtClass:

Class clazz = cc.toClass();

toClass()请求当前线程的上下文类加载程序加载由CtClass表示的类文件。它返回一个表示已加载类的java.lang.Class对象。

2.1、定义类

ClassPool pool = ClassPool.getDefault();

CtClass cc = pool.makeCLass("Point");

此程序定义一个类Point,包括没有成员。可以使用CtNewMethod中声明的工厂方法创建点的成员方法,并在CtClass中追加到点与addMethod()。

makeClass()无法创新接口;可以使用 makeInterface () 做。接口中的成员方法可以在 CtNewMethod 中使用 abstractMethod () 创建。请注意, 接口方法是一种抽象方法。

2.2、冻结类

如果CtClass对象由writeFile()、toClass()或toBytecode()转换为类文件,Javassist将冻结该CtClass对象。那CtClass对象的进一步修改不被允许。这是为了在开发人员试图修改已加载的类文件时发出警告,因为JVM不允许重新加载类。

冻结的CtClass可以解冻,一遍允许对类定义进行修改。例如,

CtClass cc = ...;


cc.writeFile();

cc.defrost();

cc.setSuperclass(...); // OK since the class is not frozen.

2.3、类搜索路径

静态方法ClassPool.getDefault()返回的默认ClassPool将搜索底层JVM(Java虚拟机)具有的同一路径。如果某个程序在web应用程序服务器(如JBoss和Tomcat)上运行,则ClassPool对象可能无法找到用户类,因为这样的web应用程序服务器使用多个类加载器以及系统类加载程序。在这种情况下,必须将附加的类路径注册到ClassPool。假设池引用的是ClassPool对象:

pool.insertClassPath(new ClassClassPath(this.getClass()));

此语句注册用于加载次饮用的对象的类的类路径。可以将任何类对象用作参数而不是this.getClass()。用于加载由该类对象表示的类的类路径已注册。可以将目录名注册为类搜索路径。例如,下面的代码将目录/usr/local/javalib添加到搜索路径中:

ClassPool pool = ClassPool.getDefault();

pool.insertClassPath("/usr/local/javalib");

用户可以添加的搜索路径不仅是一个目录,而且可以是一个URL:

ClassPool pool = ClassPool.getDefault();

ClassPath cp - new URLClassPath("www.javassist.org", 80, "/java/", "org.javassist.");

pool.insertClassPath(cp);

此程序将"http://www.javassist.org:80/java/"添加到类搜索路径中。此URL仅用于搜索属于包组织javassist的类。例如,要加载类org.javassist.test.Main,将从以下内容获取其类文件:

http://www.javassist.org:80/java/org/javassist/test/Main.class

此外,您可以直接给ClassPool对象一个字节数组,并从该数组构造一个CtClass对象。为此,请使用ByteArrayClassPath.例如:

ClassPool cp = ClassPool.getDefault();

byte[] b = a byte array;

String name = class name;

cp.insertClassPath(new ByteArrayClassPath(name, b));

CtClass cc = cp.get(name);

获得的CtClass对象表示由b指定的类文件定义的类。ClassPool从给定的ByteArrayClassPath读取类文件(如果调用了get(),并且给定的类名为get()等于名称指定的类别。

如果您不知道该类的完全限定名,则可以在ClassPool中使用makeClass():

ClassPool cp = ClassPool.getDefault();

InputStream ins = an input stream for reading a class file;

CtClass cc = cp.makeClass(ins);

 makeClass()从给定输入流返回构造的CtClass对象。您可以使用makeClass()将类文件送到ClassPool对象。如果搜索路径包含大jar文件,这可能会提高性能。由于ClassPool对象根据需要读取类文件,因此它可能会反复搜索每个类文件的整个jar文件。makeClass()可用于优化此搜索。由makeClass()构造的CtClass保存在ClassPool对象中,不再读取类文件。

3、小结

本文简要介绍了javaassist及其简单用法。会有一些读者好奇:它和AOP有什么关系和区别?举个简单的例子即可:CGLib是动态代理的经典类库,其底层实现使用ASM,javaassist是类似ASM的东东。

4、参考文献

  1. javassist
  2. Java动态编程初探——Javassist
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Javassist 的相关文章

  • Grails 3.x bootRun 失败

    我正在尝试在 grails 3 1 11 中运行一个项目 但出现错误 失败 构建失败并出现异常 什么地方出了错 任务 bootRun 执行失败 进程 命令 C Program Files Java jdk1 8 0 111 bin java
  • 如何为最终用户方便地启动Java GUI程序

    用户想要从以下位置启动 Java GUI 应用程序Windows 以及一些额外的 JVM 参数 例如 javaw Djava util logging config file logging properties jar MyGUI jar
  • 为什么 i++ 不是原子的?

    Why is i Java 中不是原子的 为了更深入地了解 Java 我尝试计算线程中循环的执行频率 所以我用了一个 private static int total 0 在主课中 我有两个线程 主题 1 打印System out prin
  • Java JDBC:更改表

    我希望对此表进行以下修改 添加 状态列 varchar 20 日期列 时间戳 我不确定该怎么做 String createTable Create table aircraft aircraftNumber int airLineCompa
  • 多个 Maven 配置文件激活多个 Spring 配置文件

    我想在 Maven 中构建一个环境 在其中我想根据哪些 Maven 配置文件处于活动状态来累积激活多个 spring 配置文件 目前我的 pom xml 的相关部分如下所示
  • 列出jshell中所有活动的方法

    是否有任何命令可以打印当前 jshell 会话中所有新创建的方法 类似的东西 list但仅适用于方法 您正在寻找命令 methods all 它会打印所有方法 包括启动 JShell 时添加的方法 以及失败 被覆盖或删除的方法 对于您声明的
  • 反射找不到对象子类型

    我试图通过使用反射来获取包中的所有类 当我使用具体类的代码 本例中为 A 时 它可以工作并打印子类信息 B 扩展 A 因此它打印 B 信息 但是当我将它与对象类一起使用时 它不起作用 我该如何修复它 这段代码的工作原理 Reflection
  • 禁止的软件包名称:java

    我尝试从数据库名称为 jaane 用户名 Hello 和密码 hello 获取数据 错误 java lang SecurityException Prohibited package name java at java lang Class
  • Java TestNG 与跨多个测试的数据驱动测试

    我正在电子商务平台中测试一系列商店 每个商店都有一系列属性 我正在考虑对其进行自动化测试 是否有可能有一个数据提供者在整个测试套件中提供数据 而不仅仅是 TestNG 中的测试 我尝试不使用 testNG xml 文件作为机制 因为这些属性
  • 如何将 pfx 文件转换为 jks,然后通过使用 wsdl 生成的类来使用它来签署传出的肥皂请求

    我正在寻找一个代码示例 该示例演示如何使用 PFX 证书通过 SSL 访问安全 Web 服务 我有证书及其密码 我首先使用下面提到的命令创建一个 KeyStore 实例 keytool importkeystore destkeystore
  • 总是使用 Final?

    我读过 将某些东西做成最终的 然后在循环中使用它会带来更好的性能 但这对一切都有好处吗 我有很多地方没有循环 但我将 Final 添加到局部变量中 它会使速度变慢还是仍然很好 还有一些地方我有一个全局变量final 例如android Pa
  • 加密 JBoss 配置中的敏感信息

    JBoss 中的标准数据源配置要求数据库用户的用户名和密码位于 xxx ds xml 文件中 如果我将数据源定义为 c3p0 mbean 我会遇到同样的问题 是否有标准方法来加密用户和密码 保存密钥的好地方是什么 这当然也与 tomcat
  • 如何在 javadoc 中使用“<”和“>”而不进行格式化?

    如果我写
  • 在 Mac 上正确运行基于 SWT 的跨平台 jar

    我一直致力于一个基于 SWT 的项目 该项目旨在部署为 Java Web Start 从而可以在多个平台上使用 到目前为止 我已经成功解决了由于 SWT 依赖的系统特定库而出现的导出问题 请参阅相关thread https stackove
  • Eclipse Java 远程调试器通过 VPN 速度极慢

    我有时被迫离开办公室工作 这意味着我需要通过 VPN 进入我的实验室 我注意到在这种情况下使用 Eclipse 进行远程调试速度非常慢 速度慢到调试器需要 5 7 分钟才能连接到远程 jvm 连接后 每次单步执行断点 行可能需要 20 30
  • 在mockito中使用when进行模拟ContextLoader.getCurrentWebApplicationContext()调用。我该怎么做?

    我试图在使用 mockito 时模拟 ContextLoader getCurrentWebApplicationContext 调用 但它无法模拟 here is my source code Mock org springframewo
  • simpleframework,将空元素反序列化为空字符串而不是 null

    我使用简单框架 http simple sourceforge net http simple sourceforge net 在一个项目中满足我的序列化 反序列化需求 但在处理空 空字符串值时它不能按预期工作 好吧 至少不是我所期望的 如
  • Firebase 添加新节点

    如何将这些节点放入用户节点中 并创建另一个节点来存储帖子 我的数据库参考 databaseReference child user getUid setValue userInformations 您需要使用以下代码 databaseRef
  • 如何实现仅当可用内存较低时才将数据交换到磁盘的写缓存

    我想将应用程序生成的数据缓存在内存中 但如果内存变得稀缺 我想将数据交换到磁盘 理想情况下 我希望虚拟机通知它需要内存并将我的数据写入磁盘并以这种方式释放一些内存 但我没有看到任何方法以通知我的方式将自己挂接到虚拟机中before an O
  • 使用 xpath 和 vtd-xml 以字符串形式获取元素的子节点和文本

    这是我的 XML 的一部分

随机推荐

  • IntelliJ Idea 常用快捷键 列表(实战终极总结!!!!)

    自动代码 常用的有fori sout psvm Tab即可生成循环 System out main方法等boilerplate样板代码 例如要输入for User user users 只需输入user for Tab 再比如 要输入Dat
  • SQL BOY 4 款脚本工具利器

    对于正在运行的mysql 性能如何 参数设置的是否合理 账号设置的是否存在安全隐患 你是否了然于胸 俗话说工欲善其事 必先利其器 定期对你的MYSQL数据库进行一个体检 是保证数据库安全运行的重要手段 今天和大家分享几个mysql 优化的工
  • Java多线程工具类之循环栅栏计数器

    Java多线程下循环计数器 本文主要内容 CyclicBarrier 下文中凯哥就用cycBar来代替 定义介绍 举例说明 代码演示 从源码来看原理及总结 CyclicBarrier与CountDownLatch 下文就用CountDown
  • 多分类SVM支持向量机的matlab仿真

    目录 一 理论基础 二 核心程序 三 仿真结论 一 理论基础 支持向量机 Support Vector Machine SVM 是一种在统计学习基础上发展起来的机器学习方法 其最大特点是根据Vapnik结构风险最小化原则 它的基本模型是定义
  • 从0到1:如何建立一个大规模多语言代码生成预训练模型

    国产AI辅助编程工具CodeGeeX是一个使用AI大模型为基座的辅助编程工具 帮助开发人员更快的编写代码 可以自动完成整个函数的编写 只需要根据注释或Tab按键即可 它已经在Java JavaScript和Python等二十多种语言上进行了
  • 判断机器大端小端的方法

    Big Endian和Little Endian的定义如下 1 Little Endian就是低位字节排放在内存的低地址端 高位字节排放在内存的高地址端 2 Big Endian就是高位字节排放在内存的低地址端 低位字节排放在内存的高地址端
  • DAC0832数模转换芯片介绍及使用教程

    1 芯片简介 DAC0832是采样频率为八位的D A转换芯片 集成电路内有两级输入寄存器 使DAC0832芯片具备双缓冲 单缓冲和直通三种输入方式 D A转换结果采用电流形式输出 若需要相应的模拟电压信号 可通过一个高输入阻抗的线性运算放大
  • Python小实验2—产生式系统实验

    文章目录 1 实验内容 2 实验目的 3 实验思路 4 源代码 5 实验结果 1 实验内容 设已知初始事实存放在综合数据库中 该动物身上有 暗斑点 长脖子 长腿 奶 蹄 推理机构的工作过程 1 从规则库中取出r 检查其前提是否可与综合数据库
  • QT之QChart绘制动态曲线

    QT之QChart绘制动态曲线 1 头文件 2 值写入QLineSeries 3 创建QChart对象 添加坐标轴 4 创建QChartView 5 QChartView显示到窗口 6 完整例子 QChart的系列 QChartSeries
  • 数据量太大,DOM节点加载过多,怎么保证前端在渲染的时候页面不会卡(性能优化)

    一 定时器分批渲染 既然一次渲染10万条数据会造成页面加载速度缓慢 那么我们可以不要一次性渲染这么多数据 而是分批次渲染 比如一次10000条 分10次来完成 这样或许会对页面的渲染速度有提升 然而 如果这13次操作在同一个代码执行流程中运
  • 苹果今天发布了 iOS 14.5 的第一个开发者预览版

    苹果今天发布了 iOS 14 5 的第一个开发者预览版 其中一个重要的新功能是 iPhone 12 机型在双 SIM 卡模式下对 5G 的全球支持 此前该功能仅在中国大陆地区提供 海外 iPhone 12 机型同时配备了物理 SIM 卡槽和
  • windows利用msys2安装minGW64

    目录 下载mysy2 配置国内源 安装minGW64 下载mysy2 官网下载非常慢 所以我们可以选择从清华大学的源下载 清华大学的msys2源说明 下载msys2 x86 64 20220603 exe 配置国内源 pacman 的配置
  • springboot启动feign项目报错:Service id not legal hostname

    正经学徒 佛系记录 不搞事情 在feign项目中 定义接口调用服务 FeignClient name eureka client public interface TestInterface GetMapping value get Str
  • 99款高质量免费(X)HTML/CSS模板

    99款高质量免费 X HTML CSS模板 01 T 20 在线预览下载该模板 02 Shape 在线预览下载该模板 03 Your Business 在线预览下载该模板 04 Solitude 在线预览下载该模板 05 Fashion C
  • C# BackgroundWorker控件使用方法

    在C Winform开发中 若遇到大量数据操作或运算 通常UI界面卡死造成交互不良 解决方法 1 使用BackgroundWorker控件 2 使用多线程委托回调 本章先介绍该控件使用方法 界面展示 若没使用该控件 点击开始 进度条会滚动但
  • linux程序前后台切换

    1 怎么样使程序在后台执行 方法有很多 这里主要列举两种 假如我们有程序pso cpp 通过编译后产生可执行文件pso 我们要使pso在linux服务器后台执行 当客户端关机后重新登入服务器后继续查看本来在终端输出的运行结果 假设操作都在当
  • Python selenium —— selenium与自动化测试成神之路

    Python selenium selenium与自动化测试成神之路 忽然想谈谈自动化的学习路径 因为发现很多人总是急于求成 不懂该如何学习 在群里总是会遇到很多人问低级问题 写了一个selenium脚本 却执行失败 跑到群里来问 大神 这
  • 双指针模板

    核心思路 首先打一个 O n 2 O n 2 O n2 的暴力 然后考虑性质 当i j具有单调性的时候 那么我们才可以用双指针来优化 基础例题 最长连续不重复子
  • HTTPS加密流程

    HTTPS HTTPS 一 什么是HTTPS 二 什么是 加密 三 加密的方式有哪些 1 对称加密 2 非对称加密 3 中间人攻击 4 引入证书 HTTPS 一 什么是HTTPS HTTPS与HTTP一样都是应用层协议 与HTTPS不同的是
  • Javassist

    1 简介 Javassist JAVA programming ASSISTant 是在Java中编辑字节码的类库 它使Java程序能够在运行时定义一个新类 并在JVM加载是修改类文件 我们常用到的动态特性主要是反射 在运行时查找对象属性