Sun 1.6.0_15 および 1.5.0_17 JRE で A の前に B を変換しても問題は見られませんでした (私はASMを使用しました)。変換コードを外部で実行し、結果のクラスを (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