初探Java 9 的的模塊化

Java 9中最重要的功能,毫無疑問就是模塊化(Module),它將自己長期依賴JRE的結構,轉變成以Module爲基礎的組件,當然這在使用Java 9 開發也和以前有着很大的不同。

Java8或更加早期的系統的問題

  1. Jar文件,像rt.jar等jar文件太大的以至於不能使用在小設備和應用中。
  2. 因爲JDK是太大的,我們的應用或設備不能支持更好的平臺.
  3. 由於修飾符是public的緣故,每個人都可以通過此來進行訪問,所以在當前Java系統的封閉性不是很強。
  4. 由於JDK,Jre過於龐大,以至於很難進行測試和維護應用。
  5. 由於public的關係,Java比較開放。不可避免的能訪問象sun., .internal.*等的一些內部不重要的APIs。

Java9模塊系統的特性

  1. 在Java SE 9中分離了JDK, JRE,jar等爲更小的模。因此我們可以方便的使用任何我們想要的模塊。因此縮減Java應用程序到小設備是非常容易的。
  2. 更加容易的測試和維護。
  3. 支持更好的平臺。
  4. public不再僅僅是public。現在已經支持非常強的封閉性(不用擔心,後邊我們會用幾個例子來解釋)。
  5. 我們不能再訪問內部非關鍵性APIs了。
  6. 模塊可以非常安全地幫助我們掩藏那些我們不想暴露的內部細節,我們可以得到更好的Security。
  7. 應用會變的非常小,因爲我們可以只使用我們要用的模塊。
  8. 組件間的鬆耦合變得非常容易。
  9. 更容易支持唯一責任原則(SRP)。

目錄結構

以前的jre中有一個很大的架包,jdk8 中rt.jar有62M,即便運行一個最簡單的HelloWorld,都必須帶上它。

image

jdk9 的目錄
image

我們發現,jdk9 中沒有Jre 文件,也沒有rt.jar等這種很大的架包,但是它有了一個新的文件jmods,模塊都是放在jmods文件夾中。

目前共有98個模塊。

Module的相關屬性

在主目錄的/main/java/下新建module-info.java文件,可以管理這個項目的module。

module M {

}

模塊命名

又稱模塊描述文件
模塊命名需要保證單一,可以使用反向域名模式,如com.wuwii.xxx.xxx,這個模塊會導出包com.wuwii

在JDK 9中, open, module, requires, transitive, exports, opens, to, uses, provides 和 with是受限關鍵字。只有當具體位置出現在模塊聲明中時,它們才具有特殊意義。 可以將它們用作程序中其他地方的標識符。

例如:可以在程序中聲明一個module變量。

訪問權限

模塊之間的關係被稱作readability(可讀性),代表一個模塊是否可以找到這個模塊文件,並且讀入系統中(注意:並非代表可以訪問其中的類型)。在實際的代碼,一個類型對於另外一個類型的調用,我們稱之爲可訪問性(Accessible),這意味着可以使用這個類型; 可訪問性的前提是可讀性,換句話說,現有模塊可讀,然後再進一步檢測可訪問性(安全)。
導出語句將模塊的指定包導出到所有模塊或編譯時和運行時的命名模塊列表。 它的兩種形式如下:

exports <package>;
exports <package> to <module1>, <module2>...;

以下是使用了導出語句的模塊示例:

module java.xml.ws {
……
    exports com.oracle.webservices.internal.api.databinding to
        jdk.xml.ws;
    exports com.sun.xml.internal.ws.addressing to
        jdk.xml.ws,
        java.xml.bind;
……
}

開放語句允許對所有模塊的反射訪問指定的包或運行時指定的模塊列表。 其他模塊可以使用反射訪問指定包中的所有類型以及這些類型的所有成員(私有和公共)。 開放語句採用以下形式:

opens <package>;
opens <package> to <module1>, <module2>...;

Tips
對比導出和打開語句。 導出語句允許僅在編譯時和運行時訪問指定包的公共API,而打開語句允許在運行時使用反射訪問指定包中的所有類型的公共和私有成員。

module N {
    exports M;
    opens M;
}

閱讀有關模塊的時候會遇到三個短語:

  • 模塊M導出包P
  • 模塊M打開包Q
  • 模塊M包含包R

前兩個短語對應於模塊中導出語句和開放語句。 第三個短語意味着該模塊包含的包R既不導出也不開放。 在模塊系統的早期設計中,第三種情況被稱爲“模塊M隱藏包R”。

聲明依賴關係

