java - 如何在Java中重新定义已经定义的类

标签 java java-bytecode-asm

我希望在下面的代码中调用 newTarget.a () 和 newTarget.b () 时应用调制后的 ASM 类,使其看起来像这样

应用修改后的 ASM 类调用 newTarget.a () 和 newTarget.b () 时如何得到以下结果?

代码:

package asm;

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 static org.objectweb.asm.Opcodes.ASM5;

public class Main {
    public static void main(String[] args) throws Exception {
        Target target = new Target();
        target.a();
        target.b();

        ClassReader reader = new ClassReader("asm.Main$Target");
        ClassWriter writer = new ClassWriter(0);
        ClassVisitor visitor = new TestClassVisitor(ASM5, writer);

        reader.accept(visitor, 0);

        byte[] transformed = writer.toByteArray();

        // Apply byte[] transformed

        Target newTarget = new Target();
        newTarget.a();
        newTarget.b();
    }

    static class Target {
        private void a() {
            System.out.println("first method");
        }

        private void b() {
            System.out.println("second method");
        }
    }

    static class TestClassVisitor extends ClassVisitor {
        public TestClassVisitor(int i, ClassVisitor classVisitor) {
            super(i, classVisitor);
        }

        @Override
        public MethodVisitor visitMethod(int i, String s, String s1, String s2, String[] strings) {
            MethodVisitor visitor = super.visitMethod(i, s, s1, s2, strings);
            if (!s.equals("<init>")) {
                return visitor;
            }
            return new TestMethodVisitor(api, visitor);
        }
    }

    static class TestMethodVisitor extends MethodVisitor {
        public TestMethodVisitor(int i, MethodVisitor methodVisitor) {
            super(i, methodVisitor);
        }

        @Override
        public void visitCode() {
            super.visitCode();
            super.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
            super.visitLdcInsn("transformed method");
            super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
        }

        @Override
        public void visitEnd() {
            super.visitEnd();
        }
    }
}

想要的结果:

第一种方法

第二种方法

转换后的方法

第一种方法

转换后的方法

第二种方法

最佳答案

您正在寻找Java Instrumentation API 。它要求您通过 -javaagent 参数附加 Java 代理。使用API​​,您可以调用:

instrumentation.redefineClasses(
  new ClassDefinition(asm.Main.Target.class, classWriter.toBytes())
);

确保不要更改类的布局,大多数 JVM 目前不支持此功能。

关于java - 如何在Java中重新定义已经定义的类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41995162/

相关文章:

java - 如何从 strings.xml 获取值到 arraylist

java - 使用 Java ASM 库生成 'Hello, World!' 类

java - 需要帮助来理解 Java 字节码指令

java - Asm 字节码查询

java - 有什么方法可以从字节码重新生成堆栈图?

Java如何调用另一个类?

java - 当 RequestDispatcher 包含脚本时无法重定向

java - COMPUTE_MAXS 正在删除 StackMapTable

java - 访问现有 SQS 队列时抛出 AWS.SimpleQueueService.NonExistentQueue 异常

java - 我如何知道文件类型是否为 PDF?