00-JAVA基础-javassist字节码操作

2024-04-04 21:36
文章标签 java 基础 操作 字节 00 ssist

本文主要是介绍00-JAVA基础-javassist字节码操作,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

字节码操作

什么是字节码

Java字节码(Java bytecode)是Java虚拟机(JVM)执行的一种虚拟指令格式。它是由Java编译器生成的,基于栈的指令集,用于在Java虚拟机上执行。字节码文件包含了JVM能够识别的指令,当JVM执行这些指令时,会通过解释器逐条读取字节码文件中的指令,并将其解释成机器码执行。

JAVA动态性的实现方式

  • 字节码操作
  • 反射

运行时操作字节码可以让我们实现如下功能:

  • 动态生成新的类
  • 动态改变某个类的结构(添加/删除/修改 新的属性/方法)

运行时操作字节码的优势:反射开销小,性能高

什么是Javassist

Javassist是一个开源的分析、编辑和创建Java字节码的类库。它是jboss的一个子项目,主要优点在于简单且快速。它允许开发者直接使用Java编码的形式,而无需了解虚拟机指令,就能动态地改变类的结构或动态生成类。

Javassist提供了一组简单易用的API,使开发者能够动态地创建、修改、分析Java类,而无需关心底层的字节码细节。
它的核心API包括ClassPool和CtClass,其中ClassPool用于跟踪和控制所操作的类,而CtClass则提供了检查类数据(如字段和方法)以及在类中添加新字段、方法和构造函数等功能。

Javassist的功能与jdk自带的反射功能类似,但更为强大。它能在Java程序运行时定义新的类并加载到JVM中,也可以在JVM加载时修改一个类文件。
此外,Javassist还用于生成新的Java类以适应不同的需求,例如在实现ORM框架时;或者用于编写Mock框架,以便在测试过程中控制类的行为;以及实现性能监控工具,对方法的执行时间进行统计和分析,诊断潜在问题。

Javassist常用API

Javassist是一个功能强大的Java字节码操作和分析库,它提供了一组常用的API来简化对字节码的修改和生成。以下是一些Javassist的常用API及其功能描述:

模块方法描述
ClassPool模块--
-ClassPool.getDefault()获取默认的ClassPool对象,它用于存储和操作类信息。
-pool.get(className)根据类名获取对应的CtClass对象。
-pool.makeClass(name)创建一个新的类。
CtClass模块--
-ctClass.setName(name)设置类的名称。
-ctClass.setSuperclass(superclass)设置类的父类。
-ctClass.getDeclaredMethods()获取类声明的所有方法。
-ctClass.getDeclaredFields()获取类声明的所有字段。
-ctClass.makeClassFile()获取该类的ClassFile对象,用于更底层的字节码操作。
-ctClass.writeFile()将修改后的类写入文件。
CtMethod模块--
-ctMethod.setName(name)设置方法名称。
-ctMethod.setModifier(modifier)设置方法修饰符。
-ctMethod.setBody(body)设置方法的字节码体。
-ctMethod.insertBefore(src)在方法体前插入字节码。
-ctMethod.insertAfter(src)在方法体后插入字节码。
CtField模块--
-ctField.setName(name)设置字段名称。
-ctField.setType(type)设置字段类型。
-ctField.setModifier(modifier)设置字段修饰符。
-ctField.setInitialValue(value)设置字段的初始值。
CtConstructor-与CtMethod类似,用于操作类的构造函数。
javassist.bytecode 模块-ClassFile、MethodInfo、CodeAttribute等类提供了对字节码的低级别访问接口,允许直接读取和修改字节码指令。
其他--
-new Annotation()创建注解。
-new Bytecode()直接操作字节码。

这些API只是Javassist功能的一部分,Javassist还提供了许多其他功能,如接口修改、异常处理、导入类等。使用时,建议参考Javassist的官方文档或相关教程以获取更详细的信息和示例代码

测试案例

案例1

