深入理解JVM(③)Java的模組化

紀莫發表於2020-07-01

前言

JDK9引入的Java模組化系統(Java Platform Module System ,JPMS)是 對Java技術的一次重要升級,除了像之前JAR包那樣充當程式碼的容器之外,還包括:

  • 依賴其他模組的列表。
  • 匯出的包列表,即其他模組可以使用的列表。
  • 開放的包列表,即其他模組可反射訪問模組的列表。
  • 使用的服務列表。
  • 提供服務的實現列表。

模組化系統

可配置的封裝隔離機制解決了原來類路徑上跨檔案的public類的可訪問性的問題。public型別不再意味著所有地方程式碼都可以訪問它們,未匯出未開放的類是不能夠被外部使用。

舉例說明:

新建立一個maven工程,並建立兩個module。

在這裡插入圖片描述

在每個module的Language level 和 SDK 設定成JDK9

在這裡插入圖片描述在這裡插入圖片描述

然後在每個module的頂層目錄中建立module-info.java

在這裡插入圖片描述

在ExampleOne中建立兩個不同package下的類,ExampleFirstExampleOne

package com.jimoer.jdkmoduleOne.test;

public class ExampleFirst {

    private int id;

    private String name;

    private String sex;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    @Override
    public String toString() {
        return "ExampleFirst{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                '}';
    }
}
package com.jimoer.jdkmoduleOne;

public class ExampleOne {


    private int id;

    private String name;

    private String arg;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getArg() {
        return arg;
    }

    public void setArg(String arg) {
        this.arg = arg;
    }

    @Override
    public String toString() {
        return "ExampleOne{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", arg='" + arg + '\'' +
                '}';
    }
}

然後在ExampleOne的module-info.java中宣告匯出包的路徑。

module exampleOne {

     // 匯出包路徑
    exports com.jimoer.jdkmoduleOne.test;

}

在ExampleTwo中宣告requires為引入包

module exampleTwo {
    requires exampleOne;
}

然後在ExampleTwo中使用ExampleOne中的類

ublic class ExampleTwo {

    public static void main(String[] args) {

        ExampleFirst first = new ExampleFirst();

        first.setId(1);
        first.setName("餘歡水");
        first.setSex("男");
        
        System.out.println(first);
    }
}

執行結果:

ExampleFirst{id=1, name='餘歡水', sex='男'}

但是當在ExampleTwo中引用ExampleOne中非匯出包下的類時就會編譯出錯。
在這裡插入圖片描述
如上圖提示,所有不被匯出的包預設都被封裝在模組裡面。

模組的相容性

為了使可配置的封裝隔離機制能夠相容傳統的類路徑查詢機制,JDK9提出了與“類路徑”(ClassPath)相對應的“模組路徑”(ModulePath)的概念。只要放在類路徑上的JAR檔案,都會被當作傳統的JAR包來對待;相應地,只要放在模組路徑上的JAR檔案,即使沒有使用JMOD字尾,甚至不包含module-info.class檔案,也仍然會被當作一個模組來對待。

為了保證Java應用升級到JDK9之後依然使用傳統的類路徑,不會受到影響,制定了三條規則來保證相容性。

  • JAR檔案在類路徑的訪問規則:所有類路徑下的JAR檔案及其他資原始檔,都被視為自動打包在一個匿名模組(Unnamed Module)裡,這個匿名模組幾乎是沒有任何隔離的,它可以看到和使用類路徑上所有的包、JDK模組中所有的匯出包,以及模組路徑是哪個所有模組中匯出的包。
  • 模組在模組路徑的訪問規則:模組路徑下的具名模組(Named Module)只能訪問到她依賴定義中列明依賴的模組和包,匿名模組裡所有的內容對具名模組來說都是不可見的,即具名模組看不見傳統JAR包的內容。
  • JAR檔案在模組路徑的訪問規則:如果把一個傳統的、不包含模組定義的JAR檔案放置到模組路徑中,它就會變成一個自動模組(Automatic Module)。

模組化下的類載入器

JDK9為了保證相容性,依然保持了三層類載入器架構以及雙親委派模型。但是為了模組化系統的順利實施,還是對類載入器做了一些改動。

  • 首先,擴充套件類載入器(Extension Class Loader)被平臺類載入器(Platform Class Loader)取代。因為模組化天然的支援擴充套件,自然不需要在存在擴充套件類載入器了。
  • 其次,平臺類載入器和應用類載入器都不再派生自java.net.URLClassLoader,如果有程式直接依賴了這種繼承關係,或者依賴了URLClassLoader類特定方法,那程式碼很可能會在JDK9及更高版本的JDK中崩潰。
  • 最後,JDK9中雖然仍然維持著三層類載入器和雙親委派架構,但類載入的委派關係也發生了變動。當平臺及應用程式類載入器收到類載入請求,在委派給父載入器載入前,要先判斷該類是否能夠歸屬到某一個系統模組中,如果可以找到這樣的歸屬關係,就要優先委派給負責哪個模組的載入器完成載入,這可以算是對雙親委派的第四次破壞
    JDK9前後三層類載入器的架構圖對比如下:
    在這裡插入圖片描述

相關文章