經(jīng)過 javac 編譯后,得到的類文件大致是:
圖 3. ASM – Java 類文件
從上圖中可以看到,一個(gè) Java 類文件大致可以歸為 10 個(gè)項(xiàng):
Magic:該項(xiàng)存放了一個(gè) Java 類文件的魔數(shù)(magic number)和版本信息。一個(gè) Java 類文件的前 4 個(gè)字節(jié)被稱為它的魔數(shù)。每個(gè)正確的 Java 類文件都是以 0xCAFEBABE 開頭的,這樣保證了 Java 虛擬機(jī)能很輕松的分辨出 Java 文件和非 Java 文件。
Version:該項(xiàng)存放了 Java 類文件的版本信息,它對(duì)于一個(gè) Java 文件具有重要的意義。因?yàn)?Java 技術(shù)一直在發(fā)展,所以類文件的格式也處在不斷變化之中。類文件的版本信息讓虛擬機(jī)知道如何去讀取并處理該類文件。
Constant Pool:該項(xiàng)存放了類中各種文字字符串、類名、方法名和接口名稱、final 變量以及對(duì)外部類的引用信息等常量。虛擬機(jī)必須為每一個(gè)被裝載的類維護(hù)一個(gè)常量池,常量池中存儲(chǔ)了相應(yīng)類型所用到的所有類型、字段和方法的符號(hào)引用,因此它在 Java 的動(dòng)態(tài)鏈接中起到了核心的作用。常量池的大小平均占到了整個(gè)類大小的 60% 左右。
Access_flag:該項(xiàng)指明了該文件中定義的是類還是接口(一個(gè) class 文件中只能有一個(gè)類或接口),同時(shí)還指名了類或接口的訪問標(biāo)志,如 public,private, abstract 等信息。
This Class:指向表示該類全限定名稱的字符串常量的指針。
Super Class:指向表示父類全限定名稱的字符串常量的指針。
Interfaces:一個(gè)指針數(shù)組,存放了該類或父類實(shí)現(xiàn)的所有接口名稱的字符串常量的指針。以上三項(xiàng)所指向的常量,特別是前兩項(xiàng),在我們用 ASM 從已有類派生新類時(shí)一般需要修改:將類名稱改為子類名稱;將父類改為派生前的類名稱;如果有必要,增加新的實(shí)現(xiàn)接口。
Fields:該項(xiàng)對(duì)類或接口中聲明的字段進(jìn)行了細(xì)致的描述。需要注意的是,fields 列表中僅列出了本類或接口中的字段,并不包括從超類和父接口繼承而來的字段。
Methods:該項(xiàng)對(duì)類或接口中聲明的方法進(jìn)行了細(xì)致的描述。例如方法的名稱、參數(shù)和返回值類型等。需要注意的是,methods 列表里僅存放了本類或本接口中的方法,并不包括從超類和父接口繼承而來的方法。使用 ASM 進(jìn)行 AOP 編程,通常是通過調(diào)整 Method 中的指令來實(shí)現(xiàn)的。
Class attributes:該項(xiàng)存放了在該文件中類或接口所定義的屬性的基本信息。
事實(shí)上,使用 ASM 動(dòng)態(tài)生成類,不需要像早年的 class hacker 一樣,熟知 class 文件的每一段,以及它們的功能、長度、偏移量以及編碼方式。ASM 會(huì)給我們照顧好這一切的,我們只要告訴 ASM 要改動(dòng)什么可以了 —— 當(dāng)然,我們首先得知道要改什么:對(duì)類文件格式了解的越多,我們能更好地使用 ASM 這個(gè)利器。