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,一经查实,立即删除!

相关文章

【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…

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;算术运算…

电商系列之促销

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

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

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

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

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

【JavaEE初阶系列】——文件操作 IO 之 文件系统操作

目录 &#x1f4dd;认识文件 &#x1f6a9;树型结构组织 和 目录 &#x1f388;绝对路径和相对路径 &#x1f6a9;文件类型 &#x1f4dd;文件系统操作 &#x1f388;File 概述 &#x1f388;File类的使用 1. 绝对路径 vs 相对路径 2. 路径分隔符 3. 静态成员变量 4…

【C语言】翻译环境与运行环境

一、前言 在我们学习C语言的时候&#xff0c;第一个接触的程序就是&#xff1a;在屏幕上打印” hello word! “&#xff0c;可当时的我们却未去深入的理解与感悟&#xff0c;一个程序代码是如何运行的&#xff1b;而这一期的博客&#xff0c;则是带着我们&#xff0c;通过C代码…

mac电脑安装redis教程

1、下载地址 Download | RedisRedisYou can download the last Redis source files here. For additional options, see the Redis downloads section below.Stable (7.2)Redis 7.2 …https://redis.io/download/#redis-downloads 2、安装 2.1 解压下载后的压缩文件 2.2 进入…

Vulnhub:WESTWILD: 1.1

目录 信息收集 arp nmap nikto whatweb WEB web信息收集 dirmap enm4ulinux sumbclient get flag1 ssh登录 提权 横向移动 get root 信息收集 arp ┌──(root㉿ru)-[~/kali/vulnhub] └─# arp-scan -l Interface: eth0, type: EN10MB, MAC: 0…

LeetCode-236. 二叉树的最近公共祖先【树 深度优先搜索 二叉树】

LeetCode-236. 二叉树的最近公共祖先【树 深度优先搜索 二叉树】 题目描述&#xff1a;解题思路一&#xff1a;递归判断解题思路二&#xff1a;0解题思路三&#xff1a;0 题目描述&#xff1a; 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖…

linux操作系统的进程状态

这个博客只是为了自己复习用的&#xff01;&#xff01;&#xff01; 冯诺依曼体系结构 计算机是由一个一个硬件组成的 输入设备&#xff1a;键盘&#xff0c;鼠标&#xff0c;扫描仪&#xff0c;写板等等 中央处理器&#xff08;CPU&#xff09;:含有运算器和控制器等 输出单…

【算法练习】27:冒泡排序学习笔记

一、冒泡排序的算法思想 原理&#xff1a;以升序为例&#xff0c;冒泡排序通过从左往右连续比较相邻元素&#xff0c;当发现左边比右边大就交换元素。从左往右依次比较完称为“一轮”&#xff0c;每轮结束之后就会固定一个元素。 时间复杂度&#xff1a;2层循环&#xff0c;所以…

不讲概念,讲实操,mysql 分表模糊查询、分页查询 及 merge 表的使用

1.Mysql merge合并表的要求 1.合并的分表必须是 MyISAM 引擎&#xff0c;MyISAN引擎是不支持事务的。2.Merge表只保证合表后数据唯一性&#xff0c;合表前的数据可能会存在重复。3.表的结构必须一致&#xff0c;包括索引、字段类型、引擎和字符集。4.删除 tb_member1 分表正确…