使用java代理和操纵java字节码(ASM)



我正试图弄清楚ASM是如何工作的,在ASM的Java字节码操作指南的帮助下。我按照教程,为Integer类创建了一个"额外的"静态字段。下面是Instrumentation类的简化逻辑:

public static void premain(String agentArgs, Instrumentation inst) {
inst.addTransformer(new ClassFileTransformer() {
@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, final byte[] buffer) {
if (className.equals("ClassX")) {
var reader = new ClassReader(bytes);
var writer = new ClassWriter(reader, 0);
var visitor = new ClassVisitor() { // simplified
@Override
public void visitEnd() {
if (!isFieldPresent) {
var fv = cv.visitField(ACC_PUBLIC + ACC_STATIC, "aNewBooleanField", BOOLEAN_TYPE.toString(), null, true);
if (fv != null) fv.visitEnd();
}
cv.visitEnd();
}
};
reader.accept(visitor, 0);
return writer.toByteArray();
}
return buffer;
}
});
}

我用maven打包了ASM代码;现在我试着运行它,但我不知道如何使用它。我希望我能够这样做:

public static void main(String[] args) {
System.out.println(ClassX.aNewBooleanField);
}

这当然不会编译,因为编译器不知道额外的字段。我尝试按照教程的建议添加java代理¹,但我也不理解。编译器如何知道在运行时操作的进程(代理)?

所以底线问题是,我如何使用ASM操纵代码?


¹-javaagent:"/<path>/.m2/repository/org/example/try-asm/1.0-SNAPSHOT/try-asm-1.0-SNAPSHOT.jar"

如问题所述,编译器确实不知道对类的字节码所做的修改。因此,不能直接调用ClassX.aNewBooleanField。因此,您需要返回到反射:

var modifiedClass = Class.forName("ClassX");
var field = modifiedClass.getField("aNewBooleanField");
System.out.println(field.getBoolean(null));

你现在可以编译你的代码了,你可以用java代理运行它,上面的代码将打印出true值!

最新更新