You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Javassist将方法字节码复制修改后写入新方法

使用Javassist提取旧方法字节码并修改生成新方法

我来帮你搞定这个Javassist的字节码修改需求,其实步骤很清晰,咱们一步步来实现:

核心思路

先加载目标类,提取Method_old的字节码结构,移除不需要的指令(也就是bipush 20和istore_2这两步),然后把修改后的字节码封装成一个新的method_new方法,最后将修改后的类写入文件。

具体实现代码

首先确保你的项目已经引入了Javassist依赖(比如Maven添加javassist:javassist:3.29.2-GA),然后用以下代码完成操作:

import javassist.*;
import javassist.bytecode.*;

public class BytecodeModifier {
    public static void main(String[] args) throws Exception {
        // 1. 初始化类池,加载目标类(替换成你的类全限定名)
        ClassPool classPool = ClassPool.getDefault();
        CtClass targetClass = classPool.get("com.yourpackage.YourClass");

        // 2. 获取原方法Method_old
        CtMethod oldMethod = targetClass.getDeclaredMethod("Method_old");

        // 3. 复制原方法生成新方法,命名为method_new
        CtMethod newMethod = CtNewMethod.copy(oldMethod, "method_new", targetClass, null);

        // 4. 处理字节码:移除bipush 20和istore_2指令
        CodeAttribute codeAttr = newMethod.getMethodInfo().getCodeAttribute();
        CodeIterator iterator = codeAttr.iterator();
        Bytecode modifiedBytecode = new Bytecode(codeAttr.getConstPool(), codeAttr.getMaxStack(), codeAttr.getMaxLocals());

        while (iterator.hasNext()) {
            int pos = iterator.next();
            int opcode = iterator.byteAt(pos);

            // 跳过bipush 20这个指令,同时跳过后续的istore_2
            if (opcode == Opcode.BIPUSH && iterator.byteAt(pos + 1) == 20) {
                iterator.next();
                continue;
            }

            // 将其他指令添加到新的字节码对象中
            modifiedBytecode.addOpcode(opcode);
            // 处理带操作数的指令
            if (Opcode.length(opcode) > 1) {
                if (Opcode.isWide(opcode)) {
                    modifiedBytecode.addIndex(iterator.u16bitAt(pos + 1));
                } else {
                    modifiedBytecode.add(iterator.byteAt(pos + 1));
                }
            }
        }

        // 5. 更新新方法的字节码,并重新计算栈大小和局部变量数
        codeAttr.setCode(modifiedBytecode.get());
        codeAttr.computeMaxStack();
        codeAttr.computeMaxLocals();

        // 6. 将新方法添加到类中,然后写入文件
        targetClass.addMethod(newMethod);
        targetClass.writeFile("your/output/directory"); // 替换成你的输出目录

        // 释放类池资源
        targetClass.detach();
        System.out.println("新方法method_new已成功生成!");
    }
}

代码说明

  • 类池初始化:ClassPool.getDefault()获取默认类池,如果你的类不在默认类路径下,需要用classPool.insertClassPath()添加路径。
  • 方法复制:用CtNewMethod.copy()快速复制原方法的签名和结构,避免手动创建方法的麻烦。
  • 字节码遍历修改:通过CodeIterator遍历原方法的每一条指令,当遇到bipush 20时,跳过当前指令和后续的istore_2,其他指令正常保留。
  • 字节码更新:修改后的字节码设置回新方法后,必须调用computeMaxStack()和computeMaxLocals()重新计算栈和局部变量的最大值,否则会导致字节码验证失败。

另一种更直接的方式(适合已知最终字节码的情况)

如果你已经明确知道修改后的字节码指令,也可以直接手动构造字节码数组并设置:

// 替换步骤4和5的代码
byte[] finalCode = {
    (byte)Opcode.BIPUSH, 10,          // 0: bipush 10
    (byte)Opcode.ISTORE_1,            // 2: istore_1
    (byte)Opcode.GETSTATIC, (byte)(2 >> 8), (byte)(2 & 0xFF), // 3: getstatic #2
    (byte)Opcode.ILOAD_1,             // 6: iload_1
    (byte)Opcode.INVOKEVIRTUAL, (byte)(11 >> 8), (byte)(11 & 0xFF), //7: invokevirtual #11
    (byte)Opcode.RETURN               //8: return
};
codeAttr.setCode(finalCode);
codeAttr.computeMaxStack();
codeAttr.computeMaxLocals();

这种方式要注意:常量池的索引(比如#2和#11)必须和原类中的一致,因为这些索引对应原类常量池里的System.out和println(I)V方法引用。

注意事项

  • 不要修改JDK核心类(比如java.lang.String),这类类由Bootstrap类加载器加载,修改后无法正常加载。
  • 操作完成后记得调用detach()释放类池中的CtClass对象,避免内存泄漏。
  • 实际项目中建议捕获具体的异常(比如NotFoundException、CannotCompileException),而不是直接抛出Exception。

内容的提问来源于stack exchange,提问作者adeel iqbal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:33:05