1

我只是在试验 Java Instrumentation,因为它非常有趣,我想了解更多。我将它与 javassist 库结合使用,以使字节码操作更容易,以及 JDK 安装中包含的“工具”库。

这是我的主要课程:

public class MainClass {
public static boolean first = true;
static{
    AgentClass.initialize();
}
public static void loadAgent(){
    String path = System.getProperty("user.dir") + "\\AgentJar.jar";
    String nameOfRunningVM = ManagementFactory.getRuntimeMXBean().getName();
    int p = nameOfRunningVM.indexOf('@');
    String pid = nameOfRunningVM.substring(0, p);

    try {
        VirtualMachine vm = VirtualMachine.attach(pid);
        vm.loadAgent(path, "");
        vm.detach();
    } catch (Exception e) {
        e.printStackTrace();
        throw new RuntimeException(e);
    }
}
public static void main(String[] args){
   System.out.println("First run-through, code should be modified once.");
    new Hello().hello();
            first = false;
    try {
        AgentClass.getInstrumentation().retransformClasses(Class.forName("test.Hello"));
    } catch (Exception e){
            e.printStackTrace();
    }
    System.out.println("Second run-through, code should be modified twice.");
    new Hello().hello();
}
}

这是“你好”类:

public class Hello {
    public void hello(){
            System.out.println("Hello World!");
    }
}

这是 FileTransformer 类:

public class FileTransformer implements ClassFileTransformer{
private static boolean first = true;
@Override
public byte[] transform(ClassLoader loader, String className,
        Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
        byte[] classfileBuffer) throws IllegalClassFormatException {
    if (!className.contains("Hello"))
        return null;
    else{
        byte[] result;
        CtClass cc = null;
        try {
            cc = ClassPool.getDefault().get("test.Hello");
            CtMethod method = cc.getDeclaredMethod("hello");
            if (MainClass.first){
            System.out.println("In transformer: first");
            method.insertAfter("System.out.println(\"Modified First Time!\");");
            }else{
                System.out.println("In transformer: second");
                method.insertAfter("System.out.println(\"I modified it again.!\");");
            }
            cc.writeFile();
            result = cc.toBytecode();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return result;
    }

}

}

代理类在另一个 jar 中,它是它的基本实现:

public class AgentClass {
protected static Instrumentation inst;
private static boolean added = false;
public static void agentmain(String args, Instrumentation inst){
    AgentClass.inst = inst;
    if (!added)
        inst.addTransformer(new FileTransformer());
}
public static void premain(String args, Instrumentation inst){
    AgentClass.inst = inst;
    inst.addTransformer(new FileTransformer());
    added = true;
}
public static void initialize(){
    if (inst == null){
        MainClass.loadAgent();
    }
}
public static Instrumentation getInstrumentation(){
    return inst;
}
}

当我运行时,我没有遇到任何错误。但是,输出不是我期望的那样。

这是我得到的输出:

First run-through, code should be modified once.
In transformer: first
Hello World!
Modified First Time!
Second run-through, code should be modified twice.
Hello World!
Modified First Time!

您可能会注意到没有一行写着“我再次修改了它!”

任何帮助表示赞赏。

4

1 回答 1

0

不确定这里是否还有其他问题,但是如果要重新转换类,则需要使用允许您指定转换器可以重新转换的方法注册 ClassFileTransformer 。IE

如果你打电话给仪器。addTransformer(ClassFileTraIsformer),那么您是在声明转换器支持重新转换。

您需要调用仪器。addTransformer(ClassFileTraIsformer, true)这将使您的变压器启动。

于 2013-08-08T11:50:36.440 回答