掌握Java9模塊化系統-進階部分

在上篇基礎部分介紹了模塊化系統的基本使用和使用命令編譯和運行咱們的模塊。java

再來看下模塊相關的關鍵字:windows


目前爲止咱們熟悉的就是exports/reuires/module,其使用也是很簡單的。下面將逐步介紹其餘關鍵字的使用場景。bash

系統模塊

Java9之後JDK自己也是模塊化的,若是咱們使用模塊化機制那麼引用系統的包和對象,也須要在模塊定義中引入這些包的依賴模塊。模塊化


其中java.base較爲特殊。這個模塊由編譯器自動添加到咱們的自定義模塊依賴聲明中。咱們看看JDK源碼中是如何定義該模塊的:post

C:/Program Files/Java/jdk-10.0.1/lib/src.zip!/java.base/module-info.java
ui

該模塊導出了java.io/java.nio/java.lang/java.net/java.math/java.time/java.util等核心基礎包,這樣咱們的代碼中就不須要顯示去導入這些包所在模塊的依賴聲明瞭。spa

遺留代碼和匿名模塊

因爲Java9模塊機制能夠說是breaking change,因此他須要解決舊代碼的兼容。若是咱們的代碼中不聲明module-info.java,那麼咱們的代碼在一個稱爲「unnamed module」模塊中。.net

「unnamed module」有兩個要的特性。首先,該模塊裏的全部包默認都會自動進行exports聲明,所以其餘外部模塊對他的全部對象可讀。其次,匿名模塊能訪問全部其餘模塊(固然是有權限的狀況下)。code

當咱們代碼不顯示聲明模塊定義的時候,編譯代碼仍然使用的classpath機制而不是上文提到的mudule path機制。正是這兩個缺省機制才能保證咱們的項目從Java8及如下能夠直接切換到Java10。而不須要對新的模塊機制進行適配。cdn

因此JDK的開發者在這方面仍是付出了不少努力的,在此對他們表示深感欽佩。

導出到特定模塊

若是你的模塊中的某些對象只容許特定的模塊使用,則可使用to關鍵字:

exports packageName to moduleName1,moduleName1,...;複製代碼

to後面能夠添加多個模塊,修改pojo模塊定義:

module pojo {
    exports pojo to order;
}複製代碼

咱們再編譯goods模塊就會報錯:

D:\code\mods>javac --module-path target -d target\goods goods\src\*.java goods\src\goods\*.java 
goods\src\goods\GoodsService.java:5: 錯誤: 程序包 pojo 不可見 import pojo.Goods; ^ (程序包 pojo 已在模塊 pojo 中聲明, 但該模塊未將它導出到模塊 goods) 1 個錯誤  

可傳遞性transitive

上文的三個模塊就是可傳遞性的栗子:goods->order->pojo,同時goods也依賴pojo。來看看目前goods模塊的定義:

module goods{
    requires order;
    requires pojo;
}複製代碼

顯然requires pojo;和order模塊中重複了,咱們只需修改order的模塊聲明:

module order {
    exports order;
    requires transitive pojo;
}
複製代碼

module goods{
    requires order;
}複製代碼

模塊的開放

open module

一個模塊裏的包只有在模塊中聲明exports,別的模塊才能進行訪問。固然,咱們要明白這裏的訪問指的的是靜態編譯期間的訪問。其定義以下:

open module moduleName{
    //模塊定義
}複製代碼

下面舉個栗子,咱們在pojo模塊新增一個包和類other.Other。這是一個空類啥都沒有:

package other;

public class Other {
}而後複製代碼

修改pojo模塊定義,加上open關鍵字:

open module pojo {
    exports pojo;
}複製代碼

咱們在goods模塊中使用:

GoodsService.java

package goods;


import order.OrderService;
import pojo.Goods;

public class GoodsService {

    private OrderService orderService = new OrderService();

    Goods queryGoodsInfoByName(String goodsName) {
        Goods goods = new Goods();
        goods.setGoodsName(goodsName);
        goods.setOrderList(orderService.queryOrdersByGoodsName(goodsName));
        return goods;
    }

    public static void main(String[] args) throws Exception{
        String module = "test",exports = "test",with="with";
        System.out.println(new GoodsService().queryGoodsInfoByName("test"));

        //test open module
        Object other = Class.forName("other.Other");
        System.out.println(other);
    }
}複製代碼

上邊的栗子使用反射來建立Other的實例,運行程序發現確實能夠。然而咱們並無在pojo模塊定義:

exports other;

可見open關鍵字使得pojo的包和對象在運行期開放給了其餘模塊,然而在編譯仍是不開放的。修改示例代碼:

Other other = new Other();複製代碼

編譯是不能經過的。

opens Statement

咱們想更細粒度的開放包的訪問性,可使用opens把特定的包開放給外部模塊,提供運行時訪問權限。其使用以下:

opens packageName [to moduleName];複製代碼

注意兩點:一、opens聲明不能用在open的module中,由於open module定義的模塊全部包都有了open屬性。二、opens只是開放了運行時訪問權限,而不是編譯期。三、opens能夠指定具體的模塊,只有指定的模塊才能在運行時訪問該包。

咱們能夠看到系統模塊也是用這個機制,好比java.desktp:

opens com.sun.java.swing.plaf.windows to jdk.jconsole;
opens javax.swing.plaf.basic to jdk.jconsole;複製代碼

requires static

使用static那麼導入的模塊只是在編譯可用,在運行期是不可見的。使用該關鍵字則必須明確,引入的模塊在運行期是可選的,不然用到就會報錯。好比咱們改變goods模塊的定義:

module goods{
    requires static order;
}複製代碼

而後編譯,一切OK。可是在運行的時候就會報錯:

Exception in thread "main" java.lang.NoClassDefFoundError: order/OrderService
	at goods/goods.GoodsService.<init>(GoodsService.java:9)
	at goods/goods.GoodsService.main(GoodsService.java:20)
Caused by: java.lang.ClassNotFoundException: order.OrderService
	at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:582)
	at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:190)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:499)
	... 2 more

Process finished with exit code 1
複製代碼

總結

模塊的基本使用到此已經差很少了,咱們學會了:

  • 如何定義模塊
  • 系統模塊和匿名模塊
  • 模塊依賴的可傳遞性
  • 如何將模塊導出到目標模塊
  • 運行時開放模塊
  • 運行時開放指定模塊下的包
  • 靜態模塊依賴
相關文章
相關標籤/搜索