前言
JDK9引入的Java模組化系統(Java Platform Module System ,JPMS)是 對Java技術的一次重要升級,除了像之前JAR包那樣充當程式碼的容器之外,還包括:
- 依賴其他模組的列表。
- 匯出的包列表,即其他模組可以使用的列表。
- 開放的包列表,即其他模組可反射訪問模組的列表。
- 使用的服務列表。
- 提供服務的實現列表。
模組化系統
可配置的封裝隔離機制解決了原來類路徑上跨檔案的public類的可訪問性的問題。public型別不再意味著所有地方程式碼都可以訪問它們,未匯出和未開放的類是不能夠被外部使用。
舉例說明:
新建立一個maven工程,並建立兩個module。
在每個module的Language level 和 SDK 設定成JDK9
然後在每個module的頂層目錄中建立module-info.java
在ExampleOne中建立兩個不同package下的類,ExampleFirst
和ExampleOne
。
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前後三層類載入器的架構圖對比如下: