我正在使用 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;
}
}
(是在构造函数中传入inst
的句柄)Instrumentation
我遇到困难的部分是评论中标有**2**
. 让我们再说一遍 A 扩展了 B 并且我对转换 A 很“感兴趣”。我期待的是我会看到超类 (B) 的名称被打印在**1**
(但没有被转换,因为我不认为第一遍很有趣)然后,一旦我到达**2**
并发现 A 的超类是 B,我应该尝试重新转换 B。此时我希望再次调用此方法(通过inst.retransformClasses()
)并且我会见 B 打印在**1**
。但是,我没有。(我已经添加了打印语句,并且确信我已经到达了 retransform 调用。我也检查了它Instrumentation.isRetransformClassesSupported()
并且Instrumentation.isModifiableClass(c)
都返回 true)。
我相信我已经正确设置了代理;在清单中将 Can-Retransform-Classes 和 Can-Redefine-Classes 设置为 true。此外,当我在代理的方法中将变压器添加到 Instrumentation 时,premain
我会这样做:
public static void premain(String agentArgs, Instrumentation inst) {
inst.addTransformer(new PyClassFileTransformer(inst), true);
}
关于我在这里做错了什么的任何见解?谢谢。