javassist 基本用法

article/2025/9/15 4:32:42

Javassist是一个能够操作字节码框架,在学习的过程中存在了一些问题,用博客的方式记录下来,希望对大家有所帮助。


一、实例功能

    学习的实例来自于 IBM developer   主要功能实现计算一个方式具体的执行时间. 

二、代码实例

  

package org.java.javassist.one;/*** 该类并不是对StringBuilder进行解释,而是提供了中方式,方便我们来使用javassist的一些细节* @author xianglj* @date 2016/7/13* @time 9:59*/
public class StringBuilderTest {/*** 假如我们现在需要计算该程序的计算时间:* 则可以标记开始时间(start)和结束时间(end)* 最终的执行时间为(end - start)的值* @param length* @return*/public String buildString(int length) {String result = "";for(int i = 0; i<length; i++ ) {result += (i %26 + 'a');}return result;}public static void main(String[] args) {/*** class.getName()返回的字符串中,不仅包括了类的名称,同时也包含了该类所在的包名称* <pre>*     格式:*     packagename.classname* </pre>*/
//        System.out.println(StringBuilderTest.class.getName());StringBuilderTest test = new StringBuilderTest();if(null != args) {for(int i = 0, len = args.length; i<len; i++) {String result = test.buildString(Integer.parseInt(args[i]));System.out.println("result:" + result);}}}
}
这是一个基本实例,通过一个参数传入的length,来生成length长度的字符串。(但是该方式存在一个很验证的性能问题,就是当length的长度组件增大时,该方法的效率就会越低),但在此处不必关系方法的效率问题

三、解决方案

    1) 第一个中解决方案,也是最直观的方式,就是在进入方法时,记录一个当前时间 start , 当代码执行完成之后,获取当前时间 end , 然后采用 (end - start)的方式,来获取代码执行时间

    2) 第二中方式,我们采用Javassit框架来实现:

        (1)  使用通过ClassPool 来获取 CtClass对象

        (2)  从CtClass对象中,获取buildString()的方法

        (3)  为buildString()方法添加代码块

特别说明:

         为方法添加代码块,有三种方式可以实现

       

 * <pre>*     1. 添加的代码不能引用在方法中其他地方定义的局部变量。这种限制使我不能在 Javassist 中使用在源代码中使用的同样方法实现计时代码,*        在这种情况下,我在开始时添加的代码中定义了一个新的局部变量,并在结束处添加的代码中引用这个变量。**     2. 我 可以在类中添加一个新的成员字段,并使用这个字段而不是局部变量。不过,这是一种糟糕的解决方案,*        在一般性的使用中有一些限制。例如,考虑在一个递归方法中会发生的事情。每次方法调用自身时,上次保存的开始时间值就会被覆盖并且丢失。**     3. 我可以保持原来方法的代码不变,只改变方法名,然后用原来的方法名增加一个新方法。* </pre>
前两种方式,在实现上都有一定的问题,所以我们采用第三种方式实现,会比较的容易实现

代码如下:

