Oracle 在 9月22 日終于發(fā)布了 jdk 9 (難產(chǎn)了好長時間啊%>_<%),java在走過了22個年頭之后迎來了新的版本祖驱,作為一門使用率排在第一的編程語言(截止到現(xiàn)在10月份)乱顾, What 's new ?? 話不多說,讓我們看一下這次發(fā)布帶給我們的一個主要新功能——模塊化。
Jigsaw
Modularity(模塊化)是第一個要介紹的乞巧,代碼名稱為Jigsaw。作為這次發(fā)布的重點功能摊鸡,也是本文詳細(xì)介紹的绽媒。其實在jdk 7的時候Oracle就考慮引入了,但是這中間考慮到穩(wěn)定性免猾、安全性等問題直到 jdk 9 才引入巩那。
首先介紹一下模塊化概念:
簡單說模塊是代碼和數(shù)據(jù)的封裝體琅轧。這里的代碼指的是一些包含類型的Packages 僵缺。Packages是類路徑名稱寸五,模塊就是一個或者多個Package組成的封裝體。
說完了概念锨苏,那么為什么Oracle要拋出這樣一個概念呢石窑?
- 改進(jìn)組件間的依賴管理,引入比Jar粒度更大的Module
- 使得java SE程序更加容易輕量部署
- 改進(jìn)性能和安全性
先說第二點如何理解蚓炬,請看下面這張圖:
運行一個簡單的hello world程序松逊,在JDK8中你需要幾百兆的JRE環(huán)境,有些jar可能并不是你運行程序所必須的肯夏,浪費了很多空間经宏。但是在 JDK 9中,JDK被分成了大約94個modules驯击,在用的時候引入你需要的module就行了烁兰。
對于第一點如何理解呢?其實這點說的是解決 java 中 ““classpath hell”” 或者說“jar hell”的問題徊都』φ澹看到這兩個名詞我也是一臉懵逼啊暇矫?主之!what the hell !
PS: ““classpath hell”” 和“jar hell”在本質(zhì)上相同,只是后者更加關(guān)注由復(fù)雜類加載層級產(chǎn)生的問題李根。
JAR Hell
JAR Hell 問題源自java中的類加載機制槽奕。它主要表現(xiàn)出四個問題:
1、無法表述的依賴關(guān)系(Unexpressed Dependencies)和傳遞依賴(Transitive Dependencies)
首先一個 jar 包是不能通過某種方式去告訴JVM 我依賴哪些其他 jar包的房轿。 需要開發(fā)者自己通過讀文檔粤攒,判斷依賴關(guān)系所森,下載缺失的依賴jar包。而且只有在運行時需要用到依賴jar包時夯接,才會報 NoClassDefFoundError
的錯誤焕济。傳遞依賴指的是應(yīng)用程序需要依賴一些庫,而這些庫又會依賴其他一些庫盔几,像這樣依賴傳遞下去晴弃,使得依賴關(guān)系呈現(xiàn)指數(shù)級復(fù)雜關(guān)系,很容易出錯问欠。
2、遮蔽(Shadowing)
不同的jar包在類路徑上的類有完全相同的名字粒蜈。這種現(xiàn)象比較常見顺献,原因有很多,比如相同庫中有兩個不同版本枯怖,庫被重命名并添加到類路徑中兩次注整。
由于類在第一個包含他們的JAR包中就已經(jīng)被加載,類中的變量會“shadow” 所有其他變量度硝,使得他們不能在用了肿轨。
是不是說的比較晦澀?show me the code!!!
public class Base {
public String name = "Base";
public String getName() { return name; }
}
public class Sub extends Base {
public String name = "Sub";
public String getName() { return name; }
}
下面程序輸出的是“Sub”, 子類shadow 父類的變量蕊程,是不是立馬理解了椒袍?
class Program {
public static void main(String[] args) {
Sub s = new Sub();
System.out.println(s.name); //Output "Sub"
}
}
3、版本沖突(Version Conflicts)
當(dāng)兩個庫依賴的的第三方庫是不同并且不兼容的時候就會出現(xiàn)版本問題藻茂。如果兩個版本同時出現(xiàn)在類路徑中驹暑,會出現(xiàn)不可預(yù)知的結(jié)果。首先因為“shadowing”辨赐,在這兩個版本中存在的類只會加載一個优俘。更糟糕的是,如果一個類存在于一個庫而不在另一個中掀序,這個類被訪問的時候也會被加載進(jìn)來帆焕,調(diào)用庫的代碼會發(fā)現(xiàn)兩個版本混在一起。但是不兼容版本是必須的不恭,缺少一個程序就無法正常運行叶雹,比如拋出NoClassDefFoundErrors
。
4换吧、復(fù)雜的類加載(Complex Class Loading)
默認(rèn)情況下所有程序類都是被相同的類加載器加載的浑娜,但是開發(fā)人員可以自己定義類加載器。類加載通常對開發(fā)者來說是不可見的式散,由容器比如web服務(wù)筋遭、組件系統(tǒng)來完成,但是
all abstractions are leaky。開發(fā)人員在某些情況下得自己顯示地加載類漓滔,這些自定義的類加載器會導(dǎo)致復(fù)雜的不可預(yù)知的行為编饺。(原諒我直接這句話直接翻譯了%>_<%)。
那么Module是如何處理上述四個問題的呢响驴?
首先看一下JDK 9如何定義一個module:
模塊的是通過module-info.java進(jìn)行定義透且,編譯后打包后,就成為一個模塊的實體豁鲤;在模塊的定義文件中秽誊,我們需要指定模塊之間的依賴靠關(guān)系,可以exports
給那些模塊用琳骡,需要使用那些模塊(requires
) 锅论。下面是一個例子:
module com.foo.bar {
requires org.baz.qux;
exports com.foo.bar.alpha;
exports com.foo.bar.beta;
}
1、對于第一個問題楣号,先看下圖最易,左邊是定義module的代碼,app需要zoop和sql炫狱,右邊是依賴關(guān)系藻懒。
JVM會通過了module path 去解析所有的依賴,類似下面的圖
视译,這個過程發(fā)生在啟動階段嬉荆,而不是運行階段。當(dāng)不是所有的modules在這條路徑上被發(fā)現(xiàn)時酷含,解析一個module傳遞依賴的過程就失敗了员寇。這就解決了無法描述和無限傳遞依賴的問題。
2第美、第二問題蝶锋。模塊系統(tǒng)只要發(fā)現(xiàn)兩個模塊輸出相同的包給同一個模塊就會報錯。如下圖所示什往。
3扳缕、版本沖突問題,最直接的解決方式是模塊系統(tǒng)能加載相同模塊的不同版本别威,并且保證這些版本不會互相影響躯舔,保證封裝性和可讀性。但是抱歉省古,版本沖突問題目前沒有辦法解決粥庄。原因摘自官網(wǎng):
It is not necessary to support more than one version of a module within a single configuration.
事實上,現(xiàn)在的Maven和Gradle也沒法理解module的版本信息豺妓。
4惜互、最后是復(fù)雜的類加載問題布讹,先說結(jié)果:Module并沒有徹徹底底解決該問題,但是簡化了問題復(fù)雜性训堆。
Oracle引入了層(Layer)的概念描验。(一個module夠煩的,還拋出個layer概念坑鱼,發(fā)現(xiàn)很多互聯(lián)網(wǎng)公司都喜歡拋個新概念出來強行裝逼氨炝鳌!B沉ぁ:艄伞!画恰!TMD)彭谁。layer控制模塊和類加載器之間的關(guān)系。使得在同一個layer里的不同模塊都使用相同的類加載器阐枣。換句話說類加載器和模塊之間是1:n的關(guān)系马靠。同時Layer還有堆疊的特點奄抽,如下圖所示蔼两。
一個新layer可以在boot layer上構(gòu)建,另外的layer也能在這個layer上構(gòu)建逞度。在解析某個layer里的module時候可以讀取里面的modules或者在它下面的layer里的module额划。
說白了,我的個人理解档泽,layer就是讓一個類加載器去專門負(fù)責(zé)某個module下的所有類加載俊戳,有什么好處,我也不知道肮菽洹抑胎?各位能不能寫個列子出來?渐北?阿逃?
講了模塊化的好處,似乎還是比較抽象赃蛛,下面兩張圖比較直觀展示模塊化之后的好處:
下是 jdk 8中包的依賴關(guān)系
是不是很復(fù)雜呢恃锉?
JAVA 9 引入模塊后,將所有的類組織成模塊形式呕臂,模塊之間有著優(yōu)美的依賴關(guān)系
再看JDK9 簡化后的關(guān)系圖
最后的問題
以下是Jigsaw官網(wǎng)的原話:
Make it easier for developers to construct and maintain libraries and large applications, for both the Java SE and EE Platforms.
是不是讓你聯(lián)想到了Maven 和 Gradle? 他們之間到底是什么關(guān)系捌仆小?歧蒋?
Google 一下在StackOverFlow上找到了答案土砂。
在Jigsaw之后州既,public
關(guān)鍵字不再意味著任意的可訪問性。訪問的范圍局限在JAR瘟芝,想要訪問 JAR包外的其他類易桃,必須export
。 任何模塊之間的交互必須通過module-info文件來定義锌俱。
舉個列子晤郑,生成的WAR包可能并沒改變源代碼,但是在其中的所有JAR必須定義module-info贸宏。
Maven 有兩個主要的特征造寝。他負(fù)責(zé)項目的依賴管理和構(gòu)建。依賴管理的意思是Maven可以決定庫中的版本并從倉庫中下載下來吭练。構(gòu)建的意思是Maven可以編譯代碼并且打包成產(chǎn)品诫龙。
Module是系統(tǒng)內(nèi)置用于表述組件之間的關(guān)系,對于版本的管理還是處于最原始的狀體鲫咽。它管理一種強制的依賴關(guān)系签赃。
總結(jié)一下:Maven還是組要負(fù)責(zé)構(gòu)建,包括開發(fā)過程中的各種任務(wù)分尸,初始化锦聊,測試。但是要利用Jigsaw, Maven必須知道如何通過Jigsaw模塊編譯打包箩绍。Modules 對于像Maven這樣的構(gòu)建工具(build tools)來說扮演的是輔助補充的角色孔庭。因為這些構(gòu)建工具在構(gòu)建時建立的依賴關(guān)系圖譜和他們版本可以根據(jù)Module來創(chuàng)建,Module強制確定了module和artifacts之間的依賴關(guān)系材蛛,而Maven對于依賴并非是強制的圆到。
本文主要參考了一下兩個國外文獻(xiàn),一篇CSDN上的博客卑吭,以及oracle官方的視頻講解芽淡,youtube上有的(別問我怎么翻墻)
https://dzone.com/articles/what-is-jar-hell
https://blog.codefx.org/java/dev/will-there-be-module-hell/#Shadowing
http://www.cnblogs.com/xiongxx/p/6734416.html
感謝閱讀,這是我第一次在簡書上寫文章豆赏,以后會多寫一些挣菲,有問題歡迎討論,最后祝大家節(jié)日快樂:诱馈己单!