原創 【我的ASM學習進階之旅】 04 使用ASM的Core API 的Classes的api來解析類

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
}

方式一和方式二 都可以正常讀取字節碼。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章