我正在使用asm库执行一些Java字节码修改-专门用于修改我的类以实现新的接口和关联的方法。我当前的方法是通过javaagent使用核心asm API。我想保留这种动态方法,而不是静态修改.class文件。
在更高的层次上,我的问题是,如果我选择修改从B扩展的类A,那么我还需要修改B。(鉴于我对JVM中类的加载方式的理解,我相信类B永远都是(如果我错了,请更正我。)基于这个假设,我认为我需要返回并重新转换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。另外,当我在代理的
premain
方法中将转换器添加到Instrumentation时,我会这样做:public static void premain(String agentArgs, Instrumentation inst) {
inst.addTransformer(new PyClassFileTransformer(inst), true);
}
关于我在这里做错的任何见解吗?谢谢。
最佳答案
您可以更改字节码检测策略,因此,在加载类B时,将找到其所有子类,然后决定是否需要立即修改类B。可以通过维护类元数据存储库或在内存中缓存(即有关类层次结构的信息)来优化此操作,因此您不必每次都加载元数据。