Java ASM詳解:基礎(chǔ)知識
由于很久沒有投稿了,想了想還是發(fā)專欄吧!
之前一直在做虛擬化學實驗室的程序,由于感覺不太舒服,重構(gòu)了很多遍,并加入了億點點內(nèi)容,其中就有包括ASM的知識。由于網(wǎng)上的文章很碎,重復率也是很高,所以我打算自己來寫一個。
首先第一個問題:
ASM是什么,字節(jié)碼又是什么
ASM是一個Java字節(jié)碼分析、創(chuàng)建和修改的開源應用框架。它可以動態(tài)生成二進制格式的stub類或其他代理類,或者在類被Java虛擬機裝入內(nèi)存之前,動態(tài)修改類。在ASM中提供了諸多的API用于對類的內(nèi)容進行字節(jié)碼操作的方法。與傳統(tǒng)的BCEL和SERL不同,在ASM中提供了更為優(yōu)雅和靈活的操作字節(jié)碼的方式。
這是ASM官網(wǎng)上給出的沒用解釋,一句話概括:ASM是一個修改,分析Java類文件的框架。先拋開ASM框架的基本定義,先來看看字節(jié)碼是什么
字節(jié)碼(Byte-code)是一種包含執(zhí)行程序,由一序列 op 代碼/數(shù)據(jù)對組成的二進制文件
又是一段廢話。從這里可以的知,字節(jié)碼是一種介于翻譯語言和底層語言的東西,與底層語言(C/C++)相比較你可以從它這里知道程序的運行方式,而對于翻譯語言(JS)你又無法從字節(jié)碼中輕易看出什么。但是由于字節(jié)碼的這個特性,我們得以修改它,操縱它,并且我們還可以反編譯它。
前提知識說完,開始真正的了解。


官網(wǎng):https://asm.ow2.io/?FORM=UCIAST&pname=shenma
添加ASM庫依賴
首先,是有關(guān)于ASM的Maven依賴
<dependency>
? ? ?<groupId>org.ow2.asm</groupId>
? ? ?<artifactId>asm-all</artifactId>
? ? ?<version>6.0_BETA</version>
</dependency>
現(xiàn)在的ASM版本已經(jīng)來到了6.0_BETA版本,如果你用的是asm,asm-tree這樣組合也能達到相同效果。這個庫以后會詳細講述,這里先說幾個強有力的工具。
幫助學習ASM的工具
1.ASMifier:自動生成ASM代碼
ASM庫中有不少實用類,為了了解晦澀難懂的ASM代碼,可以用ASMifier來進行解析。這是一個可執(zhí)行類,你可以通過java.exe運行它。
運行方法:下載ASM的jar(比如asm-all-6.0_BETa.jar)或者從你的.m2文件夾里面找到它,然后運行:
java -classpath asm-all-6.0_BETA.jar org.objectweb.asm.util.ASMifier DemoDump.class
你會得到這樣的輸出:

2.javap.exe:Java自帶的字節(jié)碼解析工具
javap是你在安裝JDK時就有的一個程序文件,是JDK的原生字節(jié)碼解析工具,關(guān)于它的使用因篇幅有限不再細說,可以參考這篇文章?https://www.cnblogs.com/frinder6/articles/5440173.html
上方是有關(guān)于直接查看字節(jié)碼的,下方則是反編譯的工具
3.Eclipse插件:Enhanced Class Decompiler
這個插件是反編譯器,可以在你沒有庫的源代碼時反編譯出源代碼進行調(diào)試。當然,這個也可以作為你ASM程序的結(jié)果測試方案,通常反編譯結(jié)果會很貼近于源代碼,如果相差很大可以換一種方式反編譯。

下面是有關(guān)于以后經(jīng)常會用到的名詞,這些對于學習ASM都極其重要。
類(型)的不同名稱
類的二進制名稱/類全名/簡單名稱
這個三個名稱是等價的,也就是我們平常說的類名,例如 java.lang.Thread java.lang.Thread$UncaughtExceptionHandler這樣的名稱。
全限定名
這個名稱是用于class文件中的名稱,其實就是將二進制名稱的所有"."換為"/",這個名稱只有非數(shù)組引用類型才有。例如 java/lang/Thread? ?java/io/IOException。
類型描述符
類型描述符是有關(guān)于class文件內(nèi)定義字段等的類型的名稱,它遵循以下規(guī)則:
1.原始類型的描述符一一對應
原始類型的描述符都對應相應的一個字母,具體來說是這樣的:
byte -> B
short -> S
int -> I
long -> J
float -> F
double -> D
char -> C
void -> V
boolean -> Z
2.非數(shù)組的引用類型為 L+全限定名+;
3.數(shù)組引用類型為 [+數(shù)組內(nèi)類型的描述符
例子:
java.lang.Thread -> Ljava/lang/Thread;
java.lang.Object[] -> [Ljava/lang/Object;
int[][] -> [[I
方法描述符
了解類名稱和類型描述符,下面講一下方法描述符(其實字段描述符和方法描述符統(tǒng)稱描述符)
方法描述符是class文件中保存參數(shù)類型列表和返回值類型的方式,在各種方法調(diào)用的操作碼里面都會涉及到。
規(guī)則:
1.格式為 ( + 參數(shù)列表 + ) + 返回值
2.所有類型名稱都為類型描述符
3.參數(shù)列表中不需要逗號分隔
下面是抽象含義下的具體例子(省略了參數(shù)名稱,只保留了參數(shù)類型):
void a(int,int,int) -> (III)V
String s(double[],boolean) ->?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ([DZ)Ljava/lang/String;
int[] i(Object) -> (Ljava/lang/Object;)[I
void t() -> ()V
操作碼(OpCode)
Opcode是用于JVM解釋運行Java程序的關(guān)鍵。每一個Opcode都有自己獨特的含義與操作,如0x60,助記符iadd,將兩個int相加。
有一點要注意:操作碼其實就是一個數(shù)字,我們平時經(jīng)常看到的iadd,invokestatic并不是操作碼,而是助記符。
而Java中字節(jié)碼的名稱也與操作碼有關(guān),因為每個操作碼都是用一個字節(jié),所以叫字節(jié)碼。
每一個字節(jié)用來表示一個指令,理論上可以有 256 個操作碼。
對于ASM庫來說,所有的Opcode都存儲于org.objectweb.asm.Opcodes里面,其中還有包括它們在什么方法中作用的注釋。
有關(guān)于所有操作碼的網(wǎng)頁:https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html?FORM=UCIAST&pname=shenma

這就是有關(guān)于ASM的基礎(chǔ)知識,有些沒有說到的東西會在以后的專欄中說到。
如果文章中有任何錯誤,可以在評論區(qū)留言,我將會修正錯誤。
這篇文章稍后也會在CSDN上同步。
P.S.由于這篇專欄是我用手機寫的,所以沒有配圖,排版也有可能有問題,下一篇專欄還是用電腦寫。

參考文章:
字節(jié)碼-搜狗百科 https://baike.sogou.com/m/v53858822.htm
ASM 庫的介紹和使用 -? lijiankun24 - 簡書?https://www.jianshu.com/p/905be2a9a700
全限定名、簡單名稱和描述符是什么東西?https://www.cnblogs.com/wxdlut/articles/12229562.html
bytecode 和 opcode 是什么?有什么區(qū)別??https://segmentfault.com/q/1010000009643588/a-1020000009643956