如何在asm代码中正确使用Instrumentation.retransformClasses()?

时间:2012-03-01 22:41:15

标签: java bytecode java-bytecode-asm

我正在使用asm库来执行一些Java字节码修改 - 专门用于修改我的类以实现新的接口和相关方法。我目前的方法是通过javaagent使用核心asm API。我想保留这种动态方法,而不是静态修改.class文件。

在更高级别,我的问题是,如果我选择修改从B扩展的A类,我还需要修改B.(鉴于我对如何在JVM中加载类的理解,我相信B类总是会在A级之前交给变压器。(如果我错了,请纠正我。)鉴于这个假设,我认为我需要回去并转换 B.我的方法在这段代码中捕获:

public byte[] transform(ClassLoader l, String name, Class<?> clazz, ProtectionDomain d, byte[] b) {
      throws IllegalClassFormatException {
    // **1**
    System.out.println("--->>> " + name);

    if (interestingClass(name)) {
        try {
            ClassReader cr = new ClassReader(b);
            ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
            PyClassVisitorAdapter pv = new PyClassVisitorAdapter(cw, name);
            cr.accept(pv, 0);

            // **2** Retrieve the superclass and try to transform that
            if (! "Ljava/lang/Object;".equals(pv.getSuperName())) {
                String cName = classJvmToCanonical(pv.getSuperName());
                Class[] classes = inst.getAllLoadedClasses();
                for (Class c : classes) {
                    if (c.getName().equals(cName)) {
                        inst.retransformClasses(c);
                        break;
                    }
                }
            }

            // Dump the transformed class
            ClassReader cr2 = new ClassReader(cw.toByteArray());
            ClassWriter cw2 = new ClassWriter(cr2, 0);
            TraceClassVisitor tcv = new TraceClassVisitor(cw2, new PrintWriter(System.out));
            cr2.accept(tcv, 0);

            return cw2.toByteArray();
        } catch (Exception ex) {
            ex.printStackTrace();
            return null;
        }
    } else {
        return b;
    }
}

instInstrumentation的句柄,它在构造函数中传入)

我遇到困难的部分是**2**评论中标记的块。让我们再说一次A扩展B并且我'感兴趣'转换A.我期待的是我会看到超级类(B)的名称在**1**打印(但是没有被转换,因为我不认为它在第一次传递时很有意思)然后,一旦我到达**2**并发现A的超类是B,我应该尝试重新转换B.此时我期待这种方法再次调用(通过inst.retransformClasses()),我会看到B在**1**打印。但是,我没有。 (我已经添加了print语句,并确定我正在接收转换调用。我还检查过Instrumentation.isRetransformClassesSupported()Instrumentation.isModifiableClass(c)都返回true。

我相信我已经正确设置了代理商;在清单中将Can-Retransform-Classes和Can-Redefine-Classes设置为true。此外,当我在代理的premain方法中将变换器添加到Instrumentation时,我这样做:

public static void premain(String agentArgs, Instrumentation inst) {
    inst.addTransformer(new PyClassFileTransformer(inst), true);
}

关于我在这里做错了什么见解?感谢。

1 个答案:

答案 0 :(得分:1)

您可以更改字节码检测策略,因此在加载B类时,您会找到其所有子类,并在此时决定是否需要立即修改B类。这可以通过在内存中维护类元数据存储库或缓存(即有关类层次结构的信息)来优化,因此您不必每次都加载元数据。