javaagents:转换顺序

时间:2012-08-23 12:10:55

标签: java java-bytecode-asm javaagents

在我的项目中,我想分两个阶段转换字节码。订单很重要。

  1. 首先,我需要更改方法定义
  2. 然后是方法的方式
  3. 例如

    1. 将定义从String hello()更改为String hello(String s)
    2. 将来电从Hello.hello()更改为Hello.hello("newArgument")
    3. 我设法添加变压器,这是第一步。

      public class MyJavaAgent {
      
          private static Instrumentation instrumentation;
      
          public static void premain(String args, Instrumentation inst)
                  throws Exception {
              instrumentation = inst;
              instrumentation.addTransformer(new MyClassFileTransformer());
          }
      }
      

      我的问题是:我可以这样添加新的变压器:

      public class MyJavaAgent {
      
          private static Instrumentation instrumentation;
      
          public static void premain(String args, Instrumentation inst)
                  throws Exception {
              instrumentation = inst;
              instrumentation.addTransformer(new MyClassFileTransformer());
              instrumentation.addTransformer(new MyClassFileTransformer2());
          }
      }
      

      并确保MyClassFileTransformerMyClassFileTransformer2之前完成工作?

1 个答案:

答案 0 :(得分:3)

有一种简单的方法可以测试它...在每个Transformer类的transform方法中添加一个System.out指令,该指令将输出各个类的唯一消息。然后查看在控制台上获取输出的顺序。如果在Transformer2之前获得Transformer1的唯一消息,那么是的,按顺序调用转换方法。

这就是我做的......

package Test;

import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;

public class TransFormerTest  {


    public TransFormerTest() {
        super();
    }

    public static void premain(String agentArguments, Instrumentation instrumentation) {
        instrumentation.addTransformer(new Transformer1());
        instrumentation.addTransformer(new Transformer2());
    }
}

class Transformer1 implements ClassFileTransformer {
    public byte[] transform(ClassLoader loader, String className, Class redefiningClass, ProtectionDomain domain, byte[] bytes) throws IllegalClassFormatException {
        System.out.println(className + "one"); // one for Transformer
        return bytes;
    }
}

class Transformer2 implements ClassFileTransformer {
    public byte[] transform(ClassLoader loader, String className, Class redefiningClass, ProtectionDomain domain, byte[] bytes) throws IllegalClassFormatException {
        System.out.println(className + "two"); // two for Transformer 2
        return bytes;
    }
}

因此,每次加载一个类并调用变换器时,您应该看到要打印两次的类的名称,首先使用一个作为后缀,然后两个作为后缀。

我用简单的Hello World程序测试了这个理论......这是我的输出::

Test/Transformer2 - one
sun/launcher/LauncherHelper - one
sun/launcher/LauncherHelper - two
java/lang/Enum - one
java/lang/Enum - two
HelloWorld - one
HelloWorld - two
java/lang/Void - one
java/lang/Void - two
Hello World
java/lang/Shutdown - one
java/lang/Shutdown - two
java/lang/Shutdown$Lock - one
java/lang/Shutdown$Lock - two

所以,它似乎确实维持了秩序。

话虽如此,您是否考虑过链接变换方法?例如......

public class TransFormerTest  {

    public TransFormerTest() {
        super();
    }

    public static void premain(String agentArguments, Instrumentation instrumentation) {
        instrumentation.addTransformer(new Transformer());
    }
}

class Transformer implements ClassFileTransformer {
    public byte[] transform(ClassLoader loader, String className, Class redefiningClass, ProtectionDomain domain, byte[] bytes) throws IllegalClassFormatException {
        byte[] bytes2 =  privateTransformer(className, bytes);
        return bytes2;
    }

    private byte[] privateTransformer(String className, byte[] bytes) {
        System.out.println(className + " - one");
            // TODO add code for First Transformation.
        byte[] bytes2 = privateTransformer2(className, bytes);
        return bytes2;
    }

    private byte[] privateTransformer2(String className, byte[] bytes) {
        System.out.println(className + " - two");
            // TODO add code for Second Transformation.
        return bytes;
    }
}

这仍然会取得类似的结果,这肯定会保持转变可能发生的顺序。