package org.java.javassist.one;import javassist.*;import java.io.IOException;/*** 通过Javassist来为需要实现计算的方法前后各加上一个拦截器,* 依次来实现方法计算的时间* <pre>*     1. 添加的代码不能引用在方法中其他地方定义的局部变量。这种限制使我不能在 Javassist 中使用在源代码中使用的同样方法实现计时代码,*        在这种情况下,我在开始时添加的代码中定义了一个新的局部变量,并在结束处添加的代码中引用这个变量。**     2. 我 可以在类中添加一个新的成员字段,并使用这个字段而不是局部变量。不过,这是一种糟糕的解决方案,*        在一般性的使用中有一些限制。例如,考虑在一个递归方法中会发生的事情。每次方法调用自身时,上次保存的开始时间值就会被覆盖并且丢失。**     3. 我可以保持原来方法的代码不变,只改变方法名,然后用原来的方法名增加一个新方法。* </pre>* @author xianglj* @date 2016/7/13* @time 10:07*/
public class JavassistTiming {public static void main(String[] args) {//开始获取class的文件javassist();}public static void javassist() {//开始获取class的文件try {
//            String classFileName = StringBuilderTest.class.getName();String classFileName = "org.java.javassist.one.StringBuilderTest";CtClass ctClass = ClassPool.getDefault().getCtClass(classFileName);if(ctClass == null) {System.out.println("Class File (" + classFileName + ") Not Found.....");} else {addTiming(ctClass, "buildString");//为class添加计算时间的过滤器ctClass.writeFile();}Class<?> clazz = ctClass.toClass();StringBuilderTest test = (StringBuilderTest) clazz.newInstance();test.buildString(2000);} catch (NotFoundException e) { //类文件没有找到e.printStackTrace();} catch (CannotCompileException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}}/*** 为方法添加拦截器:* <pre>*     构造拦截器方法的正文时使用一个 java.lang.StringBuffer 来累积正文文本(这显示了处理 String 的构造的正确方法,*     与在 StringBuilder 的构造中使用的方法是相对的)。这种变化取决于原来的方法是否有返回值。*     如果它 有返回值,那么构造的代码就将这个值保存在局部变量中,这样在拦截器方法结束时就可以返回它。*     如果原来的方法类型为 void ,那么就什么也不需要保存,也不用在拦截器方法中返回任何内容。* </pre>* @param clazz 方法所属的类* @param method 方法名称*/private static void addTiming(CtClass clazz, String method) throws NotFoundException, CannotCompileException {//获取方法信息,如果方法不存在,则抛出异常CtMethod ctMethod = clazz.getDeclaredMethod(method);//将旧的方法名称进行重新命名,并生成一个方法的副本,该副本方法采用了过滤器的方式String nname = method + "$impl";ctMethod.setName(nname);CtMethod newCtMethod = CtNewMethod.copy(ctMethod, method, clazz, null);/** 为该方法添加时间过滤器,用来计算时间。* 这就需要我们去判断获取时间的方法是否具有返回值*/String type = ctMethod.getReturnType().getName();StringBuffer body = new StringBuffer();body.append("{\n long start = System.currentTimeMillis();\n");if(!"void".equals(type)) {body.append(type + " result = ");}//可以通过$$将传递给拦截器的参数,传递给原来的方法body.append(nname + "($$);\n");//  finish body text generation with call to print the timing//  information, and return saved value (if not void)body.append("System.out.println(\"Call to method " + nname + " took \" + \n (System.currentTimeMillis()-start) + " +  "\" ms.\");\n");if(!"void".equals(type)) {body.append("return result;\n");}body.append("}");//替换拦截器方法的主体内容,并将该方法添加到class之中newCtMethod.setBody(body.toString());clazz.addMethod(newCtMethod);//输出拦截器的代码块System.out.println("拦截器方法的主体:");System.out.println(body.toString());}
}

可能会出现的问题:

1. LinkageError  我在实践的过程中,由于想方便,采用了StringBuilderTest.class.getName() 的方法来代替手写的字符串,这个时候,我在使用CtClass.toClass()时出现了异常,异常原因大致为: 一个Class只能被加载一次,因为我们在调用toClass()方法时,会去再加载Class,所以会出现重复加载。

官方文档如下 Javassist Tutorial


