Я не видел никаких проблем, когда я преобразовал B до A на JRE Sun 1.6.0 & lt; x5f; 15 и 1.5.0 & # x5f; 17 (я использовал ASM ). Я бы дважды проверил код преобразования, запустив его извне и проверив получающиеся классы (например, с помощью javap). Я также проверил бы конфигурацию вашего classpath, чтобы убедиться, что A по какой-то причине не загружен перед вашим агентом (возможно, проверьте ваш premain с помощью getAllLoadedClasses ).
EDIT:
Если вы загружаете класс 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