Java 8+ASM 5.0.3修改JVM已加载类字节码的实现疑问
运行时修改已加载类的字节码实现方案
针对你的需求(JVM加载类后修改字节码、Java 8+ASM 5.0.3、无JVM启动参数),可以通过Java Attach API + Instrumentation类重定义实现,核心思路是动态挂载Agent,再用ASM修改目标方法的字节码。
实现步骤与代码
1. 编写ASM字节码修改逻辑(Agent类)
这个类负责接收Instrumentation实例,读取目标类字节码并修改方法体:
import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import java.lang.instrument.ClassDefinition; import java.lang.instrument.Instrumentation; public class BytecodeInjectAgent { public static void agentmain(String agentArgs, Instrumentation inst) throws Exception { // 获取目标类 Class<?> targetClass = ExampleObject.class; // 读取原有字节码 ClassReader cr = new ClassReader(targetClass.getName()); ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_FRAMES); // 自定义ClassVisitor拦截目标方法 ClassVisitor cv = new ClassVisitor(Opcodes.ASM5, cw) { @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); // 仅处理exampleMethod方法 if ("exampleMethod".equals(name) && "(Ljava/lang/Object;)V".equals(descriptor)) { return new MethodVisitor(Opcodes.ASM5, mv) { @Override public void visitCode() { // 在方法开头插入调用methodToInject的指令 // 1. 加载方法参数o到操作数栈 mv.visitVarInsn(Opcodes.ALOAD, 1); // 2. 调用Example类的静态方法methodToInject mv.visitMethodInsn(Opcodes.INVOKESTATIC, "Example", "methodToInject", "(Ljava/lang/Object;)V", false); // 3. 执行原有方法逻辑 super.visitCode(); } }; } return mv; } }; cr.accept(cv, ClassReader.SKIP_FRAMES); // 生成修改后的字节码并触发类重定义 byte[] modifiedBytes = cw.toByteArray(); inst.redefineClasses(new ClassDefinition(targetClass, modifiedBytes)); } }
2. 修改主类的injectAtHead方法
通过Attach API动态挂载Agent,无需启动参数:
import com.sun.tools.attach.AttachNotSupportedException; import com.sun.tools.attach.VirtualMachine; import java.lang.management.ManagementFactory; import java.io.IOException; // 位于某库中 class ExampleObject { public void exampleMethod(Object o) { System.out.println("Body " + o); } } public class Example { // 来自同一库 public static final ExampleObject exampleObject = new ExampleObject(); public static void main(String[] args) { exampleObject.exampleMethod(1); injectAtHead(); exampleObject.exampleMethod(2); } public static void injectAtHead() { // 获取当前进程PID String pid = ManagementFactory.getRuntimeMXBean().getName().split("@")[0]; try { // 附加到当前进程并加载Agent VirtualMachine vm = VirtualMachine.attach(pid); // 替换为你的Agent Jar实际路径 vm.loadAgent("bytecode-inject-agent.jar"); vm.detach(); } catch (AttachNotSupportedException | IOException | Exception e) { e.printStackTrace(); } } public static void methodToInject(Object o) { System.out.println("Head " + o); } }
3. 打包Agent Jar(关键配置)
创建MANIFEST.MF文件,指定Agent入口和权限:
Manifest-Version: 1.0 Agent-Class: BytecodeInjectAgent Can-Redefine-Classes: true Can-Retransform-Classes: true Class-Path: asm-5.0.3.jar
打包时将ASM 5.0.3的Jar、所有编译后的class文件一起打包,确保MANIFEST.MF被正确包含。
注意事项
- 依赖:必须引入ASM 5.0.3 Jar,以及JDK的
tools.jar(Java 8中Attach API位于此Jar,需添加到类路径)。 - 权限:运行程序时需要足够的系统权限(比如Linux下需允许进程附加,Windows下可能需要管理员权限)。
- JVM限制:类重定义不能修改类的继承结构、不能新增/删除字段或方法(仅修改方法体是合法的)。
内容的提问来源于stack exchange,提问作者crazycat256
相关产品推荐
相关产品推荐

