java模块_Java 9 揭秘(2. 模块化系统)
文 by / 林本托
Tips
做一個(gè)終身學(xué)習(xí)的人。
在此章節(jié)中,主要介紹以下內(nèi)容:
在JDK 9之前Java源代碼用于編寫,打包和部署的方式以及該方法的潛在問題
JDK 9中有哪些模塊
如何聲明模塊及其依賴關(guān)系
如何封裝模塊
什么是模塊路徑
什么是可觀察的模塊
如何打印可觀察模塊的列表
如何打印模塊的描述
本章旨在為你簡要概述JDK 9中引入的模塊系統(tǒng)。后續(xù)章節(jié)將詳細(xì)介紹所有這些概念,并附有實(shí)例。 不要擔(dān)心,如果你第一次不了解所有模塊相關(guān)的概念。 一旦你獲得開發(fā)模塊代碼的經(jīng)驗(yàn),你可以回來并重新閱讀本章。
一. Java 9 之前的開發(fā)
在 JDK 9之前,開發(fā)一個(gè) Java 應(yīng)用程序通常包括以下步驟:
Java源代碼以Java類型(如類,接口,枚舉和注釋)的形式編寫。
不同的Java類型被安排在一個(gè)包(package)中,而且始終屬于一個(gè)明確或默認(rèn)的包。 一個(gè)包是一個(gè)邏輯的類型集合,本質(zhì)上為它包含的類型提供一個(gè)命名空間。 即使聲明為public,包可能包含公共類型,私有類型和一些內(nèi)部實(shí)現(xiàn)類型。
編譯的代碼被打包成一個(gè)或多個(gè)JAR文件,也稱為應(yīng)用程序JAR,因?yàn)樗鼈儼瑧?yīng)用程序代碼。 一個(gè)程序包中的代碼可能會(huì)引用多個(gè)JAR。
應(yīng)用程序可能使用類庫。 類庫作為一個(gè)或多個(gè)JAR文件提供給應(yīng)用程序使用。
通過將所有JAR文件,應(yīng)用程序JAR文件和JAR類庫放在類路徑上來部署應(yīng)用程序。
下圖顯示了JAR文件中打包的代碼的典型布局。 該圖僅顯示了包和Java 類型,不包括其他內(nèi)容,如manifest.mf文件和資源文件。
20多年來,Java社區(qū)以這種編寫,編譯,打包和部署Java代碼的方式開發(fā)。 但是,20年漫長的旅程并沒有像你所希望的一樣順利! 這樣安排和運(yùn)行Java代碼就存在固有的問題:
一個(gè)包只是一個(gè)類型的容器,而不強(qiáng)制執(zhí)行任何可訪問性邊界。包中的公共類型可以在所有其他包中訪問;沒有辦法阻止在一個(gè)包中公開類型的全局可見性。
除了以java和javax開頭的包外,包應(yīng)該是開放擴(kuò)展的。如果你在具有包級(jí)別訪問的JAR中進(jìn)行了類型化,則可以在其他JAR中訪問定義與你的名稱相同的包中的類型。
Java運(yùn)行時(shí)會(huì)看到從JAR列表加載的一組包。沒有辦法知道是否在不同的JAR中有多個(gè)相同類型的副本。Java運(yùn)行時(shí)首先加載在類路徑中遇到的JAR中找到的類型。
Java運(yùn)行時(shí)可能會(huì)出現(xiàn)由于應(yīng)用程序在類路徑中需要的其中一個(gè)JAR引起的運(yùn)行時(shí)缺少類型的情況。當(dāng)代碼嘗試使用它們時(shí),缺少的類型會(huì)引起運(yùn)行時(shí)錯(cuò)誤。
在啟動(dòng)時(shí)沒有辦法知道應(yīng)用程序中使用的某些類型已經(jīng)丟失。還可以包含錯(cuò)誤的JAR文件版本,并在運(yùn)行時(shí)產(chǎn)生錯(cuò)誤。
這些問題在Java社區(qū)中非常頻繁和臭名昭著,他們得到了一個(gè)名字 ——JAR-hell。
包裝JDK和JRE也是一個(gè)問題。 它們作為一個(gè)整體作為使用,從而增加了下載時(shí)間,啟動(dòng)時(shí)間和內(nèi)存占用。 單體JRE使得Java不可能在內(nèi)存很小的設(shè)備上使用。 如果將Java應(yīng)用程序部署到云端,則需要支付更多的費(fèi)用購買更多的使用內(nèi)存。 大多數(shù)情況下,單體JRE使用的內(nèi)存比所需的內(nèi)存多,這意味著需要為云服務(wù)支付更多的內(nèi)存。 Java 8中引入的Compact配置文件通過允許將JRE的一個(gè)子集打包在稱為緊湊配置文件的自定義運(yùn)行時(shí)映像中,大大減少了JRE大小,從而減少了運(yùn)行時(shí)內(nèi)存占用。
Tips
在早期訪問版本中,JDK 9包含三個(gè)名為java.compact1,java.compact2和java.compact3的模塊,這些模塊對應(yīng)于JDK 8中的三個(gè)compact配置文件。之后,它們被刪除,因?yàn)镴DK中的模塊可以完全控制在自定義JRE中包含的模塊列表。
可以將JDK 9之前的JDK/JRE中的這些問題分為三類:
不可靠的配置
弱封裝
JDK/JRE的單體結(jié)構(gòu)
下圖顯示了Java運(yùn)行時(shí)如何看到類路徑上的所有JAR,以及如何從其他JAR訪問一個(gè)JAR中的代碼,沒有任何限制,除了在訪問控制方面由類型聲明指定的代碼。
Java 9通過引入開發(fā),打包和部署Java應(yīng)用程序的新方法來解決這些問題。 在Java 9中,Java應(yīng)用程序由稱為模塊的小型交互組件組成。 Java 9也已經(jīng)將JDK/JRE組織為一組模塊。
二. 全新的模塊系統(tǒng)
Java 9引入了一個(gè)稱為模塊的新的程序組件。 您可以將Java應(yīng)用程序視為具有明確定義的邊界和這些模塊之間依賴關(guān)系的交互模塊的集合。 模塊系統(tǒng)的開發(fā)具有以下目標(biāo):
可靠的配置
強(qiáng)封裝
模塊化JDK/JRE
這些目標(biāo)是解決Java 9之前開發(fā)和部署Java應(yīng)用程序所面臨的問題。
可靠的配置解決了用于查找類型的容易出錯(cuò)的類路徑機(jī)制的問題。 模塊必須聲明對其他模塊的顯式依賴。 模塊系統(tǒng)驗(yàn)證應(yīng)用程序開發(fā)的所有階段的依賴關(guān)系 —— 編譯時(shí),鏈接時(shí)和運(yùn)行時(shí)。 假設(shè)一個(gè)模塊聲明對另一個(gè)模塊的依賴,并且第二個(gè)模塊在啟動(dòng)時(shí)丟失。 JVM檢測到依賴關(guān)系丟失,并在啟動(dòng)時(shí)失敗。 在Java 9之前,當(dāng)使用缺少的類型時(shí),這樣的應(yīng)用程序會(huì)生成運(yùn)行時(shí)錯(cuò)誤(不是在啟動(dòng)時(shí))。
強(qiáng)大的封裝解決了類路徑上跨JAR的公共類型的可訪問性問題。 模塊必須明確聲明其中哪些公共類型可以被其他模塊訪問。 除非這些模塊明確地使其公共類型可訪問,否則模塊不能訪問另一個(gè)模塊中的公共類型。 Java 9中的公共類型并不意味著程序的所有部分都可以訪問它。 模塊系統(tǒng)增加了更精細(xì)的可訪問性控制。
Tips
Java 9通過允許模塊在開發(fā)的所有階段聲明明確的依賴關(guān)系并驗(yàn)證這些依賴關(guān)系來提供可靠的配置。它通過允許模塊聲明其公共類型可以訪問其他模塊的軟件包來提供強(qiáng)大的封裝。
JDK 9通過將其前身的體結(jié)構(gòu)分解成一組稱為平臺(tái)模塊的模塊來重寫。 JDK 9還引入了一個(gè)可選的階段,稱為鏈接時(shí),這可能在編譯時(shí)和運(yùn)行時(shí)之間發(fā)生。 在鏈接期間,使用一個(gè)鏈接器,它是JDK 9附帶的一個(gè)名為jlink的工具,用于創(chuàng)建應(yīng)用程序的自定義運(yùn)行時(shí)映像,其中僅包含應(yīng)用程序中使用的模塊。 這將運(yùn)行時(shí)的大小調(diào)整到最佳大小。
三. 什么是模塊化
模塊是代碼和數(shù)據(jù)集合。 它可以包含Java代碼和本地代碼。 Java代碼被組織為一組包含諸如類,接口,枚舉和注解等類型的類。 數(shù)據(jù)可以包括諸如圖像文件和配置文件的資源。
對于Java代碼,模塊可以看做零個(gè)或多個(gè)包的集合。 下圖顯示了三個(gè)名為policy,claim和utility的模塊,其中policy模塊包含兩個(gè)包,claim模塊包含一個(gè)包,而utility模塊不包含任何包。
一個(gè)模塊不僅僅是一個(gè)包的容器。 除了其名稱,模塊定義包含以下內(nèi)容:
所需的其他模塊(或依賴于)的列表
導(dǎo)出的軟件包列表(其公共API),其他模塊可以使用
開放的軟件包(其整個(gè)API,公共和私有)到其他反射訪問模塊的列表
使用的服務(wù)列表(或使用java.util.ServiceLoader類發(fā)現(xiàn)和加載)
提供的服務(wù)的實(shí)現(xiàn)列表
在使用這些模塊時(shí),可以使用這些方面中的一個(gè)或多個(gè)。
Java SE 9平臺(tái)規(guī)范將平臺(tái)劃分為稱為平臺(tái)模塊的一組模塊。 Java SE 9平臺(tái)的實(shí)現(xiàn)可能包含一些或所有平臺(tái)模塊,從而提供可擴(kuò)展的Java運(yùn)行時(shí)。 標(biāo)準(zhǔn)模塊的名字是以Java 為前綴。 Java SE標(biāo)準(zhǔn)模塊的示例有java.base,java.sql,java.xml和java.logging。 支持標(biāo)準(zhǔn)平臺(tái)模塊中的API,供開發(fā)人員使用。
非標(biāo)準(zhǔn)平臺(tái)模塊是JDK的一部分,但未在Java SE平臺(tái)規(guī)范中指定。 這些JDK特定的模塊的名稱以jdk為前綴。 JDK特定模塊的示例是jdk.charsets,jdk.compiler,jdk.jlink,jdk.policytool和jdk.zipfs。 JDK特定模塊中的API不適用于開發(fā)人員。 這些API通常用于JDK本身以及不能輕易獲得使用Java SE API所需功能的庫開發(fā)人員使用。 如果使用這些模塊中的API,則可能會(huì)在未經(jīng)通知的情況下對其進(jìn)行支持或更改。
JavaFX不是Java SE 9平臺(tái)規(guī)范的一部分。 但是,在安裝JDK/JRE時(shí),會(huì)安裝與JavaFX相關(guān)的模塊。 JavaFX模塊名稱以javafx為前綴。 JavaFX模塊的示例是javafx.base,javafx.controls,javafx.fxml,javafx.graphics和javafx.web。
作為Java SE 9平臺(tái)的一部分的java.base模塊是原始模塊。 它不依賴于任何其他模塊。 模塊系統(tǒng)只知道java.base模塊。 它通過模塊中指定的依賴關(guān)系發(fā)現(xiàn)所有其他模塊。 java.base模塊導(dǎo)出核心Java SE軟件包,如java.lang,java.io,java.math,java.text,java.time,java.util等。
四. 模塊依賴關(guān)系
包括JDK 8之前的版本,一個(gè)包中的公共類型可以被其他包訪問,沒有任何限制。 換句話說,包沒有控制它們包含的類型的可訪問性。 JDK 9中的模塊系統(tǒng)對類型的可訪問性提供了細(xì)粒度的控制。
模塊之間的可訪問性是所使用的模塊和使用模塊之間的雙向協(xié)議:模塊明確地使其公共類型可供其他模塊使用,并且使用這些公共類型的模塊明確聲明對第一個(gè)模塊的依賴。 模塊中的所有未導(dǎo)出的軟件包都是模塊的私有的,它們不能在模塊之外使用。
將包中的 API 設(shè)置為公共供其他模塊使用被稱之為導(dǎo)出包。如果名為policy的模塊將名為pkg1的包設(shè)置為公共類型可用于其他模塊訪問,則說明policy模塊導(dǎo)出包pkg1。如果名為claim的模塊聲明對policy模塊的依賴性,則稱之為claim模塊讀取(read)policy模塊。這意味著,在claim模塊內(nèi)部可以訪問policy模塊導(dǎo)出包中的所有公共類型。模塊還可以選擇性地將包導(dǎo)出到一個(gè)或多個(gè)命名模塊。這種導(dǎo)出成為qualified導(dǎo)出或module-friendly導(dǎo)出。 qualified導(dǎo)出中的包中的公共類型只能訪問指定的命名模塊。
在模塊系統(tǒng)的上下文中,可以互換使用三個(gè)術(shù)語 —— 需要(require),讀取(read)和依賴(depend)。 以下三個(gè)語句意思相同:P讀取Q,P需要Q,P依賴Q,其中P和Q指的是兩個(gè)模塊。
下圖描述了兩個(gè)名為policy和claim的模塊之間的依賴關(guān)系。 policy模塊包含兩個(gè)名為pkg1和pkg2的包,它導(dǎo)出包pkg1,該包使用虛線邊界顯示,以將其與未導(dǎo)出的包pkg2區(qū)分開來。 claim模塊包含兩個(gè)件包pkg3和pkg4,它不導(dǎo)出包。 它聲明了對policy模塊的依賴。
在JDK 9中,您可以如下聲明這兩個(gè)模塊:
module policy {
exports pkg1;
}
module claim {
requires policy;
}
Tips
用于指示模塊中的依賴關(guān)系的語法是不對稱的 ——導(dǎo)出一個(gè)包,但需要一個(gè)模塊。
如果你的模塊依賴于另一個(gè)模塊,則該模塊聲明要求知道模塊名稱。幾個(gè)Java框架和工具在很大程度上依賴于反射來在運(yùn)行時(shí)訪問未導(dǎo)出的模塊的代碼。它們提供了很大的功能,如依賴注入,序列化,Java Persistence API的實(shí)現(xiàn),代碼自動(dòng)化和調(diào)試。Spring,Hibernate和XStream是這樣的框架和庫的例子。這些框架和庫不了解你的應(yīng)用程序模塊。 但是,他們需要訪問模塊中的類型來完成他們的工作。 他們還需要訪問模塊的私有成員,這打破了JDK 9中強(qiáng)封裝的前提。當(dāng)模塊導(dǎo)出軟件包時(shí),依賴于第一個(gè)模塊的其他模塊只能訪問導(dǎo)出的軟件包中的公共API。 在運(yùn)行時(shí),在模塊的所有軟件包上授予深入的反射訪問權(quán)限(訪問公共和私有API),可以聲明一個(gè)開放的模塊。
在JDK 9中,可以如下聲明這兩個(gè)模塊:
open module policy.model {
requires jdojo.jpa;
}
module jdojo.jpa {
// The module exports its packages here
}
1. 模塊圖
模塊系統(tǒng)只知道一個(gè)模塊:java.base。 java.base模塊不依賴于任何其他模塊。 所有其他模塊都隱含地依賴于java.base模塊。
應(yīng)用程序的模塊化結(jié)構(gòu)可以被視為一個(gè)稱為模塊圖。 在模塊圖中,每個(gè)模塊都表示為一個(gè)節(jié)點(diǎn)。 如果第一個(gè)模塊依賴于第二個(gè)模塊,則存在從模塊到另一個(gè)模塊的有向邊。 通過將稱為根模塊的一組初始模塊的依賴關(guān)系與稱為可觀察模塊的模塊系統(tǒng)已知的一組模塊相結(jié)合來構(gòu)建模塊圖。
Tips
模塊解析意味著該模塊所依賴的模塊可用。 假設(shè)名為P的模塊取決于兩個(gè)名為Q和R的模塊。解析模塊P表示您定位模塊Q和R,并遞歸地解析模塊Q和R。
構(gòu)建模塊圖旨在在編譯時(shí),鏈接時(shí)和運(yùn)行時(shí)解析模塊依賴關(guān)系。 模塊解析從根模塊開始,并遵循依賴關(guān)系鏈接,直到達(dá)到j(luò)ava.base模塊。 有時(shí),可能在模塊路徑上有一個(gè)模塊,但是會(huì)收到該模塊未找到的錯(cuò)誤。 如果模塊未解析,并且未包含在模塊圖中,則可能會(huì)發(fā)生這種情況。 對于要解決的模塊,需要從根模塊開始依賴關(guān)系鏈。 根據(jù)調(diào)用編譯器或Java啟動(dòng)器的方式,選擇一組默認(rèn)的根模塊。 還可以將模塊添加到默認(rèn)的根模塊中。 了解在不同情況下如何選擇默認(rèn)根模塊很重要:
如果應(yīng)用程序代碼是從類路徑編譯的,或者主類是從類路徑運(yùn)行的,則默認(rèn)的根模塊將由java.se模塊和所有非“java.”系統(tǒng)模塊組成,如“jdk.”和“JavaFX.”。 如果java.se模塊不存在,則默認(rèn)的根模塊將由所有“java.”和非“java.*”模塊組成。
如果您的應(yīng)用程序由模塊組成,則默認(rèn)的根模塊將依賴于以下階段:
在編譯時(shí),它由所有正在編譯的模塊組成。
在鏈接時(shí),它是空的。
在運(yùn)行時(shí),它包含有主類的模塊。 在java命令中使用--module或-m選項(xiàng)指定要運(yùn)行的模塊及其主類。
繼續(xù)介紹policy和claim模塊的例子,假設(shè)pkg3.Main是claim模塊中的主類,并且兩個(gè)模塊都作為模塊化JAR打包在C:\ Java9Revealed\lib目錄中。下圖顯示了使用以下命令運(yùn)行應(yīng)用程序時(shí)在運(yùn)行時(shí)構(gòu)建的模塊圖:
C:\Java9Revealed>java -p lib -m claim/pkg3.Main
claim模塊包含應(yīng)用程序的主類。 因此,claim是創(chuàng)建模塊圖時(shí)唯一的根模塊。 policy模塊需要被解決,因?yàn)閏laim模塊依賴于policy模塊。 還需要解析java.base模塊,因?yàn)樗衅渌K都依賴于它,這兩個(gè)模塊也是如此。
模塊圖的復(fù)雜性取決于根模塊的數(shù)量和模塊之間的依賴關(guān)系。 假設(shè)除了依賴于policy模塊之外,claim模塊還取決于java.sql的平臺(tái)模塊。 claim模塊的新聲明如下所示:
module policy {
requires policy;
requires java.sql;
}
如下圖,顯示在claim模塊中運(yùn)行pkg3.Main類時(shí)將構(gòu)建的模塊圖。 請注意,java.xml和java.logging模塊也存在于圖中,因?yàn)閖ava.sql模塊依賴于它們。 在圖中,claim模塊是唯一的根模塊。
下圖顯示了java.se的平臺(tái)模塊的最復(fù)雜的模塊圖形之一。 java.se模塊的模塊聲明如下:
java.se 模塊的定義如下所示:
module java.se {
requires transitive java.sql;
requires transitive java.rmi;
requires transitive java.desktop;
requires transitive java.security.jgss;
requires transitive java.security.sasl;
requires transitive java.management;
requires transitive java.logging;
requires transitive java.xml;
requires transitive java.scripting;
requires transitive java.compiler;
requires transitive java.naming;
requires transitive java.instrument;
requires transitive java.xml.crypto;
requires transitive java.prefs;
requires transitive java.sql.rowset;
requires java.base;
requires transitive java.datatransfer;
}
有時(shí),需要將模塊添加到默認(rèn)的根模塊中,以便解析添加的模塊。 可以在編譯時(shí),鏈接時(shí)和運(yùn)行使用--add-modules命令行選項(xiàng)指定其他根模塊:
--add-modules
這里的是逗號(hào)分隔的模塊名稱列表。
可以使用以下特殊值作為具有特殊含義的--add-modules選項(xiàng)的模塊列表:
ALL-DEFAULT
ALL-SYSTEM
ALL-MODULE-PATH
所有三個(gè)特殊值在運(yùn)行時(shí)都有效。 只能在編譯時(shí)使用ALL-MODULE-PATH。
如果使用ALL-DEFAULT作為模塊列表,則從應(yīng)用程序從類路徑運(yùn)行時(shí)使用的默認(rèn)的根模塊集將添加到根集中。 這對于作為容器的應(yīng)用程序是有用的,托管可能需要容器應(yīng)用程序本身不需要的其他模塊的其他應(yīng)用程序。 這是一種使所有Java SE模塊可用于容器的方法,因此任何托管的應(yīng)用程序都可能使用到它們。
如果將ALL-SYSTEM用作模塊列表,則將所有系統(tǒng)模塊添加到根集中。 這對于運(yùn)行測試時(shí)非常有用。
如果使用ALL-MODULE-PATH作為模塊列表,則在模塊路徑上找到的所有模塊都將添加到根集中。 這對于諸如Maven這樣的工具非常有用,這確保了應(yīng)用程序需要模塊路徑上的所有模塊。
Tips
即使模塊存在于模塊路徑上,也可能會(huì)收到模塊未找到的錯(cuò)誤。 在這種情況下,需要使用--add-modules命令行選項(xiàng)將缺少的模塊添加到默認(rèn)的根模塊中。
JDK 9支持一個(gè)有用的非標(biāo)準(zhǔn)命令行選項(xiàng),它打印描述在構(gòu)建模塊圖時(shí)用于解析模塊的步驟的診斷消息。 選項(xiàng)是-Xdiag:resolver。 以下命令在聲明模塊中運(yùn)行pkg3.Main類。 顯示部分輸出。 在診斷消息的結(jié)尾,你會(huì)發(fā)現(xiàn)一個(gè)結(jié)果:部分列出了解決模塊。
使用命令C:\Java9Revealed>java -Xdiag:resolver -p lib -m claim/pkg3.Main,會(huì)得到如下輸出:
[Resolver] Root module claim located
[Resolver] (file:///C:/Java9Revealed/lib/claim.jar)
[Resolver] Module java.base located, required by claim
[Resolver] (jrt:/java.base)
[Resolver] Module policy located, required by claim
[Resolver] (file:///C:/Java9Revealed/lib/policy.jar)
...
[Resolver] Result:
[Resolver] claim
[Resolver] java.base
...
[Resolver] policy
五. 聚合模塊
你可以創(chuàng)建一個(gè)不包含任何代碼的模塊。 它收集并重新導(dǎo)出其他模塊的內(nèi)容。 這樣的模塊稱為聚合模塊。假設(shè)有幾個(gè)模塊依賴于五個(gè)模塊。 您可以為這五個(gè)模塊創(chuàng)建一個(gè)聚合模塊,現(xiàn)在,你的模塊只能依賴于一個(gè)模塊 —— 聚合模塊。
為了方便, Java 9包含幾個(gè)聚合模塊,如java.se和java.se.ee。 java.se模塊收集Java SE的不與Java EE重疊的部分。 java.se.ee模塊收集組成Java SE的所有模塊,包括與Java EE重疊的模塊。
六. 聲明模塊
本節(jié)包含用于聲明模塊的語法的快速概述。 在以后的章節(jié)中更詳細(xì)地解釋每個(gè)部分。 如果不明白本節(jié)提到的模塊,請繼續(xù)閱讀。
使用模塊聲明來定義模塊,是Java編程語言中的新概念。其語法如下:
[open] module {
;
;
...
}
open修飾符是可選的,它聲明一個(gè)開放的模塊。 一個(gè)開放的模塊導(dǎo)出所有的包,以便其他模塊使用反射訪問。 是要定義的模塊的名稱。 是一個(gè)模塊語句。 模塊聲明中可以包含零個(gè)或多個(gè)模塊語句。 如果它存在,它可以是五種類型的語句之一:
導(dǎo)出語句(exports statement);
開放語句(opens statement);
需要語句(requires statement);
使用語句(uses statement);
提供語句(provides statement)。
導(dǎo)出和開放語句用于控制對模塊代碼的訪問。 需要語句用于聲明模塊對另一個(gè)模塊的依賴關(guān)系。 使用和提供的語句分別用于表達(dá)服務(wù)消費(fèi)和服務(wù)提供。 以下是名為myModule的模塊的模塊聲明示例:
module myModule {
// Exports the packages - com.jdojo.util and
// com.jdojo.util.parser
exports com.jdojo.util;
exports com.jdojo.util.parser;
// Reads the java.sql module
requires java.sql;
// Opens com.jdojo.legacy package for reflective access
opens com.jdojo.legacy;
// Uses the service interface java.sql.Driver
uses java.sql.Driver;
// Provides the com.jdojo.util.parser.FasterCsvParser
// class as an implementation for the service interface
// named com.jdojo.util.CsvParser
provides com.jdojo.util.CsvParser
with com.jdojo.util.parser.FasterCsvParser;
}
你可以使用模塊聲明中的open修飾符來創(chuàng)建一個(gè)開放模塊。 一個(gè)開放模塊可以將其所有軟件包的反射訪問授予其他模塊。 你不能在open模塊中再使用open語句,因?yàn)樗谐绦虬际窃趏pen模塊中隱式打開的。 以下代碼段聲明一個(gè)名為myLegacyModule的開放模塊:
open module myLegacyModule {
exports com.jdojo.legacy;
requires java.sql;
}
1. 模塊命名
模塊名稱可以是Java限定標(biāo)識(shí)符。 合法標(biāo)識(shí)符是一個(gè)或多個(gè)由點(diǎn)分隔的標(biāo)識(shí)符,例如policy,com.jdojo.common和com.jdojo.util。 如果模塊名稱中的任何部分不是有效的Java標(biāo)識(shí)符,則會(huì)發(fā)生編譯時(shí)錯(cuò)誤。 例如,com.jdojo.common.1.0不是有效的模塊名稱,因?yàn)槊Q中的1和0不是有效的Java標(biāo)識(shí)符。
與包命名約定類似,使用反向域名模式為模塊提供唯一的名稱。 使用這個(gè)慣例,名為com.jdojo.common的最簡單的模塊可以聲明如下:
module com.jdojo.common {
// No module statements
}
模塊名稱不會(huì)隱藏具有相同名稱的變量,類型和包。 因此,可以擁有一個(gè)模塊以及具有相同名稱的變量,類型或包。 他們使用的上下文將區(qū)分哪個(gè)名稱是指什么樣的實(shí)體。
在JDK 9中, open, module, requires, transitive, exports, opens, to, uses, provides 和 with是受限關(guān)鍵字。只有當(dāng)具體位置出現(xiàn)在模塊聲明中時(shí),它們才具有特殊意義。 可以將它們用作程序中其他地方的標(biāo)識(shí)符。 例如,以下模塊聲明是有效的,即使它不使用直觀的模塊名稱:
// Declare a module named module
module module {
// Module statements go here
}
第一個(gè)模塊字被解釋為一個(gè)關(guān)鍵字,第二個(gè)是一個(gè)模塊名稱。
你可以在程序中的任何地方聲明一個(gè)名為module的變量:
String module = "myModule";
2. 模塊的訪問控制
導(dǎo)出語句將模塊的指定包導(dǎo)出到所有模塊或編譯時(shí)和運(yùn)行時(shí)的命名模塊列表。 它的兩種形式如下:
exports ;
exports to , ...;
以下是使用了導(dǎo)出語句的模塊示例:
module M {
exports com.jdojo.util;
exports com.jdojo.policy
to com.jdojo.claim, com.jdojo.billing;
}
開放語句允許對所有模塊的反射訪問指定的包或運(yùn)行時(shí)指定的模塊列表。 其他模塊可以使用反射訪問指定包中的所有類型以及這些類型的所有成員(私有和公共)。 開放語句采用以下形式:
opens ;
opens to , ...;
使用開放語句的實(shí)例:
module M {
opens com.jdojo.claim.model;
opens com.jdojo.policy.model to core.hibernate;
opens com.jdojo.services to core.spring;
}
Tips
對比導(dǎo)出和打開語句。 導(dǎo)出語句允許僅在編譯時(shí)和運(yùn)行時(shí)訪問指定包的公共API,而打開語句允許在運(yùn)行時(shí)使用反射訪問指定包中的所有類型的公共和私有成員。
如果模塊需要在編譯時(shí)從另一個(gè)模塊訪問公共類型,并在運(yùn)行時(shí)使用反射訪問類型的私有成員,則第二個(gè)模塊可以導(dǎo)出并打開相同的軟件包,如下所示:
module N {
exports com.jdojo.claim.model;
opens com.jdojo.claim.model;
}
閱讀有關(guān)模塊的時(shí)候會(huì)遇到三個(gè)短語:
模塊M導(dǎo)出包P
模塊M打開包Q
模塊M包含包R
前兩個(gè)短語對應(yīng)于模塊中導(dǎo)出語句和開放語句。 第三個(gè)短語意味著該模塊包含的包R既不導(dǎo)出也不開放。 在模塊系統(tǒng)的早期設(shè)計(jì)中,第三種情況被稱為“模塊M隱藏包R”。
3. 聲明依賴關(guān)系
需要(require)語句聲明當(dāng)前模塊與另一個(gè)模塊的依賴關(guān)系。 一個(gè)名為M的模塊中的“需要N”語句表示模塊M取決于(或讀取)模塊N。語句有以下形式:
requires ;
requires transitive ;
requires static ;
requires transitive static ;
require語句中的靜態(tài)修飾符表示在編譯時(shí)的依賴是強(qiáng)制的,但在運(yùn)行時(shí)是可選的。requires static N語句意味著模塊M取決于模塊N,模塊N必須在編譯時(shí)出現(xiàn)才能編譯模塊M,而在運(yùn)行時(shí)存在模塊N是可選的。require語句中的transitive修飾符會(huì)導(dǎo)致依賴于當(dāng)前模塊的其他模塊具有隱式依賴性。假設(shè)有三個(gè)模塊P,Q和R,假設(shè)模塊Q包含requires transitive R語句,如果如果模塊P包含包含requires Q語句,這意味著模塊P隱含地取決于模塊R。
4. 配置服務(wù)
Java允許使用服務(wù)提供者和服務(wù)使用者分離的服務(wù)提供者機(jī)制。 JDK 9允許使用語句(uses statement)和提供語句(provides statement)實(shí)現(xiàn)其服務(wù)。
使用語句可以指定服務(wù)接口的名字,當(dāng)前模塊就會(huì)發(fā)現(xiàn)它,使用 java.util.ServiceLoader類進(jìn)行加載。格式如下:
uses ;
使用語句的實(shí)例如下:
module M {
uses com.jdojo.prime.PrimeChecker;
}
com.jdojo.PrimeChecker是一個(gè)服務(wù)接口,其實(shí)現(xiàn)類將由其他模塊提供。 模塊M將使用java.util.ServiceLoader類來發(fā)現(xiàn)和加載此接口的實(shí)現(xiàn)。
提供語句指定服務(wù)接口的一個(gè)或多個(gè)服務(wù)提供程序?qū)崿F(xiàn)類。 它采取以下形式:
provides
with , ...;
相同的模塊可以提供服務(wù)實(shí)現(xiàn),可以發(fā)現(xiàn)和加載服務(wù)。 模塊還可以發(fā)現(xiàn)和加載一種服務(wù),并為另一種服務(wù)提供實(shí)現(xiàn)。 以下是例子:
module P {
uses com.jdojo.CsvParser;
provides com.jdojo.CsvParser
with com.jdojo.CsvParserImpl;
provides com.jdojo.prime.PrimeChecker
with com.jdojo.prime.generic.FasterPrimeChecker;
}
七. 模塊描述符
在了解上一節(jié)中如何聲明模塊之后,你可能會(huì)對模塊聲明的源代碼有幾個(gè)疑問:
在哪里保存模塊聲明的源代碼? 是否保存在文件中? 如果是,文件名是什么?
在哪里放置模塊聲明源代碼文件?
模塊的聲明的源代碼如何編譯?
1. 編譯模塊聲明
模塊聲明存儲(chǔ)在名為module-info.java的文件中,該文件存儲(chǔ)在該模塊的源文件層次結(jié)構(gòu)的根目錄下。 Java編譯器將模塊聲明編譯為名為module-info.class的文件。 module-info.class文件被稱為模塊描述符,它被放置在模塊的編譯代碼層次結(jié)構(gòu)的根目錄下。 如果將模塊的編譯代碼打包到JAR文件中,則module-info.class文件將存儲(chǔ)在JAR文件的根目錄下。
模塊聲明不包含可執(zhí)行代碼。 實(shí)質(zhì)上,它包含一個(gè)模塊的配置。 那為什么我們不在XML或JSON格式的文本文件中保留模塊聲明,而是在類文件中? 類文件被選為模塊描述符,因?yàn)轭愇募哂锌蓴U(kuò)展,明確定義的格式。 模塊描述符包含源碼級(jí)模塊聲明的編譯形式。 它可以通過工具來增強(qiáng),例如 jar工具,在模塊聲明初始編譯之后,在類文件屬性中包含附加信息。 類文件格式還允許開發(fā)人員在模塊聲明中使用導(dǎo)入和注解。
2. 模塊版本
在模塊系統(tǒng)的初始原型中,模塊聲明還包括模塊版本的。 包括模塊版本在聲明中使模塊系統(tǒng)的實(shí)現(xiàn)復(fù)雜化,所以模塊版本從聲明中刪除。
模塊描述符(類文件格式)的可擴(kuò)展格式被利用來向模塊添加版本。 當(dāng)將模塊的編譯代碼打包到JAR中時(shí),該jar工具提供了一個(gè)添加模塊版本的選項(xiàng),最后將其添加到module-info.class文件中。
3. 模塊源文件結(jié)構(gòu)
我們來看一個(gè)組織源代碼和一個(gè)名為com.jdojo.contact的模塊的編譯代碼的例子。 該模塊包含用于處理聯(lián)系信息的包,例如地址和電話號(hào)碼。 它包含兩個(gè)包:
com.jdojo.contact.info
com.jdojo.contact.validator
com.jdojo.contact.info包中包含兩個(gè)類 —— Address 和 Phone。 com.jdojo.contact.validator包中包含一個(gè)名為Validator的接口和兩個(gè)名為AddressValidator和PhoneValidator的類。
下圖顯示了com.jdojo.contact模塊中的內(nèi)容
在Java 9中,Java編譯器工具javac添加了幾個(gè)選項(xiàng)。 它允許一次編譯一個(gè)模塊或多個(gè)模塊。 如果要一次編譯多個(gè)模塊,則必須將每個(gè)模塊的源代碼存儲(chǔ)在與模塊名稱相同的目錄下。 即使只有一個(gè)模塊,也最好遵循此源目錄命名約定。
假設(shè)你想編譯com.jdojo.contact模塊的源代碼。 可以將其源代碼存儲(chǔ)在名為C:\j9r\src的目錄中,其中包含以下文件:
module-info.java
com\jdojo\contact\info\Address.java
com\jdojo\contact\info\Phone.java
com\jdojo\contact\validator\Validator.java
com\jdojo\contact\validator\AddressValidator.java
com\jdojo\contact\validator\PhoneValidator.java
請注意,需要遵循包層次結(jié)構(gòu)來存儲(chǔ)接口和類的源文件。
如果要一次編譯多個(gè)模塊,則必須將源代碼目錄命名為com.jdojo.contact,這與模塊的名稱相同。 在這種情況下,可以將模塊的源代碼存儲(chǔ)在名為C:\j9r\src的目錄中,其目錄如下:
com.jdojo.contact\module-info.java
com.jdojo.contact\com\jdojo\contact\info\Address.java
com.jdojo.contact\com\jdojo\contact\info\Phone.java
com.jdojo.contact\com\jdojo\contact\validator\Validator.java
com.jdojo.contact\com\jdojo\contact\validator\AddressValidator.java
com.jdojo.contact\com\jdojo\contact\validator\PhoneValidator.java
模塊的編譯后代碼將遵循與之前看到的相同的目錄層次結(jié)構(gòu)。
八. 打包模塊
模塊的artifact可以存儲(chǔ)在:
目錄中
模塊化的JAR文件中
JMOD文件中,它是JDK 9中引入的一種新的模塊封裝格式
1. 目錄中的模塊
當(dāng)模塊的編譯代碼存儲(chǔ)在目錄中時(shí),目錄的根目錄包含模塊描述符(module-info.class文件),子目錄是包層次結(jié)構(gòu)的鏡像。 繼續(xù)上一節(jié)中的示例,假設(shè)將com.jdojo.contact模塊的編譯代碼存儲(chǔ)在C:\j9r\mods\ com.jdojo.contact目錄中。 目錄的內(nèi)容如下:
com\jdojo\contact\info\Address.class
com\jdojo\contact\info\Phone.class
com\jdojo\contact\validator\Validator.class
com\jdojo\contact\validator\AddressValidator.class
com\jdojo\contact\validator\PhoneValidator.class
2. 模塊化JAR中的模塊
JDK附帶一個(gè)jar工具,以JAR(Java Archive)文件格式打包Java代碼。 JAR格式基于ZIP文件格式。 JDK 9增強(qiáng)了在JAR中打包模塊代碼的jar工具。 當(dāng)JAR包含模塊的編譯代碼時(shí),JAR稱為模塊化JAR。 模塊化JAR在根目錄下包含一個(gè)module-info.class文件。
無論在JDK 9之前使用JAR,現(xiàn)在都可以使用模塊化JAR。 例如,模塊化JAR可以放置在類路徑上,在這種情況下,模塊化JAR中的module-info.class文件將被忽略,因?yàn)閙odule-info在Java中不是有效的類名。
在打包模塊化JAR的同時(shí),可以使用JDK 9中添加的jar工具中可用的各種選項(xiàng),將模塊描述符中的信息例如模塊版本添加到主類中。
Tips
模塊化JAR在各個(gè)方面來看都是一個(gè)JAR,除了它在根路徑下包含的模塊描述符。通常,比較重要的Java應(yīng)用程序由多個(gè)模塊組成。 模塊化JAR可以是一個(gè)模塊,包含編譯的代碼。 需要將應(yīng)用程序的所有模塊打包到單個(gè)JAR中。
繼續(xù)上一節(jié)中的示例,com.jdojo.contact模塊的模塊化JAR內(nèi)容如下。 請注意,JAR在META-INF目錄中始終包含一個(gè)MANIFEST.MF文件。
module-info.class
com/jdojo/contact/info/Address.class
com/jdojo/contact/info/Phone.class
com/jdojo/contact/validator/Validator.class
com/jdojo/contact/validator/AddressValidator.class
com/jdojo/contact/validator/PhoneValidator.class
META-INF/MANIFEST.MF
3. JMOD文件中的模塊
JDK 9引入了一種稱為JMOD的新格式來封裝模塊。 JMOD文件使用.jmod擴(kuò)展名。 JDK模塊被編譯成JMOD格式,放在JDK_HOME \ jmods目錄中。例如,可以找到一個(gè)包含java.base模塊內(nèi)容的java.base.jmod文件。 僅在編譯時(shí)和鏈接時(shí)才支持JMOD文件。 它們在運(yùn)行時(shí)不受支持。
九. 模塊路徑
自JDK開始以來,類路徑機(jī)制查找類型已經(jīng)存在。 類路徑是一系列目錄,JAR文件和ZIP文件。 當(dāng)Java需要在各個(gè)階段(編譯時(shí),運(yùn)行時(shí),工具使用等)中查找類型時(shí),它會(huì)使用類路徑中的條目來查找類型。
Java 9類型作為模塊的一部分存在。 Java需要在不同階段查找模塊,而不是類似于Java 9之前的模塊。Java 9引入了一種新的機(jī)制來查找模塊,它被稱為模塊路徑。
模塊路徑是包含模塊的路徑名稱序列,其中路徑名可以是模塊化JAR,JMOD文件或目錄的路徑。 路徑名由特定于平臺(tái)的路徑分隔符分隔,在UNIX平臺(tái)上為冒號(hào)(😃,Windows平臺(tái)上分號(hào)(;)。
當(dāng)路徑名稱是模塊化的JAR或JMOD文件時(shí),很容易理解。 在這種情況下,如果JAR或JMOD文件中的模塊描述符包含要查找的模塊的模塊定義,則會(huì)找到該模塊。 如果路徑名是目錄,則存在以下兩種情況:
如果類文件存在于根目錄,則該目錄被認(rèn)為具有模塊定義。 根目錄下的類文件將被解釋為模塊描述符。 所有其他文件和子目錄將被解釋為此一個(gè)模塊的一部分。 如果根目錄中存在多個(gè)類文件,則首先找到的文件被解釋為模塊描述符。 經(jīng)過幾次實(shí)驗(yàn),JDK 9似乎以按字母排列的順序拾取了第一類文件。 這種存儲(chǔ)模塊編譯代碼的方式肯定會(huì)讓你頭疼。 因此,如果目錄在根目錄中包含多個(gè)類文件,請避免向模塊路徑添加目錄。
如果根目錄中不存在類文件,則目錄的內(nèi)容將被不同的解釋。 目錄中的每個(gè)模塊化JAR或JMOD文件被認(rèn)為是模塊定義。 每個(gè)子目錄,如果它包含在它的根一個(gè) module-info.class文件,被認(rèn)為具有展開目錄樹格式的模塊定義。 如果一個(gè)子目錄的根目錄不包含一個(gè)module-info.class文件,那么它不會(huì)被解釋為包含一個(gè)模塊定義。 請注意,如果子目錄包含模塊定義,則其名稱不必與模塊名稱相同。 模塊名稱是從module-info.class文件中讀取的。
以下是Windows上的有效模塊路徑:
C:\mods
C:\mods\com.jdojo.contact.jar;C:\mods\com.jdojo.person.jar
C:\lib;C:\mods\com.jdojo.contact.jar;C:\mods\com.jdojo.person.jar
第一個(gè)模塊路徑包含名為C:\mods的目錄的路徑。 第二個(gè)模塊路徑包含兩個(gè)模塊化JAR——com.jdojo.contact.jar和com.jdojo.person.jar的路徑。 第三個(gè)模塊路徑包含三個(gè)元素 —— 目錄C:\lib的路徑,以及兩個(gè)模塊化JAR——com.jdojo.contact.jar和com.jdojo.person.jar的路徑。 在類似UNIX的平臺(tái)上顯示相當(dāng)于這些路徑:
/usr/ksharan/mods
/usr/ksharan/mods/com.jdojo.contact.jar:/usr/ksharan/com.jdojo.person.jar
/usr/ksharan/lib:/usr/ksharan/mods/com.jdojo.contact.jar:/usr/ksharan/mods/com.jdojo.person.jar
避免模??塊路徑問題的最佳方法是不要將分解的目錄用作模塊定義。
有兩個(gè)目錄作為模塊路徑 —— 一個(gè)包含所有應(yīng)用程序模塊化JAR的目錄,另一個(gè)包含用于外部庫的所有模塊化JAR的目錄。例如,可以使用C:\applib 和 C:\extlib作為Windows上的模塊路徑,其中C:\applib目錄包含所有應(yīng)用程序模塊化JAR,C:\extlib目錄包含所有外部庫的模塊化JAR。
JDK 9已經(jīng)更新了所有的工具來使用模塊路徑來查找模塊。這些工具提供了指定模塊路徑的新選項(xiàng)。到JDK 9,已經(jīng)看到以一個(gè)連字符(-)開頭的UNIX樣式選項(xiàng),例如-cp和-classpath。在JDK 9中有如此多的附加選項(xiàng),JDK設(shè)計(jì)人員對于開發(fā)人員來說也用完了有意義的短名稱的選項(xiàng)。因此,JDK 9開始使用GNU樣式選項(xiàng),其中選項(xiàng)以兩個(gè)連續(xù)的連字符開頭,并且單詞由連字符分隔。以下是GNU樣式命令行選項(xiàng)的幾個(gè)示例:
--class-path
--module-path
--module-version
--main-class
--print-module-descriptor
Tips
要打印工具支持的所有標(biāo)準(zhǔn)選項(xiàng)的列表,使用--help或-h選項(xiàng)運(yùn)行該工具,對于所有非標(biāo)準(zhǔn)選項(xiàng),使用-X選項(xiàng)運(yùn)行該工具。 例如,java -h和java -X命令將分別打印java命令的標(biāo)準(zhǔn)和非標(biāo)準(zhǔn)選項(xiàng)列表。
JDK 9中的大多數(shù)工具(如javac,java和jar)都支持兩個(gè)選項(xiàng)來在命令行上指定一個(gè)模塊路徑。 它們是-p和--module-path。 將繼續(xù)支持現(xiàn)有的UNIX樣式選項(xiàng)以實(shí)現(xiàn)向后兼容性。 以下兩個(gè)命令顯示如何使用兩個(gè)選項(xiàng)來指定java工具的模塊路徑:
// Using the GNU-style option
C:\>java --module-path C:\applib;C:\lib other-args-go-here
// Using the UNIX-style option
C:\>java -p C:\applib;C:\extlib other-args-go-here
當(dāng)您使用GNU樣式選項(xiàng)時(shí),可以使用以下兩種形式之一指定該選項(xiàng)的值:
--
--=
上面的命令也可以寫成如下形式:
// Using the GNU-style option
C:\>java --module-path=C:\applib;C:\lib other-args-go-here
當(dāng)使用空格作為名稱值分隔符時(shí),需要至少使用一個(gè)空格。 您使用=作為分隔符時(shí),不得在其周圍包含任何空格。
十. 可觀察模塊
在模塊查找過程中,模塊系統(tǒng)使用不同類型的模塊路徑來定位模塊。 在模塊路徑上與系統(tǒng)模塊一起發(fā)現(xiàn)的一組模塊被稱為可觀察模塊。 可以將可觀察模塊視為模塊系統(tǒng)在特定階段可用的所有模塊的集合,例如編譯時(shí),鏈接時(shí)和運(yùn)行時(shí),或可用于工具。
JDK 9為java命令添加了一個(gè)名為--list-modules的新選項(xiàng)。 該選項(xiàng)可用于打印兩種類型的信息:可觀察模塊的列表和一個(gè)或多個(gè)模塊的描述。 該選項(xiàng)可以以兩種形式使用:
--list-modules
--list-modules ,...
在第一種形式中,該選項(xiàng)沒有跟隨任何模塊名稱。 它打印可觀察模塊的列表。 在第二種形式中,該選項(xiàng)后面是逗號(hào)分隔的模塊名稱列表,用于打印指定模塊的模塊描述符。
以下命令打印可觀察模塊的列表,其中僅包括系統(tǒng)模塊:
c:\Java9Revealed> java --list-modules
java.base@9-ea
java.se.ee@9-ea
java.sql@9-ea
javafx.base@9-ea
javafx.controls@9-ea
jdk.jshell@9-ea
jdk.unsupported@9-ea
...
上面顯示的是輸出部分內(nèi)容。 輸出中的每個(gè)條目都包含兩個(gè)部分—— 一個(gè)模塊名稱和一個(gè)由@符號(hào)分隔的版本字符串。 第一部分是模塊名稱,第二部分是模塊的版本字符串。 例如,在java.base@9-ea中,java.base是模塊名稱,9-ea是版本字符串。 在版本字符串中,數(shù)字9表示JDK 9,ea代表早期訪問。 運(yùn)行命令時(shí),你可能會(huì)得到不同的版本字符串輸出。
現(xiàn)在在C:\Java9Revealed\lib目錄中放置了三個(gè)模塊化JAR。 如果提供此目錄作為java命令的模塊路徑,這些模塊將被包含在可觀察模塊列表中。以下命令顯示了改變指定一個(gè)模塊路徑后,觀察到的模塊列表。 這里,lib目錄是相對路徑,C:\Java9Revealed是當(dāng)前目錄。
C:\Java9Revealed>java --module-path lib --list-modules
claim (file:///C:/Java9Revealed/lib/claim.jar)
policy (file:///C:/Java9Revealed/lib/policy.jar)
java.base@9-ea
java.xml@9-ea
javafx.base@9-ea
jdk.unsupported@9-ea
jdk.zipfs@9-ea
...
注意,對于應(yīng)用程序模塊,--list-modules選項(xiàng)還會(huì)打印它們的位置。 當(dāng)獲得意想不到的結(jié)果,并且不知道正在使用哪些模塊以及哪些位置時(shí),此信息有助于排除故障。
以下命令將com.jdojo.intro模塊指定為--list-modules選項(xiàng)的參數(shù),以打印模塊的描述:
C:\Java9Revealed>java --module-path lib --list-modules claim
module claim (file:///C:/Java9Revealed/lib/claim.jar)
exports com.jdojo.claim
requires java.sql (@9-ea)
requires mandated java.base (@9-ea)
contains pkg3
輸出的第一行包含模塊名稱和包含該模塊的模塊化JAR位置。 第二行表示該模塊導(dǎo)出com.jdojo.claim模塊。 第三行表示該模塊需要java.sql模塊。 第四行表示模塊強(qiáng)制依賴于java.base模塊。 回想一下,除了java.base模塊之外的每個(gè)模塊都取決于java.base模塊。 除了java.base模塊,在每個(gè)模塊的描述中看到需要強(qiáng)制的java.base模塊。 第五行聲明該模塊包含一個(gè)名為pkg3的包,既不導(dǎo)出也不開放。
你還可以使用--list-modules打印系統(tǒng)模塊的描述,例如java.base和java.sql。 以下命令打印出java.sql模塊的描述。
C:\Java9Revealed>java --list-modules java.sql
module java.sql@9-ea
exports java.sql
exports javax.sql
exports javax.transaction.xa
requires transitive java.xml
requires mandated java.base
requires transitive java.logging
uses java.sql.Driver
十一. 總結(jié)
Java中的包已被用作類型的容器。 應(yīng)用程序由放置在類路徑上的幾個(gè)JAR組成。 軟件包作為類型的容器,不強(qiáng)制執(zhí)行任何可訪問性邊界。 類型的可訪問性內(nèi)置在使用修飾符的類型聲明中。 如果包中包含內(nèi)部實(shí)現(xiàn),則無法阻止程序的其他部分訪問內(nèi)部實(shí)現(xiàn)。 類路徑機(jī)制在使用類型時(shí)線性搜索類型。 這導(dǎo)致在部署的JAR中缺少類型時(shí),在運(yùn)行時(shí)接收錯(cuò)誤的另一個(gè)問題 —— 有時(shí)在部署應(yīng)用程序后很長時(shí)間。 這些問題可以分為兩種類型:封裝和配置。
JDK 9引入了模塊系統(tǒng)。 它提供了一種組織Java程序的方法。 它有兩個(gè)主要目標(biāo):強(qiáng)大的封裝和可靠的配置。 使用模塊系統(tǒng),應(yīng)用程序由模塊組成,這些模塊被命名為代碼和數(shù)據(jù)的集合。 模塊通過其聲明來控制模塊的其他模塊可以訪問的部分。 訪問另一個(gè)模塊的部分的模塊必須聲明對第二個(gè)模塊的依賴。 控制訪問和聲明依賴的是達(dá)成強(qiáng)封裝的基礎(chǔ)。 在應(yīng)用程序啟動(dòng)時(shí)解決了一個(gè)模塊的依賴關(guān)系。 在JDK 9中,如果一個(gè)模塊依賴于另一個(gè)模塊,并且運(yùn)行應(yīng)用程序時(shí)第二個(gè)模塊丟失,則在啟動(dòng)時(shí)將會(huì)收到一個(gè)錯(cuò)誤,而不是應(yīng)用程序運(yùn)行后的某個(gè)時(shí)間。 這是一個(gè)可靠的基礎(chǔ)配置。
使用模塊聲明定義模塊。 模塊的源代碼通常存儲(chǔ)在名為module-info.java的文件中。 一個(gè)模塊被編譯成一個(gè)類文件,通常命名為module-info.class。 編譯后的模塊聲明稱為模塊描述符。 模塊聲明不允許指定模塊版本。 但諸如將模塊打包到JAR中的jar工具的可以將模塊版本添加到模塊描述符中。
使用module關(guān)鍵字聲明模塊,后跟模塊名稱。 模塊聲明可以使用五種類型的模塊語句:exports,opens,require,uses和provide。 導(dǎo)出語句將模塊的指定包導(dǎo)出到所有模塊或編譯時(shí)和運(yùn)行時(shí)的命名模塊列表。 開放語句允許對所有模塊的反射訪問指定的包或運(yùn)行時(shí)指定的模塊列表, 其他模塊可以使用反射訪問指定包中的所有類型以及這些類型的所有成員(私有和公共)。 使用語句和提供模塊語句用于配置模塊以發(fā)現(xiàn)服務(wù)實(shí)現(xiàn)并提供特定服務(wù)接口的服務(wù)實(shí)現(xiàn)。
從JDK 9開始,open, module, requires, transitive, exports,opens,to,uses,provides和with都是受限關(guān)鍵字。 只有當(dāng)具體位置出現(xiàn)在模塊聲明中時(shí),它們才具有特殊意義。
模塊的源代碼和編譯代碼被安排在目錄,JAR文件或JMOD文件中。 在目錄和JAR文件中,module-info.class文件位于根目錄。
與類路徑類似,JDK 9引入了模塊路徑。 但是,它們的使用方式有所不同。 類路徑用于搜索類型的定義,而模塊路徑用于查找模塊,而不是模塊中的特定類型。 Java工具(如java和javac)已經(jīng)被更新為使用模塊路徑和類路徑。 您可以使用--module-path或-p選項(xiàng)指定這些工具的模塊路徑。
JDK 9引入了與工具一起使用的GNU風(fēng)格選項(xiàng)。 選項(xiàng)以兩個(gè)破折號(hào)開頭,每個(gè)單詞用短劃線分隔,例如--module-path,--class-path,--list-modules等。如果選項(xiàng)接受一個(gè)值,則該值可以跟隨選項(xiàng)加上空格或=。 以下兩個(gè)選項(xiàng)是一樣的:
--module-path C:\lib
--module-path=C:\lib
模塊系統(tǒng)在某個(gè)階段(編譯時(shí),運(yùn)行時(shí),工具等)中可用的模塊列表被稱為可觀察模塊。 可以使用--list-modules選項(xiàng)與java命令列出運(yùn)行時(shí)可用的可觀察模塊。 還可以使用此選項(xiàng)打印模塊的描述。
總結(jié)
以上是生活随笔為你收集整理的java模块_Java 9 揭秘(2. 模块化系统)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 偷跑还是控分?周杰伦新专辑豆瓣提前开分上
- 下一篇: 华为手机成功的秘密!余承东破釜沉舟 一句