JVM 與 module-path


首先,對於模組平臺系統要知道的第一件事是,它跟 Java 程式語言本身沒有關係,而是為了管理程式庫本身的功能封裝、程式庫之間的相依性等需求而存在,將一個不支援模組的程式庫改為支援模組,基本上不用修改程式庫中的程式碼。

那麼為什麼模組可以改進程式庫的封裝性與相依性?如果你基於 A 程式庫撰寫了新的程式庫,接著有同事想要使用你的程式庫,而你不想要他直接呼叫 A 程式庫的相關功能,以免他撰寫的程式直接依賴在 A 程式庫上,就目前你知道的知識來說,只要類別路徑上可以找得到 A 程式庫的相關類別,他就可以呼叫相關功能,日後程式庫之間錯綜複雜的相依性就從此開始了,而這也是目前Java生態圈中面臨的重大問題之一。

當然,Java 生態圈 20 幾年來,也為了這樣的問題提出了解決方案,也有第三方(Third-party)的模組系統,而為了要統一模組平臺的規格,以及為 Java SE 平臺瘦身(讓小型運算裝置可以依需求下載必要的模組而不是整個JRE)、改進安全等因素,Java SE 9 決定納入模組平台系統成為標準之一。

模組平臺系統跟 Java 程式語言本身沒有關係,而且指令上的運用較為複雜,基本上會有相關開發工具代勞指令處理這些細節,不過,透過一些手動建立模組的過程,有助於瞭解模組的基本架構,也比較清 楚開發工具大致上代勞了哪些細項。

那麼就來開始建立第一個模組吧!首先看看,一個未支援模組的程式專案要如何設定,才能使之成為模組。請複製 範 例檔資料夾 中的 Hello2 資料夾至 C:\workspace,Hello2 中有個 src 資料夾,其中有 cc\openhome\Main.java檔案,你應該已經懂得它撰寫的內容了:

package cc.openhome;

public class Main {
    public static void main(String[] args) {
        System.out.println("Hello, World");
    }
}

根據先前幾個小節的介紹,你應該知道在進入 Hello2 資料夾後,可以使用底下的指令來編譯並執行程式:

javac -d classes src/cc/openhome/Main.java
java -cp classes cc.openhome.Main

這是基於類別路徑的方式,若想要設定它為模組,第一件事是決定模組名稱,這邊假設模組名稱為 cc.openhome, 建議建立一個與模組名稱相同的資料夾,然後其中依套件設定的階層放入原始碼,在這邊可以在 src 中建立 cc.openhome 資料夾,然後將原先 src 中的 cc 資料夾放到 cc.openhome 資料夾之中:

JVM 與 module-path

接下來,在 cc.openhome 資料夾中建立一個 module-info.java 並撰寫底下內容:

JVM 與 module-path

這麼一來,在原始碼層面上,你就建立了第一個模組了,雖然 module-info.java 的副檔名為.java,它實際上只是個設定檔,其中的 module 關鍵字僅在這個設定檔中進行設定之用,不是 Java 程式語言的一部份,副檔名為 .java,單純只是為了相容性,讓 javac 等工具程式易於處理這個設定檔罷了。

上圖的 module 關鍵字定義了模組名稱為 cc.openhome,除此之外 沒有其他設定,這表示目前只能存取同一模組以及 Java 標準 API 的 java.base 模組,java.base 模組中包含了像是 java.lang 等常用的套件;言下之意也表示,日後必要的話,可以在 module-info.java 中設定自己的模組可以公開哪些 API,或者是依賴在哪個模組之上。

那麼來編譯程式碼吧!可以將編譯出來的類別放在 mods 資料夾中對應模組名稱的資料夾之中:

JVM 與 module-path

這麼一來,mods 中的 cc.openhome 就是你第一個編譯成功的模組了,其他開發者若要使用這個模組,可以在執行 java 時,透過 --module-path 指定模組路徑。

由於 Main.java 中實際上撰寫了程式進入點,如果想要執行它的話,可以透過 -m 指定模組的程式進入點,例如:

JVM 與 module-path

圖中使用了 --module-path 指定了模組路徑,而不是使用 -classpath 指定類別路徑,這個時候要注意的是,在完全吻合名稱之前還要指定模組名稱,然而,這僅僅只是工具層面的需求,在程式碼撰寫上,使用模組中的 API 並不用進行任何變更。

基於相容性,模組也可以基於類別路徑來使用,方式與先前的說明是相同的,例如:

JVM 與 module-path

按照規範,在類別路徑下被發現的類別,都會被自動歸類到一個未命名的模組(unnamed module),就目前來說,你只要知道這些類別彼此之間的可見性,與 Java SE 8 或先前版本的可見性相同就可以了,如果馬上就想進一步認識未命名模組,可以參考〈The unnamed module〉中的介紹。