gradle学习二 利用javassist api修改class字节码

2023-05-16

一 前言

Javassist (Java Programming Assistant) makes Java bytecode manipulation simple. It is a class library for editing bytecodes in Java; it enables Java programs to define a new class at runtime and to modify a class file when the JVM loads it. Unlike other similar bytecode editors, Javassist provides two levels of API: source level and bytecode level. If the users use the source-level API, they can edit a class file without knowledge of the specifications of the Java bytecode. The whole API is designed with only the vocabulary of the Java language. You can even specify inserted bytecode in the form of source text; Javassist compiles it on the fly. On the other hand, the bytecode-level API allows the users to directly edit a class file as other editors.

Javassist 提供了java类库,用于方便操控Java字节码。功能包括:运行时创建java class,修改class。与其他同类工具(asm等)不同的是,Javassist提供了两个层面的API:

1.java代码层

2.字节码层

通过java代码层,开发者即时对字节码不是很熟悉,也可以非常方便快速的完成字节码的修改。

二 示例

定义一个Bean类,代码如下

public class Bean {

    public static void show(String text) {
        System.out.print("hey man " + text);
    }

    public static void main(String[] argv) {
        show("add timing ");
    }
}

直接运行,会打印
hey man add timing

如果想要统计show方法的执行时间,我们可以在写代码的时候直接在show方法体开始和结束的地方加上计时的代码,这样很简单。但是假如我们期望动态修改show方法,直接修改Test.class类字节码,那么可以通过Javassist来完成。具体方法如下:

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;

import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.CtNewMethod;
import javassist.NotFoundException;


public class JassistLearn {