package com.demo.demo1;import javassist.*;import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;/*** 是用javassist创建一个UserDo,使用构造方法设置值,打印结果** @author Anna.* @date 2024/4/4 17:32*/
public class JavassistDemo {public static void main(String[] args) throws Exception {// 创建ClassPool对象ClassPool classPool = ClassPool.getDefault();// 创建一个新的类CtClass ctClass = classPool.makeClass("com.demo.entity.UserDo");// 创建属性CtField fieldName = CtField.make("private String name;", ctClass);CtField fieldAge = CtField.make("private Integer age;", ctClass);// 将属性添加到类中ctClass.addField(fieldName);ctClass.addField(fieldAge);// 创建get方法CtMethod getName = CtMethod.make("public String getName(){return this.name;}", ctClass);CtMethod getAge = CtMethod.make("public Integer getAge(){return this.age;}", ctClass);// 将方法添加到类中ctClass.addMethod(getName);ctClass.addMethod(getAge);// 创建构造器CtConstructor constructor1 = CtNewConstructor.make("public UserDo(){}", ctClass);CtConstructor constructor2 = CtNewConstructor.make("public UserDo(String name,Integer age){this.name = name; this.age = age;}", ctClass);// 添加构造器ctClass.addConstructor(constructor1);ctClass.addConstructor(constructor2);// 写入对应文件String path = JavassistDemo.class.getResource("/").getPath();ctClass.writeFile(path);// 使用反射获取对象并初始化,调用getName方法URLClassLoader urlClassLoader = URLClassLoader.newInstance(new URL[]{new URL("file:/" + path)});Class<?> clazz = urlClassLoader.loadClass("com.demo.entity.UserDo");Object o1 = clazz.getDeclaredConstructor(String.class, Integer.class).newInstance("张三", 20);// 调用getName方法Method getName1 = clazz.getMethod("getName");System.out.println(getName1.invoke(o1));}
}

执行结果

在这里插入图片描述

案例2

package com.demo.demo2;import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;import java.lang.reflect.Method;/*** 1 通过Javassist 获取UserDo的class* 2 在setName之前添加打印信息* 3 修改getName第14行输出内容System.out.println("调用了getName方法"); 为System.out.println("通过avassist 修改了调用了getName方法");** @author Anna.* @date 2024/4/4 19:58*/
public class JavassistDemo {public static void main(String[] args) throws Exception {// 创建ClassPool对象ClassPool classPool = ClassPool.getDefault();// 获取UserDoCtClass ctClass = classPool.getCtClass("com.demo.demo2.UserDo");// 在setName之前添加打印信息// 获取调用setName方法CtMethod setName = ctClass.getDeclaredMethod("setName", new CtClass[]{classPool.get("java.lang.String")});// 设置方法调用前打印信息 $0 表示this,$1表示第一个参数setName.insertBefore("System.out.println(\"javassiist在setName前设置打印:\" + $1);");// 获取调用getName方法CtMethod getName = ctClass.getDeclaredMethod("getName");// 在getName第14行插入输出内容为System.out.println("通过avassist 修改了调用了getName方法");getName.insertAt(14, "System.out.println(\"通过avassist 修改了调用了getName方法\");");getName.insertAfter("System.out.println(\"javassiist在getName执行结束后return前设置打印\" );");// 通过反射调用新生成的方法Class<?> clazz = ctClass.toClass();// 初始化Object o = clazz.getDeclaredConstructor().newInstance();// 调用setNameMethod setName1 = clazz.getMethod("setName", String.class);setName1.invoke(o, "张三");// 调用getNameMethod getName1 = clazz.getMethod("getName");System.out.println(getName1.invoke(o));}
}

执行结果

在这里插入图片描述

gitee源码

git clone https://gitee.com/dchh/JavaStudyWorkSpaces.git

这篇关于00-JAVA基础-javassist字节码操作的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/876874

相关文章

Python调用Orator ORM进行数据库操作

《Python调用OratorORM进行数据库操作》OratorORM是一个功能丰富且灵活的PythonORM库,旨在简化数据库操作,它支持多种数据库并提供了简洁且直观的API,下面我们就... 目录Orator ORM 主要特点安装使用示例总结Orator ORM 是一个功能丰富且灵活的 python O

Java实现检查多个时间段是否有重合

《Java实现检查多个时间段是否有重合》这篇文章主要为大家详细介绍了如何使用Java实现检查多个时间段是否有重合,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录流程概述步骤详解China编程步骤1:定义时间段类步骤2:添加时间段步骤3:检查时间段是否有重合步骤4:输出结果示例代码结语作

Java中String字符串使用避坑指南

《Java中String字符串使用避坑指南》Java中的String字符串是我们日常编程中用得最多的类之一,看似简单的String使用,却隐藏着不少“坑”,如果不注意,可能会导致性能问题、意外的错误容... 目录8个避坑点如下:1. 字符串的不可变性:每次修改都创建新对象2. 使用 == 比较字符串,陷阱满

Java判断多个时间段是否重合的方法小结

《Java判断多个时间段是否重合的方法小结》这篇文章主要为大家详细介绍了Java中判断多个时间段是否重合的方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录判断多个时间段是否有间隔判断时间段集合是否与某时间段重合判断多个时间段是否有间隔实体类内容public class D

IDEA编译报错“java: 常量字符串过长”的原因及解决方法

《IDEA编译报错“java:常量字符串过长”的原因及解决方法》今天在开发过程中,由于尝试将一个文件的Base64字符串设置为常量,结果导致IDEA编译的时候出现了如下报错java:常量字符串过长,... 目录一、问题描述二、问题原因2.1 理论角度2.2 源码角度三、解决方案解决方案①:StringBui

Java覆盖第三方jar包中的某一个类的实现方法

《Java覆盖第三方jar包中的某一个类的实现方法》在我们日常的开发中,经常需要使用第三方的jar包,有时候我们会发现第三方的jar包中的某一个类有问题,或者我们需要定制化修改其中的逻辑,那么应该如何... 目录一、需求描述二、示例描述三、操作步骤四、验证结果五、实现原理一、需求描述需求描述如下:需要在

Java中ArrayList和LinkedList有什么区别举例详解

《Java中ArrayList和LinkedList有什么区别举例详解》:本文主要介绍Java中ArrayList和LinkedList区别的相关资料,包括数据结构特性、核心操作性能、内存与GC影... 目录一、底层数据结构二、核心操作性能对比三、内存与 GC 影响四、扩容机制五、线程安全与并发方案六、工程

JavaScript中的reduce方法执行过程、使用场景及进阶用法

《JavaScript中的reduce方法执行过程、使用场景及进阶用法》:本文主要介绍JavaScript中的reduce方法执行过程、使用场景及进阶用法的相关资料,reduce是JavaScri... 目录1. 什么是reduce2. reduce语法2.1 语法2.2 参数说明3. reduce执行过程

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2

Java调用DeepSeek API的最佳实践及详细代码示例

《Java调用DeepSeekAPI的最佳实践及详细代码示例》:本文主要介绍如何使用Java调用DeepSeekAPI,包括获取API密钥、添加HTTP客户端依赖、创建HTTP请求、处理响应、... 目录1. 获取API密钥2. 添加HTTP客户端依赖3. 创建HTTP请求4. 处理响应5. 错误处理6.