模組這件事其實不是甚麼新鮮的技術了,早在JavaScript, Python等等語言就有了,而Java也終於在JDK9.0釋出了Modularity。在模組出來以前,Java的封裝一直是很頭疼的問題,基本上都是包裝成jar檔,裡面有package,package裏頭的修飾詞有public、default、protected、private。
Modifier | Class | Package | Subclass | World |
public | Y | Y | Y | Y |
protected | Y | Y | Y | N |
default (no modifier) | Y | Y | N | N |
private | Y | N | N | N |
這樣的封裝有一個很大的問題。如果我有一個類別A,它被放置com.my.package,而我又有一個類別B,它被放置在com.my.package.inner,A可以被其他package存取,但B是非公開的,同時A也要可以存取B的內容。這時會發現B的修飾詞不知道該放甚麼了,因為唯一可以被自己以外的package讀到的修飾詞就只有public,而本來用意只是想給com.my.package使用的B,現在變成所有人都可以讀取了,這並不是我們一開始想要的結果。
Java 9 的模組化給了我們這個彈性。透過模組,我們可以更準確的指定哪些package是可以被”看見”的,又哪些模組、類別是要被依賴使用的。這一切的魔法都在module-info.java這個敘述檔裡去聲明。以下針對這個檔裡面幾個新的關鍵字做整理。
module module.name : 宣告一個模組名稱 (為避免撞名,建議用reversed domain name as namespace)
exports pkg.name : 輸出package (這個模組裡,哪些package是可以被”看見”的)
export pkg.name to module.name : 只輸出該package給針對的模組使用 (只有指定的module可以”看見”該package)
以上只是針對一個模組聲明哪些package可以被使用,如果要使用這個模組,要在module-info裡面加上下:
requires module.name : 需要該module
requires transitive module.name : 所有依賴當下module自動也依賴該module
假設我們兩個模組名稱分別是one和two。one裡面有com.my.package和com.my.package.inner以及上述的A、B類別。一個最簡單的module-info.java如下:
module one{ exports com.my.package; }
two裡面我們有com.fun.package,package裡面有隻C類別。則:
module two{ exports com.fun.package; requires transitive one; }
這樣子,我們就可以在module two的C類別裡面,正常的引用module one裡的A類別,但同時我們是看不到類別B的。這看似符合了我們一開始的封裝需求。注意transitive這個關鍵字,假設有另一個module three引用了module two,如果有transitive,那它會自動requires module one。反之,如果沒有的話,那module three會看不到所有module one裡面的東西。在封裝層面上,這樣的設計賦予了更多的彈性。
還有一種封裝狀況是介面實作。在依賴裡面我們只提供介面,而真正的實作沒必要顯露出來給使用方,這時候我們可以用以下關鍵字:
provides class.name with class.name.impl : 提供介面和實作類別 (service provider)
uses class.name : 使用類別 (通常為抽象介面) (service consumer)
最後一個封裝問題是Java reflection,儘管修飾詞是private,透過reflection我們還是可以存取到private的內容。但在Java9的模組管理下,reflection access就不再被允許,但如果真的有需要,還是可以透過以下關鍵字:
open module.name : 允許對該模組用reflection access
opens pkg.name : 允許對該package用reflection access
opens pkg.name to module.name 只允許對該package在針對的module下用reflection access
目前幾個常用的關鍵字如上,Java 9的模組化主要強調的是更好的封裝(strong encapsulation),它並沒有要嘗試解決一些library或版本依賴性的問題,那些問題或許在以往的gradle或maven已經有許多不錯的方法了。最後,要從非模組化轉移到模組化無非是一項工程,尤其是如果又用到一些沒有模組化的library又該怎麼辦,或是無法相容的library…等等的問號,只能說要等到碰到才會依依去找解法吧ㄎㄎ。
參考:
- https://www.oracle.com/corporate/features/understanding-java-9-modules.html
- http://files.zeroturnaround.com/pdf/RebelLabs-Java-9-modules-cheat-sheet.pdf
- Java 9 Module Services
- https://www.amazon.com/Java-Modularity-Developing-Maintainable-Applications/dp/1491954167