需要(require)語句聲明當前模塊與另一個模塊的依賴關係。 一個名爲M的模塊中的“需要N”語句表示模塊M取決於(或讀取)模塊N。語句有以下形式:

requires <module>;
requires transitive <module>;
requires static <module>;
requires transitive static <module>;
  • require語句中的靜態修飾符表示在編譯時的依賴是強制的,但在運行時是可選的。
  • requires static N語句意味着模塊M取決於模塊N,模塊N必須在編譯時出現才能編譯模塊M,而在運行時存在模塊N是可選的。
  • require語句中的transitive修飾符會導致依賴於當前模塊的其他模塊具有隱式依賴性。
  • 假設有三個模塊P,Q和R,假設模塊Q包含requires transitive R語句,如果如果模塊P包含包含requires Q語句,這意味着模塊P隱含地取決於模塊R。

配置服務

Java允許使用服務提供者和服務使用者分離的服務提供者機制。 JDK 9允許使用語句(uses statement)和提供語句(provides statement)實現其服務。

使用語句可以指定服務接口的名字,當前模塊就會發現它,使用 java.util.ServiceLoader類進行加載。格式如下:

uses <service-interface>;

使用語句的實例如下:

module M {
    uses com.jdojo.prime.PrimeChecker;
}

com.jdojo.PrimeChecker是一個服務接口,其實現類將由其他模塊提供。 模塊M將使用java.util.ServiceLoader類來發現和加載此接口的實現。

提供語句指定服務接口的一個或多個服務提供程序實現類。 它採取以下形式:

provides <service-interface>
    with <service-impl-class1>, <service-impl-class2>...;

相同的模塊可以提供服務實現,可以發現和加載服務。 模塊還可以發現和加載一種服務,併爲另一種服務提供實現。 以下是例子:

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;
}

總結

需要注意的是,不只是jdk中內置的98種模塊,引用maven的第三方架包,也需要module,
如用的比較多的日誌

    /**
     * logger
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(LearnSoap.class);

配置
需要在module-info.java配置:

requires slf4j.api;

Java中的包已被用作類型的容器。 應用程序由放置在類路徑上的幾個JAR組成。 軟件包作爲類型的容器,不強制執行任何可訪問性邊界。 類型的可訪問性內置在使用修飾符的類型聲明中。 如果包中包含內部實現,則無法阻止程序的其他部分訪問內部實現。 類路徑機制在使用類型時線性搜索類型。 這導致在部署的JAR中缺少類型時,在運行時接收錯誤的另一個問題 —— 有時在部署應用程序後很長時間。 這些問題可以分爲兩種類型:封裝和配置。

JDK 9引入了模塊系統。 它提供了一種組織Java程序的方法。 它有兩個主要目標:強大的封裝和可靠的配置。 使用模塊系統,應用程序由模塊組成,這些模塊被命名爲代碼和數據的集合。 模塊通過其聲明來控制模塊的其他模塊可以訪問的部分。 訪問另一個模塊的部分的模塊必須聲明對第二個模塊的依賴。 控制訪問和聲明依賴的是達成強封裝的基礎。 在應用程序啓動時解決了一個模塊的依賴關係。 在JDK 9中,如果一個模塊依賴於另一個模塊,並且運行應用程序時第二個模塊丟失,則在啓動時將會收到一個錯誤,而不是應用程序運行後的某個時間。 這是一個可靠的基礎配置。

使用模塊聲明定義模塊。 模塊的源代碼通常存儲在名爲module-info.java的文件中。 一個模塊被編譯成一個類文件,通常命名爲module-info.class。 編譯後的模塊聲明稱爲模塊描述符。 模塊聲明不允許指定模塊版本。 但諸如將模塊打包到JAR中的jar工具的可以將模塊版本添加到模塊描述符中。

使用module關鍵字聲明模塊,後跟模塊名稱。 模塊聲明可以使用五種類型的模塊語句:exports,opens,require,uses和provide。 導出語句將模塊的指定包導出到所有模塊或編譯時和運行時的命名模塊列表。 開放語句允許對所有模塊的反射訪問指定的包或運行時指定的模塊列表, 其他模塊可以使用反射訪問指定包中的所有類型以及這些類型的所有成員(私有和公共)。 使用語句和提供模塊語句用於配置模塊以發現服務實現並提供特定服務接口的服務實現。

從JDK 9開始,open, module, requires, transitive, exports,opens,to,uses,provides和with都是受限關鍵字。 只有當具體位置出現在模塊聲明中時,它們才具有特殊意義。

參考文章

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章