在 Sun 1.6.0_15 和 1.5.0_17 JRE(我使用ASM)上,当我在 A 之前转换 B 时,我没有看到任何问题。我会通过在外部运行转换代码并检查生成的类(例如使用 javap)来仔细检查转换代码。我还会检查您的类路径配置,以确保由于某种原因没有在您的代理之前加载 A(也许使用 getAllLoadedClasses 检查您的premain)。
编辑:
如果您A
像这样在代理中加载类:
Class.forName("A");
...然后抛出异常:
Exception in thread "main" java.lang.NoSuchMethodError: B.print()V
这是有道理的 -A
成为代理的依赖项,代理检测自己的代码是没有意义的。你会得到一个导致堆栈溢出的无限循环。因此,A
不被ClassFileTransformer
.
为了完整起见,这是我的测试代码,可以正常工作。如前所述,它依赖于 ASM 库。
中介:
public class ClassModifierAgent implements ClassFileTransformer {
public byte[] transform(ClassLoader loader, String className,
Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
byte[] classfileBuffer) throws IllegalClassFormatException {
System.out.println("transform: " + className);
if ("A".equals(className)) {
return new AModifier().modify(classfileBuffer);
}
if ("B".equals(className)) {
return new BModifier().modify(classfileBuffer);
}
return classfileBuffer;
}
/** Agent "main" equivalent */
public static void premain(String agentArguments,
Instrumentation instrumentation) {
instrumentation.addTransformer(new ClassModifierAgent());
}
}
方法注入器A
:
public class AModifier extends Modifier {
@Override
protected ClassVisitor createVisitor(ClassVisitor cv) {
return new AVisitor(cv);
}
private static class AVisitor extends ClassAdapter {
public AVisitor(ClassVisitor cv) { super(cv); }
@Override
public void visitEnd() {
MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC, "print", "()V",
null, null);
mv.visitCode();
mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out",
"Ljava/io/PrintStream;");
mv.visitLdcInsn("X");
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream",
"println", "(Ljava/lang/String;)V");
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(2, 1);
mv.visitEnd();
super.visitEnd();
}
}
}
方法替换B
:
public class BModifier extends Modifier {
@Override
protected ClassVisitor createVisitor(ClassVisitor cv) {
return new BVisitor(cv);
}
class BVisitor extends ClassAdapter {
public BVisitor(ClassVisitor cv) { super(cv); }
@Override
public MethodVisitor visitMethod(int access, String name, String desc,
String signature, String[] exceptions) {
if ("foo".equals(name)) {
MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC, "foo", "()V",
null, null);
mv.visitCode();
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "B", "print", "()V");
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
return new EmptyVisitor();
} else {
return super.visitMethod(access, name, desc, signature, exceptions);
}
}
}
}
通用基础代码:
public abstract class Modifier {
protected abstract ClassVisitor createVisitor(ClassVisitor cv);
public byte[] modify(byte[] data) {
ClassReader reader = new ClassReader(data);
ClassWriter writer = new ClassWriter(reader, ClassWriter.COMPUTE_FRAMES);
ClassVisitor visitor = writer;
visitor = new CheckClassAdapter(visitor);
visitor = createVisitor(visitor);
reader.accept(visitor, 0);
return writer.toByteArray();
}
}
对于一些可见的结果,我添加了一个System.out.println('X');
到A.print()
.
在此代码上运行时:
public class MainInstrumented {
public static void main(String[] args) {
new B().foo();
}
}
...它产生这个输出:
transform: MainInstrumented
transform: B
transform: A
X