    public static void main(String[] argv) {
        try {
            CtClass clas = ClassPool.getDefault().get("Bean");
            addTiming(clas,"show");
        } catch (NotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (CannotCompileException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    private static void addTiming(CtClass clas, String mname)
        throws NotFoundException, CannotCompileException, IOException {
        /* Retrieves methods with the specified name among the methods
        * declared in the class.  Multiple methods with different parameters
        * may be returned.*/
        CtMethod mold = clas.getDeclaredMethod(mname);
        //修改当前方法名称为一个临时名称
        String nname = mname+"Impl";
        mold.setName(nname);
         /* Creates a copy of a method with a new name.
         * This method is provided for creating
         * a new method based on an existing method.
         * This is a convenience method for calling
         * {@link CtMethod#CtMethod(CtMethod, CtClass, ClassMap) this constructor}.
         * See the description of the constructor for particular behavior of the copying.
         * 拷贝一个新的方法,内容跟当前方法一致,方法名与当前方法原有名称一致。后面主要修改新方法的
         * 方法体的内容。 
         * */
        CtMethod mnew = CtNewMethod.copy(mold, mname, clas, null);
        String type = mold.getReturnType().getName();
        StringBuffer body = new StringBuffer();
        //在新方法体开始处增加一个变量start标识当前执行时间点
        body.append("{\nlong start = System.currentTimeMillis();\n");
        if (!"void".equals(type)) {
            body.append(type + " result = ");
        }
        //原方法运行,格式为方法名+参数,($$)代表参数
        body.append(nname + "($$);\n");
        //在原方法执行后打印当前时间点。统计前后时间差可以算出原方法的执行耗时
        body.append("System.out.println(\"Call to method " + mname +
            " took \" +\n (System.currentTimeMillis()-start) + " +
            "\" ms.\");\n");
        if (!"void".equals(type)) {
            body.append("return result;\n");
        }
        body.append("}");
        //将方法体设置到新方法内  
        mnew.setBody(body.toString());
        //将新方法添加到原方法所在类
        clas.addMethod(mnew);
        //更改原方法所在class文件,刷新到磁盘,完成字节码修改
        clas.writeFile("D:/eclipse/server/JavaSistLearn/bin");
    }
}

代码注释包含了每一步对应的意义,执行过程总结一下主要是:
1. 通过ClassPool找到Bean.class类文件
2. 修改show方法名称为一个临时名称showImpl
3. 拷贝一个新方法,方法名是show,方法体与showImpl方法的内容相同
4. 修改新方法的方法体,包含三部分:方法开始添加start变量标记当前时刻;执行showImpl;计算当前时间与start的差值,算出showImpl的执行耗时
5. 将新方法添加到Bean.calss文件中;同步更新Bean.class文件到磁盘

通过上面五个步骤就完成了Bean.class字节码的修改

执行JassistLearn,打印数据如下:

hey man add timing Call to method show took 0 ms.

通过JD-DUI工具查看Bean class文件,代码如下:

import java.io.PrintStream;

public class Bean
{
  public static void showImpl(String text)
  {
    System.jdField_out_of_type_JavaIoPrintStream.print("hey man " + text);
  }

  public static void main(String[] argv) {
    show("add timing ");
  }

  public static void show(String paramString)
  {
    long l = System.currentTimeMillis();
    showImpl(paramString);
    System.jdField_out_of_type_JavaIoPrintStream.println("Call to method show took " + (System.currentTimeMillis() - l) + " ms.");
  }
}

工程下载地址

三 总结

通过示例看到通过javassist api可以很简便的在运行时修改class字节码,并且不要求使用者对字节码本身非常熟悉。利用这个特性可以实现aop,安卓应用热修复等功能。不过最新github上的javassist.jar在使用过程会报关于StackWalker.StackFrame的错误,需要使用Jdk1.9。

四 参考

Javassist github

Javassist 官网

Javassist api参考

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

gradle学习二 利用javassist api修改class字节码 的相关文章

  • Failed to start bean ‘documentationPluginsBootstrapper‘; nested exception is java.lang.NullPointerEx

    看了以上午 xff0c 终于找到了问题所在0 0 swagger 与 springboot 使用的时候 xff0c 有一个版本问题 xff1a swagger版本springboot版本2 x2 7 x 一下3 x2 7 x 及以上 不多说
  • 黑客圈最受欢迎的21个 Kali Linux 工具,你都用过哪几个?

    这里是最好的 Kali Linux 工具列表 xff0c 它们可以让你评估 Web 服务器的安全性 xff0c 并帮助你执行黑客渗透测试 如果你读过 Kali Linux 点评 xff0c 你就知道为什么它被认为是最好的黑客渗透测试的 Li
  • 今天第一次加入

    64 TOC今天第一次加入进来 欢迎使用Markdown编辑器 你好 xff01 这是你第一次使用 Markdown编辑器 所展示的欢迎页 如果你想学习如何使用Markdown编辑器 可以仔细阅读这篇文章 xff0c 了解一下Markdow
  • 学习OpenStack之环境部署一篇就够啦!!!(带你走进OpenStack世界)

    文章目录 一 xff1a 项目拓扑1 1 xff1a 环境介绍 二 xff1a 系统环境配置2 1 xff1a 安装 配置MariaDB2 2 xff1a 安装RabbitMQ2 3 xff1a 安装memcached 三 xff1a Ke
  • ccf 炉石传说

    import java util ArrayList import java util Scanner span class hljs comment date 9 11 17 author chaomaer span span class
  • python之paramiko文件夹远程上传

    Paramiko模块是一个用来连接远程服务器 xff0c 进行文件上传下载和shell命令执行的PYTHON模块 xff0c 使用方便 但是它不支持整个文件夹的上传 xff0c 近期需要分发上传一个离线工具包到服务器去执行 xff0c 考虑
  • form表单提交后,页面弹出成功或者失败的信息

    Ssm 中用RedirectAttributes做提示消息 96 span class token annotation punctuation 64 RequiresPermissions span span class token pu
  • Oracle提示表空间不足ORA-01653:

    这里写自定义目录标题 欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题 xff0c 有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中 居左 居右SmartyP
  • 递归删除树节点

    public int delete Long id 删除权限的集合ids List lt Long gt ids 61 new ArrayList lt gt ids add id 递归的将一级分类下的id也加入到集合中 this getI
  • 解决报错:sufficient buffer space or because a queue was full

    An operation on a socket could not be performed because the system lacked sufficient buffer space or because a queue was
  • java判断字符串(含中文)的内容相似度

    span class token comment 判断地址相似 此处简单的判断字符串的内容80 相似即可 64 param str1 64 param str2 64 return span span class token keyword
  • display:table-cell实现两栏自适应布局

    前面介绍了 float实现两栏自适应布局 float 43 overflow实现两栏自适应布局 xff0c 现在看下table如何实现两栏自适应布局 display table cell lt table gt lt table gt 里的
  • Microsoft Visual C++ 14.0 is required.

    Microsoft Visual C 43 43 14 0 is required Get it with Microsoft Visual C 43 43 Build Tools 当我们安装绝大部分python包的时候可以通过pip in
  • ubuntu20.04离线安装rabbitvcs

    在内网开发的时候 xff0c 管理更新代码非常不方便 xff0c 使用svn是非常普遍的现象 小乌龟svn貌似只有windows版本的 xff0c 在linux系统下与其操作接近的软件rabbitcvs变成了最好的选择 在线安装rabbit
  • 实机安装CentOS7.9操作系统图文(保姆级)教程

    一 制作启动U盘 1 下载Ventoy软件 去Ventoy官网下载Ventoy软件 xff08 Download Ventoy xff09 如下图界面 2 制作启动盘 选择合适的版本以及平台下载好之后 xff0c 进行解压 xff0c 解压
  • Angular8升级至Angular13遇到的问题

    根据项目需求 xff0c 需要把Angular版本从8升级到13 xff0c 无法从8直接升至13 xff0c 需要一级一级的升级 xff0c 本文介绍了在升级Angular版本的时候的一种报错和解决办法 一 开始之前 首先确保你 Node
  • mysql怎么查询表的大小

    通过sql语句查询整个数据库的容量 xff0c 或是单独查看表所占容量 1 要查询表所占的容量 xff0c 就是把表的数据和索引加起来就可以了 1 select sum DATA LENGTH 43 sum INDEX LENGTH fro
  • CEF3 C++接口实现自定义浏览器[simple version]

    目录 目录 1 一 工具准备 2 二 编译C 43 43 接口 2 三 实现浏览器 5 浏览器定制 5 浏览器默认最大化 xff0c 标题从配置文件读取 5 设置浏览器中文环境 xff0c 开启npapi插件功能 xff0c 并注册身份证阅
  • Windows笔记本声音无法找到输出设备

    现象 xff1a 扬声器无法正常工作 xff0c 声音选项提示无法找到输出设备 xff0c 设备管理器的音频输入和输出的声卡文件旁边有黄色的感叹号 xff0c 属性详情里面设备状态提示 xff1a 该设备无法启动 xff08 代码10 xf
  • C++扑克牌发牌

    自动发扑克牌 52张牌无大小王随机发给4个玩家 poker cpp 自动发扑克牌 52张牌无大小王随机发给4个 loaction src poker cpp include lt assert h gt include lt iostrea

随机推荐

  • ArchLinux下i3wm简单配置和美化

    先show下自己配置的截图 简单弄了下 xff0c 凑合用 本文默认你已经安装了基本的archlinux系统 xff0c 只是没有配置桌面环境 所以跳过前面archlinux的基础安装过程 ArchLinux的具体安装请参见我的博客 htt
  • CentOS8下编译配置nginx+rtmp,搭建推流服务器

    一 环境 服务器操作系统 xff1a CentOS Linux release 8 2 2004 Core nginx 版本 https nginx org download nginx 1 18 0 tar gz RMTP模块 xff1a
  • CentOS7网络配置(ping不同的原因及解决方法)

    这是配置好的CentOS7 xff0c 刚开始在Vmware里装CentOS7后是没有ip的 xff0c 原因是CentOS7默认不启动网卡的 xff0c 网卡不启用还ping个毛 进入 etc sysconfig network scip
  • SpringBoot-JPA进行多表连接查询

    通过JPA进行简单的 内 连接查询 1 准备 1 1开发工具Intellij Idea 1 2数据库mysql 1 3新建Spring Initializr项目 xff0c 勾选web mysql rest jpa依赖 2 开始 2 1项目
  • 响应式导航栏-利用纯css实现

    思路 xff1a 当屏幕为移动设备时 xff0c 隐藏导航栏列表项目 xff0c 显示菜单按钮 给菜单按钮 xff08 电脑时隐藏 xff09 加入hover 或者checked选择器实现 xff0c 当hover或者checked的触发时
  • BTRFS文件系统安装ArchLinux

    layout post title BTRFS文件系统安装ArchLinux date 2017 10 02 categories Linux 主要为以下步骤 xff1a 1 下载ArchLinux安装镜像并 制作U盘启动工具 2 开机进入
  • JSP文件上传

    JSP文件上传 网上的方法几乎都是使用的org apache commons fileupload的jar包 xff0c 需要手动下载导入commons fileupload jar和commons io jar 其实tomcat自带的or
  • ORPALIS PDF Reducer Pro(免费pdf压缩器工具)官方正式版V4.0.1 | pdf免费压缩软下载 | 怎样将pdf压缩得很小?

    ORPALIS PDF Reducer Pro 是一款优秀实用的离线单机版pdf免费压缩软件 xff0c 也就是大家说的免费pdf压缩器工具 xff0c 内置多种超高压缩比的PDF压缩算法和创新的页面布局分析以及自动颜色检测机制 xff0c
  • ubuntu系统文件夹作用

    opt 文件夹 用户级的程序目录 xff0c 可以理解为D Software xff0c opt有可选的意思 xff0c 这里可以用于放置第三方大型软件 xff08 或游戏 xff09 xff0c 当你不需要时 xff0c 直接rm rf掉
  • rhel7安装GUI

    check the rank of starting system systenmctl get default not found startx 查看光盘是否挂载 df 挂载 yum mount dev sr0 mnt mount dev
  • org-mode Properties-and-Columns翻译

    https orgmode org manual Properties and Columns html Properties and Columns 文章目录 属性属性语法特殊属性属性查询属性继承 Column View 列视图定义列列定
  • python利用ffmpeg进行rtmp推流直播

    思路 xff1a opencv读取视频 gt 将视频分割为帧 gt 将每一帧进行需求加工后 gt 将此帧写入pipe管道 gt 利用ffmpeg进行推流直播 pipe管道 xff1a 啥是pipe管道 xff1f 粗略的理解就是一个放共享文
  • 电脑环境PCL配置及VS2019环境配置

    VS2019配置pcl 1 12 0 前言 对于 3D 点云处理来说 xff0c PCL 完全是一个的模块化的现代 C 43 43 模板库 其基于以下第三方库 xff1a Boost Eigen FLANN VTK CUDA OpenNI
  • java算法--兔子繁殖问题

    java算法 兔子繁殖问题 题目 xff1a 古典问题 xff1a 有一对兔子 xff0c 从出生后第 3 个月起每个月都生一对兔子 xff0c 小兔子长到第四 个月后每个月又生一对兔子 xff0c 假如兔子都不死 xff0c 问每个月的兔
  • Linux配置Wifi模块

    linux终端无线网卡连接wifi xff1a 扫描可用连接wifi nmcli dev wifi 添加一个wifi的连接 nmcli dev wifi con 无线网络名称 password 无线网络密码 name 任意连接名称 xff0
  • Paho MQTT 嵌入式c客户端研究笔记

    最近做物联网设备 xff0c 需求长连接推送功能 当前物联网有一个标准协议是MQTT xff0c 对应有很多开源服务端 xff0c 如何快速接入这个服务呢 有两种接入方案 xff1a 1 自己clone 代码修改维护 2 找第三方服务 xf
  • Paho MQTT 嵌入式c客户端研究笔记 (二)

    paho mqtt embedded c master MQTTPacket samples xff0c 这个目录里面封装了发布消息 订阅消息的示例 运行pub0sub1 xff0c 这个示例里面会去订阅主题消息 发布主题消息 并且订阅和发
  • Android App 架构设计

    简介 本文是对谷歌原生文档的翻译 xff0c 仅供学习参照 原文链接 此文档写给希望学习最优编程实践和架构以开发健壮 高质量APP的开发者 开发者常遇到的问题 传统的桌面程序大多数使用场景是有一个启动入口 xff0c 作为一个独立进程运行
  • Linux Ubuntu 18.04 换源

    在安装好Ubuntu后 xff0c 你会发现软件下载安装速度非常慢 xff0c 甚至会出错 xff0c 因为Ubuntu官方的源在国内访问的速度非常慢 xff0c 但是我们可以更改系统的源为国内的一些源来提高速度 xff08 国内各大网站的
  • gradle学习二 利用javassist api修改class字节码

    一 前言 Javassist Java Programming Assistant makes Java bytecode manipulation simple It is a class library for editing byte