Parsing classes
解析現有類的唯一必需組件是ClassReader組件。 讓我們以一個例子來說明這一點。 假設我們要以類似於javap工具的方式打印類的內容。 第一步是編寫ClassVisitor類的子類,以打印有關其訪問的類的信息。 這是一個可能的,過於簡化的實現:
import org.objectweb.asm.*;
import static org.objectweb.asm.Opcodes.ASM4;
public class ClassPrinter extends ClassVisitor {
public ClassPrinter() {
super(ASM4);
}
@Override
public void visit(int version, int access, String name,
String signature, String superName, String[] interfaces) {
System.out.println(name + " extends " + superName + " {");
}
public void visitSource(String source, String debug) {
}
public void visitOuterClass(String owner, String name, String desc) {
}
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
return null;
}
public void visitAttribute(Attribute attr) {
}
public void visitInnerClass(String name, String outerName,
String innerName, int access) {
}
public FieldVisitor visitField(int access, String name,
String desc, String signature, Object value) {
return null;
}
@Override
public MethodVisitor visitMethod(int access, String name,
String desc, String signature, String[] exceptions) {
System.out.println(" " + name +" " + desc);
return null;
}
public void visitEnd() {
System.out.println("}");
}
}
第二步是將此ClassPrinter與ClassReader組件結合在一起,以便ClassReader消耗由ClassReader產生的事件:
import org.objectweb.asm.ClassReader;
import java.io.IOException;
public class Test {
public static void main(String[] args) throws IOException {
ClassPrinter cp = new ClassPrinter();
ClassReader cr = new ClassReader("java.lang.Runnable");
cr.accept(cp,0);
}
}
上面的代碼 main() 方法中:
在第二行創建一個ClassReader來解析Runnable類。 java.lang.Runnable 源碼如下:
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
在最後一行調用的accept方法解析Runnable類字節碼,並在cp上調用相應的ClassVisitor方法。
結果是以下輸出:
java/lang/Runnable extends java/lang/Object {
run ()V
}
請注意,有幾種方法可以構造ClassReader實例。 可以通過名稱(如上所述)或通過值(字節數組或InputStream)指定必須讀取的類。 可以使用ClassLoader的getResourceAsStream方法通過以下方式獲取用於讀取類內容的輸入流:
import org.objectweb.asm.ClassReader;
import java.io.IOException;
import java.io.InputStream;
public class Test {
public static void main(String[] args) throws IOException {
ClassPrinter cp = new ClassPrinter();
// 方式一 構建ClassReader
ClassReader cr = new ClassReader("java.lang.Runnable");
cr.accept(cp,0);
System.out.println("====================================================");
InputStream ip = Test.class.getClassLoader().getResourceAsStream("java.lang.Runnable".replace(".", "/") + ".class");
// 方式二 構建ClassReader
ClassReader cr2 = new ClassReader(ip);
cr2.accept(cp,0);
}
}
如上面的方式二,我們通過使用ClassLoader的getResourceAsStream方法通過以下方式獲取用於讀取類內容的輸入流,然後傳給ClassReader。
運行結果爲:
java/lang/Runnable extends java/lang/Object {
run ()V
}
====================================================
java/lang/Runnable extends java/lang/Object {
run ()V
}
方式一和方式二 都可以正常讀取字節碼。