        初步学习就到这里,后面会继续更新关于该框架的学习


http://chatgpt.dhexx.cn/article/NjlGR3rt.shtml

相关文章

javassist对已有的类进行操作

准备工作&#xff1a; 首先创建一个java项目&#xff0c;然后导入javassist.jar包 创建一个注解 package com.chengyu.javassist;public interface Auto {String name();int year(); }创建一个接口 public interface Earth { } 创建两个类 public class Pepelo{ } packag…

【Javassist】快速入门系列03 使用Javassist实现方法异常处理

系列文章目录 01 在方法体的开头或结尾插入代码 02 使用Javassist实现方法执行时间统计 03 使用Javassist实现方法异常处理 文章目录 系列文章目录前言引入Javassist jar包使用Javassist实现方法异常处理 总结说明 前言 上一章我们介绍了使用使用Javassist实现了对方法执行时…

【Javassist】快速入门系列04 使用Javassist更改整个方法体

系列文章目录 01 在方法体的开头或结尾插入代码 02 使用Javassist实现方法执行时间统计 03 使用Javassist实现方法异常处理 04 使用Javassist更改整个方法体 文章目录 系列文章目录前言引入Javassist jar包使用Javassist更改整个方法体 总结说明 前言 上一章我们介绍了使用Ja…

Java字节码编程之非常好用的javassist

我为什么要研究这个&#xff1f; 因为我在开发一个框架的时候需要用到。 我开发的这个框架&#xff0c;有一个注解&#xff0c;当用户输入变量名&#xff0c;类名的时候&#xff0c;我这个框架可以为其自动生成一个对象&#xff0c;并加载到内存中供以后使用。 这个小功能可…

javassist学习

Java 字节码以二进制的形式存储在 .class 文件中&#xff0c;每一个 .class 文件包含一个 Java 类或接口。Javaassist 就是一个用来 处理 Java 字节码的类库。它可以在一个已经编译好的类中添加新的方法&#xff0c;或者是修改已有的方法&#xff0c;并且不需要对字节码方面有深…

Javassist

Javassist Javassist是一个开源的分析、编辑和创建Java字节码的类库&#xff0c;可以直接编辑和生成Java生成的字节码。 相对于bcel, asm等这些工具&#xff0c;开发者不需要了解虚拟机指令&#xff0c;就能动态改变类的结构&#xff0c;或者动态生成类。javassist简单易用&…

[Java基础]—Javassist

Javassist Javassist (JAVA programming ASSISTant) 是在 Java 中编辑字节码的类库;它使 Java 程序能够在运行时定义一个新类, 并在 JVM 加载时修改类文件。原理与反射类似&#xff0c;但开销相对较低。 常用API ClassPool getDefault : 返回默认的 ClassPool 是单例模式的&…

Javassist基本用法

Javassist概述 Javassist是可以动态编辑Java字节码的类库。它可以在Java程序运行时定义一个新的类&#xff0c;并加载到JVM中&#xff1b;还可以在JVM加载时修改一个类文件&#xff0c;添加新的方法&#xff0c;或者是修改已有的方法。Javassist使用户不必关心字节码相关的规范…

Java中的高性能字节码工具:Javassist

前言 一般常见的动态方法调用使用Reflection或者字节码生成技术。虽然JDK已对反射进行了优化但在追求性能的场景中仍然显得性能不佳。本文即是介绍一个面向程序员友好的字节码操作类库javassist。根据benchmark其展现的性能已几乎无异于直接调用。 开源地址&#xff1a;javas…

Java字节码技术javassist

一、Javassist入门 &#xff08;一&#xff09;Javassist是什么 Javassist是可以动态编辑Java字节码的类库。它可以在Java程序运行时定义一个新的类&#xff0c;并加载到JVM中&#xff1b;还可以在JVM加载时修改一个类文件。Javassist使用户不必关心字节码相关的规范也是可以编…

java--javassist学习

Javassist是一个开源的分析、编辑和创建Java字节码的类库。是由东京工业大学的数学和计算机科学系的 Shigeru Chiba &#xff08;千叶 滋&#xff09;所创建的。它已加入了开放源代码JBoss 应用服务器项目,通过使用Javassist对字节码操作为JBoss实现动态AOP框架。javassist是jb…

javassist使用指南

目录 一、快速入门1.1 创建class文件1.2 ClassPool的相关方法1.3 CtClass的相关方法1.4 CtMethod的相关方法1.5 调用生成的类对象1.5.1 通过反射调用1.5.2 通过接口调用 1.6 修改现有的类对象 二、将类冻结三、类搜索路径四、$开头的特殊字符五、ProxyFactory的使用 我们知道Ja…

systemd介绍

由来 Linux 的启动一直采用init进程&#xff0c;这种方法有两个缺点。一是启动时间长。init进程是串行启动&#xff0c;只有前一个进程启动完&#xff0c;才会启动下一个进程&#xff0c;二是启动脚本复杂。init进程只是执行启动脚本&#xff0c;不管其他事情。脚本需要自己处理…

systemd man手册

SYSTEMD&#xff08;1&#xff09;systemd SYSTEMD&#xff08;1&#xff09; 名称 systemd&#xff0c;init-systemd系统和服务管理器 概要 /lib/systemd/systemd [OPTIONS...]init [OPTIONS...] {COMMAND}描述 systemd是Linux操作系统的系统和服务管理器。在启动时作为PID的…

systemd wsl 测试笔记

文章目录 systemd 简介WSL systemdsystemctljournalctlhello serviceSleep 与 Timeout 测试Requires 测试After 测试 systemd 简介 Linux 从关闭到运行, 完整的启动和启动过程有三个主要部分: 硬件启动(Hardware boot): 初始化系统硬件Linux 引导(Linux boot): 加载 Linux 内核…

Linux systemd启动流程

以Ubuntu 18.04.2 LTS为例&#xff0c;列出Systemd启动target中涉及单元(Unit). default.target ( graphical.target by default) graphical.targetRequiresmulti-user.targetWantsdisplay-manager.serviceConflictsrescue.service rescue.targetAftermulti-user.target rescu…

关于 Linux中systemd的一些笔记

写在前面 嗯&#xff0c;准备RHCA&#xff0c;学习整理这部分知识博文内容涉及&#xff1a; systemd简述对于unit的信息的介绍通过systemctl命令控制Service unit的DemoService unit配置文件内容,权值的一些介绍 傍晚时分&#xff0c;你坐在屋檐下&#xff0c;看着天慢慢地黑下…

Linux-Systemd

目录 一、Systemd概述 二、Systemd优势 2.1兼容性 2.2启动速度 2.3systemd 提供按需启动能力 2.4采用 linux 的 cgroups 跟踪和管理进程的生命周期 2.5启动挂载点和自动挂载的管理 2.6实现事务性依赖关系管理 2.7日志服务 2.8 依赖关系 2.9systemd 事务 三、unit(单…

Systemd 简介

一 概述 Linux 服务管理有两种方式service和systemctl。而systemd是Linux系统最新的初始化系统(init)&#xff0c;作用是提高系统的启动速度&#xff0c;尽可能启动较少的进程&#xff0c;尽可能更多进程并发启动&#xff0c;systemd对应的进程管理命令就是systemctl。值得一提…

systemd简介

Systemd 是一个专用于 Linux 操作系统的系统与服务管理器,其目的是要取代Unix时代以来一直在使用的init系统。 systemd概述Systemd 是 Linux 系统中最新的初始化系统(init),它主要的设计目标是克服 sysvinit 固有的缺点,提高系统的启动速度。 systemd框架图 根据 Linux 惯…