Java ASM詳解:模塊
在前兩篇專欄中我們討論了各種類的結(jié)構(gòu),將各種類組織一起它們就組成了模塊(Module)。
模塊的定義
模塊是在 Java 9 中引入的,它在module-info.java文件中定義。這個(gè)文件的內(nèi)容是一個(gè)模塊的描述,下面就是一個(gè)例子:
上面的代碼聲明了一個(gè)模塊,名稱為 JavaASMTest,并依賴模塊 org.objectweb.asm。
在模塊中,我們可以定義以下信息:
requires 語句,它代表了這個(gè)模塊需要依賴于什么其它的模塊。所有的模塊都隱式定義了requires java.base。
requires 語句可以使用 static 修飾符,代表靜態(tài)依賴。這種依賴在編譯期是必須的,但是在運(yùn)行時(shí)可以不存在。
requires 語句也可以使用 transitive 修飾符,它代表了依賴此模塊的模塊將隱式獲得對(duì)本模塊依賴的模塊的可讀性。舉一個(gè)例子:如果 A 依賴于 B,B 依賴于 C,那么 C 對(duì) A 來說不可讀,除非 A 顯式定義 requires C;但是加上這個(gè)關(guān)鍵詞,C 將對(duì) A 可讀。exports 語句,它代表了模塊內(nèi)某個(gè)包內(nèi)的 public 成員可以被其他模塊訪問,也就是開放了可讀性。如果一個(gè)模塊代碼中直接訪問了一個(gè)其他模塊沒有 exports 的成員,那么代碼不通過編譯。 exports 語句可以和 to 搭配,代表對(duì)指定模塊開放這些成員。
opens 語句,和 exports 類似,但是它開放的是訪問性。也就是說,允許通過反射訪問模塊中的類。這個(gè)語句也可以和 to 搭配。 當(dāng)模塊被定義為 open module 時(shí),模塊內(nèi)的所有成員都將開放訪問,并且不允許在模塊中重新定義 opens。
uses 語句,用于使用服務(wù)。服務(wù)通常是抽象類,比如說 java.sql.Driver。服務(wù)類所在的模塊必須使用 requires 進(jìn)行依賴。
provides ... with ... 語句,它代表提供服務(wù)的實(shí)現(xiàn)類。
下面是 opens 和 exports 搭配使用的情況:

模塊的字節(jié)碼表示
和普通的類一樣,模塊也是使用 ClassWriter 寫入的,但是寫入類信息的時(shí)候和普通的類不一樣:它要求類名稱是 module-info,僅帶有 ACC_MODULE 訪問標(biāo)志,并且沒有超類和簽名。
接下來就是寫入模塊信息,這里用到了 visitModule 方法。如果模塊是 open 的,那么訪問標(biāo)志要寫為 ACC_OPEN,否則寫為0。最后一個(gè)參數(shù)是模塊的版本,可以為 null。
模塊中有三種定義信息的模式:
顯式定義,也就是在文件中顯式寫出
隱式定義,由模塊的依賴關(guān)系自行推斷而出
既不是顯式定義也不是隱式定義,由編譯器編譯時(shí)決定添加
如果訪問標(biāo)志含有 ACC_MANDATED,則說明這個(gè)信息是被隱式定義的;如果訪問標(biāo)志含有 ACC_SYNTHETIC,則這個(gè)模塊信息既不是顯式定義也不是隱式定義。所有使用訪問標(biāo)志的模塊信息都可以使用這兩個(gè)標(biāo)志,下文將不再重復(fù)出現(xiàn)。
接下來就是填充模塊的信息,這些信息與 ModuleVisitor 類中的方法一一對(duì)應(yīng)。
requires 語句對(duì)應(yīng) visitRequire 方法,第一個(gè)參數(shù)是依賴模塊的名稱,最后一個(gè)參數(shù)是依賴模塊的版本。第二個(gè)參數(shù)訪問標(biāo)志可以使用 ACC_TRANSITIVE 和 ACC_STATIC_PHASE,分別代表了 transitive 和 static。
由于所有的模塊都隱式定義了 requires java.base,所以所有模塊都必須像下面一樣寫入:
exports 和 opens 語句分別使用了 visitExport 和 visitOpen 方法,參數(shù)分別是全限定包名、訪問標(biāo)志和指定模塊列表,最后一個(gè)參數(shù)可以為 null,代表向所有模塊開放。
uses 語句使用 visitUse 方法寫入,它唯一的參數(shù)代表服務(wù)類全限定名。
provides ... with ... 語句使用 visitProvide 方法,其中第一個(gè)參數(shù)代表服務(wù)類的全限定名,第二個(gè)則是實(shí)現(xiàn)類的列表。
現(xiàn)在,ModuleVisitor 類中還有兩個(gè)方法我們沒有說到:visitPackage 和 visitMainClass。這兩個(gè)方法的實(shí)際作用不大,所以不講解它的用法。

到這里模塊的知識(shí)就結(jié)束了,下一篇專欄將講述注解的使用。
若無特殊說明,所有字節(jié)碼均以 Java 17 為標(biāo)準(zhǔn)。
參考資料:
The Java Language Specification (Java SE 17 Edition)
https://docs.oracle.com/javase/specs/jls/se17/html/index.html
The Java Virtual Machine Specification (Java SE 17 Edition)
https://docs.oracle.com/javase/specs/jvms/se17/html/index.html
ASM 9.4 Javadoc
https://asm.ow2.io/javadoc/index.html
如果對(duì)文章內(nèi)容有問題或文章有錯(cuò)誤可以評(píng)論區(qū)或私信指出。