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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/792248.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python实战:打造简易四则运算计算器

Python实战&#xff1a;打造简易四则运算计算器 简介&#xff1a;本文将指导您编写一个简易的Python计算器程序&#xff0c;该程序支持基本的加减乘除运算。通过此教程&#xff0c;您将学习到Python编程语言的基础知识&#xff0c;并学会如何利用这些知识构建实用的应用程序。…

【JSON2WEB】 12基于Amis-admin的动态导航菜单树

【JSON2WEB】01 WEB管理信息系统架构设计 【JSON2WEB】02 JSON2WEB初步UI设计 【JSON2WEB】03 go的模板包html/template的使用 【JSON2WEB】04 amis低代码前端框架介绍 【JSON2WEB】05 前端开发三件套 HTML CSS JavaScript 速成 【JSON2WEB】06 JSON2WEB前端框架搭建 【J…

matlab 的PWM OUT代码在2023b与2024a中都报错

2023b运行程序内容&#xff1a; Monitor & Tune frequency and duty cycle of PWM signals on STMicroelectronics STM32F4xx Based Hardware Board Signal Monitoring and Parameter Tuning of Generated PWM Output - MATLAB & Simulink - MathWorks 中国报错内容&…

手搓ajax的封装

ajax的封装 前言 每一次发送请求我们都需要重复创建实例&#xff0c;配置请求 发送请求 获取数据&#xff0c;为了能够使用更加方便&#xff0c;我们对于ajax请求的步骤进行封装 同步的ajax请求 同步封装ajax请求可以通过返回值的方式返回后端的数据请求参数 type 请求方式ur…

Megatron-DeepSpeed-GPU-多机训练

Megatron-DeepSpeed-cuda-多机训练 1.从ngc拉取pytorch:24.03-py3镜像2.安装nvidia-docker、创建容器3.安装Megatron-DeepSpeed环境4.安装openmpi和ssh服务5.拷贝公钥6.安装pdsh7.升级protobuf8.准备数据集9.创建配置文件10.开始测试本文演示了Megatron-DeepSpeed-GPU-多机训练…

Flume 拦截器概念及自定义拦截器的运用

文章目录 Flume 拦截器拦截器的作用拦截器运用1.创建项目2.实现拦截器接口3.编写事件处理逻辑4.拦截器构建5.打包与上传6.编写配置文件7.测试运行 Flume 拦截器 在 Flume 中&#xff0c;拦截器&#xff08;Interceptors&#xff09;是一种可以在事件传输过程中拦截、处理和修改…

FreeRtos入门-4 事件组与同步点

事件组 事件组 同步点 创建 xEventGroupCalc xEventGroupCreate();//1&#xff0c;创建事件组 xEventGroupSyc xEventGroupCreate() 设置 xEventGroupSetBits(xEventGroupCalc,(1<<0));//设置事件组bit0 位 xEventGroupSync(xEventGroupSyc,BUSYING,ALL,portMAX…

VB 通过COM接口解析PSD文件

最近有PS测评的需求&#xff0c;故而想到了解析psd文件&#xff0c;目的就是为了获取文档信息和图层信息&#xff1b;获取PS的图像信息有很多方式&#xff0c;有过程性的&#xff0c;比如监听PS的各种操作事件&#xff1b;有结果性的&#xff0c;比如本文写的解析PSD文件。 0.…

使用pip安装geopandas(24.4更新)

geopandas是我们用Python进行地理分析常用的库&#xff0c;在数据处理、分析、制图等场景中有着极为广泛的应用&#xff0c;但是在安装过程中会出现各种问题。​geopandas的安装方式有很多&#xff0c;今天我们选取较为简单的pip来进行geopandas的安装。 ​首先&#xff0c;我…

内部类(InnerClass)

概述 什么是内部类 将一个类A定义在另一个类B里面&#xff0c;里面的那个类A就称为内部类&#xff08;InnerClass&#xff09;&#xff0c;类B则称为外部类&#xff08;OuterClass&#xff09;。 为什么要声明内部类呢 具体来说&#xff0c;当一个事物A的内部&#xff0c;还有…

Java web第一次作业

1.学会用记事本编写jsp文件&#xff0c;并放进tomcat的相关目录下&#xff0c;运行。 源代码&#xff1a; <% page contentType"text/html;charsetUTF-8" language"java" %> <html> <head> <title>我的第一个JSP页面</ti…

JavaSE——运算符

1. 概念 运算符是一种用于执行特定操作的符号或关键字。在编程中&#xff0c;运算符用于对变量、常量和表达式进行操作&#xff0c;以产生一个结果。 作为一门计算机语言&#xff0c; Java 也提供了一套丰富的运算符来操纵变量。 Java 中运算符可分为以下&#xff1a;算术运算…

Vue3实现图片懒加载

通过第三方插件VueUse实现图片懒加载。 1、需要安装依赖 npm i vueuse/core2、定义懒加载的插件 xxx/index.js // useIntersectionObserver&#xff1a;响应式监听目标元素的可见性。 import { useIntersectionObserver } from vueuse/core// 定义懒加载插件 export const …

电商系列之促销

> 插&#xff1a;AI时代&#xff0c;程序员或多或少要了解些人工智能&#xff0c;前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。 坚持不懈&#xff0c;越努力越幸运&#xff0c;大家…

nginx: 集群环境配置搭建

nginx 集群环境搭建 1 ) 概述 nginx 本身就应该选择性能强劲的机器同时为了满足更多流量的需求, 多台nginx 机器做集群来满足强大的需求故而&#xff0c;我们需要一个负载均衡器&#xff0c;以及多台nginx的机器 这里负载均衡器应该有主从和热备&#xff0c;目前先使用一台来描…

算法沉淀——动态规划篇(子数组系列问题(下))

算法沉淀——动态规划篇&#xff08;子数组系列问题&#xff08;下&#xff09;&#xff09; 前言一、等差数列划分二、最长湍流子数组三、单词拆分四、环绕字符串中唯一的子字符串 前言 几乎所有的动态规划问题大致可分为以下5个步骤&#xff0c;后续所有问题分析都将基于此 …

Java Math类、System类、Runtime类

Math类 Java中的Math类是一个包含各种数学方法的内置类&#xff0c;它提供了一系列静态方法&#xff0c;用于执行各种数学计算。具体来说&#xff0c;Math类中的方法可以分为以下几类&#xff1a; 基本数学运算&#xff1a;包括加法、减法、乘法和除法等。三角函数&#xff1…

JavaScript条件判断

JavaScript使用if () { … } else { … }来进行条件判断。 例如&#xff0c;根据年龄显示不同内容&#xff0c;可以用if语句实现如下&#xff1a; var age 20; if (age > 18) { // 如果age > 18为true&#xff0c;则执行if语句块 alert(‘adult’); } else { // 否则执行…

CSS之第一个CSS样式和CSS选择符

前端这些博客&#xff0c;我觉得都是固定的语法&#xff0c;故而不会以过多的文字进行描述&#xff0c;本系列博文均以实例和代码介绍的方式进行&#xff0c;主要按照代码进行。不会以过多的文字描述。 第一个CSS样式 <!DOCTYPE html> <html lang"en">…