另一个拼图观点
在過(guò)去的幾周中,圍繞即將發(fā)布的Java 9及其最著名的功能:Java平臺(tái)模塊系統(tǒng)JPMS展開(kāi)了激烈的辯論。
–以項(xiàng)目拼圖Jigsaw的名稱(chēng)而聞名。 模塊系統(tǒng)以正式規(guī)范過(guò)程的形式引入Java生態(tài)系統(tǒng)
– JSR –需要由專(zhuān)家組以最終形式批準(zhǔn)。 在該專(zhuān)家組的其他成員中,紅帽和IBM的代表現(xiàn)已投票反對(duì)在他們認(rèn)為尚未準(zhǔn)備好生產(chǎn)的第一輪投票中拒絕Java的模塊系統(tǒng)。
到底有什么毛病?
即使在今天,Java開(kāi)發(fā)人員也對(duì)模塊化非常熟悉。 像Maven這樣的構(gòu)建系統(tǒng)將代碼組織為針對(duì)一組聲明的依賴(lài)項(xiàng)進(jìn)行編譯的模塊。 僅在運(yùn)行時(shí),這些模塊才放到這些編譯時(shí)模塊邊界消失的類(lèi)路徑上。 使用Jigsaw,模塊路徑可以作為此類(lèi)路徑的替代方式,JVM在運(yùn)行時(shí)為其保留了此類(lèi)編譯時(shí)邊界。 通過(guò)不使用此模塊路徑,應(yīng)用程序應(yīng)該像以前一樣運(yùn)行。 但是,這依賴(lài)于JVM內(nèi)部的API的應(yīng)用程序除外。 即使將類(lèi)路徑專(zhuān)門(mén)用于無(wú)法再訪問(wèn)內(nèi)部Java API的情況下,Java標(biāo)準(zhǔn)庫(kù)也始終作為模塊的集合加載。
兼容性上的后一個(gè)限制引起了庫(kù)和最終用戶應(yīng)用程序維護(hù)者的一些關(guān)注。 在這種情況下,最近的反對(duì)意見(jiàn)與這些關(guān)注關(guān)系不大可能會(huì)令人感到驚訝。 雖然提到兼容性方面的問(wèn)題,但Red Hat和IBM都主要認(rèn)為JPMS需要進(jìn)一步擴(kuò)展,以允許與現(xiàn)有模塊系統(tǒng)(如JBoss模塊和OSGi)更好地集成。
還有什么問(wèn)題需要解決?
通過(guò)jar hell ,開(kāi)發(fā)人員通常描述Java應(yīng)用程序需要兩個(gè)不同版本的庫(kù)來(lái)滿足不同的傳遞依賴(lài)關(guān)系的情況。 使用類(lèi)路徑,這是不可能的,因?yàn)橐粋€(gè)版本的庫(kù)會(huì)遮蓋第二個(gè)副本。 如果是第一次加載給定名稱(chēng)的類(lèi),則系統(tǒng)類(lèi)加載器將按其命令行順序掃描jar文件,并加載其發(fā)現(xiàn)的第一個(gè)類(lèi)文件。 在最壞的情況下,如果帶陰影的jar文件包含一些鏈接到帶陰影的jar類(lèi)的專(zhuān)有類(lèi),則可能會(huì)導(dǎo)致科學(xué)怪人功能。 但是更典型的是,一旦觸發(fā)了依賴(lài)于特定版本的功能,就會(huì)導(dǎo)致運(yùn)行時(shí)失敗。
使用OSGi和JBoss模塊,可以部分解決此問(wèn)題。 后一個(gè)模塊系統(tǒng)允許每個(gè)庫(kù)自己的類(lèi)加載器加載一個(gè)庫(kù),從而避免了負(fù)責(zé)類(lèi)路徑的系統(tǒng)類(lèi)加載器。 使用這種方法,可以通過(guò)隔離在單獨(dú)的類(lèi)加載器中來(lái)共存同一類(lèi)的多個(gè)版本。 這樣做,例如,兩個(gè)庫(kù)都可能都依賴(lài)于它們通常中斷的Guava API的特定版本。 使用類(lèi)加載器隔離,在加載依賴(lài)類(lèi)時(shí),任何庫(kù)都會(huì)將調(diào)用委派給其所需的版本。
使用模塊路徑時(shí),JPMS(當(dāng)前)不應(yīng)用此類(lèi)類(lèi)加載器隔離。 這意味著Java 9無(wú)法解決jar hell的問(wèn)題。與使用類(lèi)路徑相反,JVM確實(shí)會(huì)檢測(cè)到所描述的版本沖突并在啟動(dòng)時(shí)使應(yīng)用程序失敗,而不是推測(cè)意外的兼容性。 為了強(qiáng)制執(zhí)行此約束,每個(gè)Java包名稱(chēng)現(xiàn)在都專(zhuān)有于特定模塊或類(lèi)路徑。 因此,兩個(gè)模塊不可能共享一個(gè)包。 如果不打算公開(kāi)私有軟件包,則此限制也成立,這是Jigsaw評(píng)論家認(rèn)為當(dāng)前模塊設(shè)計(jì)的另一個(gè)缺陷。
錯(cuò)過(guò)了逃脫地獄的機(jī)會(huì)嗎?
為了使類(lèi)加載器隔離有效,必須使同一模塊的版本永不交互。 而且,盡管兩個(gè)這樣的版本當(dāng)然永遠(yuǎn)不會(huì)直接交互,但是不幸的是,兩個(gè)版本是不同模塊的公共API的一部分比通常情況更常見(jiàn)。 例如,如果兩個(gè)庫(kù)返回番石榴的實(shí)例Function類(lèi)型,每個(gè)模塊的番石榴版本之間的版本沖突不能再使用的類(lèi)加載器隔離,即使解決了Function型沒(méi)有這些版本之間切換。 在運(yùn)行時(shí),任何已加載的類(lèi)都被描述為其名稱(chēng)和類(lèi)加載器的元組,但是由于兩個(gè)類(lèi)加載器現(xiàn)在提供了Function類(lèi)型,應(yīng)該解決哪個(gè)類(lèi)型?
實(shí)際上,該描述的問(wèn)題不能由模塊系統(tǒng)解決。 相反,模塊系統(tǒng)可以發(fā)現(xiàn)此沖突,并告知用戶需要明確的解決方案。 這是通過(guò)JPMS的當(dāng)前實(shí)現(xiàn)以及OSGi和JBoss模塊實(shí)現(xiàn)的。 歸根結(jié)底,只有通過(guò)以兼容方式發(fā)展API才能避免版本沖突。
拼圖游戲太簡(jiǎn)單了嗎?
盡管類(lèi)裝入器隔離模塊系統(tǒng)仍然存在局限性,但當(dāng)前反對(duì)Jigsaw的論點(diǎn)主要圍繞此問(wèn)題。 此外,拒絕Jigsaw的專(zhuān)家組成員指出,缺少對(duì)循環(huán)模塊依賴(lài)關(guān)系的支持(“模塊A依賴(lài)于B依賴(lài)于C依賴(lài)于A依賴(lài)”)以及在創(chuàng)建模塊圖后無(wú)法更改模塊圖。
從技術(shù)角度來(lái)看,當(dāng)然可以添加這些功能。 實(shí)際上,Java 9已經(jīng)附帶了模塊構(gòu)建器API,該模塊允許使用排他類(lèi)加載器加載模塊。 選擇為模塊路徑保留單個(gè)類(lèi)加載器沒(méi)有技術(shù)限制。 相反,該決定被Oracle認(rèn)為是JVM的負(fù)責(zé)任選擇。 在深入探討這些論點(diǎn)之前,我想聲明我完全同意公司的推理。
類(lèi)加載器隔離有什么問(wèn)題?
如前所述,即使使用類(lèi)加載器隔離,也常常無(wú)法避免手動(dòng)版本管理。 此外,依賴(lài)具有不兼容版本(例如Guava)的通用API的庫(kù)作者的確越來(lái)越掩蓋了這種依賴(lài)性。 著色時(shí),將庫(kù)的代碼復(fù)制到單獨(dú)的名稱(chēng)空間中,從而使應(yīng)用程序可以使用不同的名稱(chēng)而不是通過(guò)不同的類(lèi)加載器來(lái)引用“其版本”。 當(dāng)然,這種方法有其自身的缺陷,特別是當(dāng)陰影依賴(lài)項(xiàng)使用JNI時(shí)。 另一方面,這種方法克服了在使用具有沖突的共享依賴(lài)項(xiàng)的庫(kù)時(shí),剛剛提到的類(lèi)加載器隔離的缺點(diǎn)。 同樣,通過(guò)隱藏公共依賴(lài)關(guān)系,庫(kù)作者可以獨(dú)立于部署方法而使用戶免于潛在的沖突。
允許循環(huán)依賴(lài)既不會(huì)帶來(lái)很大的技術(shù)挑戰(zhàn)。 但是,循環(huán)依賴(lài)關(guān)系很少見(jiàn),像Maven這樣的許多構(gòu)建系統(tǒng)都不支持它們。 通常,可以通過(guò)將至少一個(gè)模塊分成實(shí)現(xiàn)和API來(lái)將循環(huán)依賴(lài)關(guān)系重構(gòu)為非循環(huán)依賴(lài)關(guān)系。 在這種情況下,如果某個(gè)功能似乎很少引起人們的普遍關(guān)注,那么我認(rèn)為角落情況并不能證明其附加功能是合理的,尤其是當(dāng)類(lèi)路徑仍充當(dāng)備用功能時(shí)。 而且,如果最終決定是錯(cuò)誤的,則可以在以后的版本中始終啟用循環(huán)依賴(lài)關(guān)系。 但是,取消此功能將是不可能的。
最后,動(dòng)態(tài)模塊提供的功能可能對(duì)多個(gè)應(yīng)用程序有用。 根據(jù)我上一個(gè)項(xiàng)目的經(jīng)驗(yàn),當(dāng)您需要?jiǎng)討B(tài)地重新部署具有生命周期的模塊時(shí),OSGi是一個(gè)很好的選擇。 也就是說(shuō),大多數(shù)應(yīng)用程序都是靜態(tài)的,沒(méi)有充分的理由使用它。 但是,通過(guò)添加對(duì)動(dòng)態(tài)模塊圖的支持,此功能的復(fù)雜性將轉(zhuǎn)化為JPMS。 因此,我認(rèn)為暫時(shí)不使用此功能,等到更好地理解其用途是正確的決定。 自然,可訪問(wèn)的模塊系統(tǒng)會(huì)提高采用率。
兼容性至上
這種不兼容性是否意味著OSGi和JBoss模塊的終結(jié)? 當(dāng)然不是。 恰恰相反,標(biāo)準(zhǔn)化模塊描述符的引入為現(xiàn)有模塊系統(tǒng)提供了機(jī)會(huì)。 使用OSGi時(shí),缺少描述包的清單標(biāo)頭是主要的痛點(diǎn)之一,因?yàn)榇罅繋?kù)沒(méi)有考慮專(zhuān)有模塊描述符。 通過(guò)引入標(biāo)準(zhǔn)化的模塊描述符,現(xiàn)有的模塊系統(tǒng)可以通過(guò)使用后一個(gè)描述符作為模塊描述的輔助來(lái)源來(lái)減輕此限制。
我毫不懷疑紅帽和IBM出于最佳意圖拒絕了JSR。 同時(shí),我不能同意對(duì)模塊系統(tǒng)缺乏覆蓋面的批評(píng)。 在我看來(lái),現(xiàn)有的變更對(duì)于Java生態(tài)系統(tǒng)的采用而言是充滿挑戰(zhàn)的,尤其是最后一刻引入類(lèi)裝入器隔離具有帶來(lái)意外意外的潛力。 有鑒于此,我發(fā)現(xiàn)針對(duì)拼圖的當(dāng)前狀態(tài)提出的論據(jù)不一致,因?yàn)樗u(píng)了向模塊過(guò)渡的復(fù)雜性,但也要求對(duì)其進(jìn)行擴(kuò)展。
沒(méi)有完善的模塊系統(tǒng)
我個(gè)人認(rèn)為,目前的JPMS提案面臨兩個(gè)重大挑戰(zhàn)。 不幸的是,由于最近的討論,它們成為了背景。
自動(dòng)模塊
如果沒(méi)有模塊描述符,則模塊化代碼只能以所謂的自動(dòng)模塊的形式引用非模塊化jar文件。 自動(dòng)模塊沒(méi)有任何限制,并由其jar文件命名。 這對(duì)于最終用戶應(yīng)用程序的開(kāi)發(fā)人員非常有用,這些用戶永遠(yuǎn)不會(huì)發(fā)布其代碼以供其他應(yīng)用程序使用。 但是,庫(kù)開(kāi)發(fā)人員確實(shí)缺少一個(gè)穩(wěn)定的模塊名稱(chēng)來(lái)引用其依賴(lài)的自動(dòng)模塊。 如果發(fā)布,它們將依賴(lài)穩(wěn)定的文件名來(lái)獲取依賴(lài)關(guān)系,這很難假設(shè)。
對(duì)于采用Jigsaw而言,這意味著一種自下而上的方法,其中任何庫(kù)作者只能在所有相關(guān)代碼都已經(jīng)模塊化之后才能對(duì)其軟件進(jìn)行模塊化。 為了簡(jiǎn)化過(guò)渡,添加了一個(gè)清單條目,該清單條目允許發(fā)布具有穩(wěn)定自動(dòng)模塊名稱(chēng)的jar,而無(wú)需模塊化代碼甚至遷移到Java9。這允許依賴(lài)此第一個(gè)庫(kù)的其他用戶使用具有穩(wěn)定名稱(chēng)的庫(kù)。對(duì)他們的代碼進(jìn)行模塊化,從而突破了自下而上的要求。
我認(rèn)為讓庫(kù)維護(hù)者在遷移其代碼以完全使用JPMS之前聲明一個(gè)明確的模塊名稱(chēng)非常重要,并且我認(rèn)為這是處理此問(wèn)題的一種充分方法,這不可能提供更好的解決方案。
反思和可及性
使用Jigsaw,不再允許使用反射來(lái)訪問(wèn)非公共,未導(dǎo)出的成員,這是許多框架當(dāng)前所認(rèn)為的機(jī)會(huì)。 當(dāng)然,設(shè)置了安全管理器后,即使在當(dāng)今的Java版本中也無(wú)法進(jìn)行這種訪問(wèn),但是由于很少使用安全管理器,因此無(wú)需過(guò)多考慮。 對(duì)于Jigsaw,這種默認(rèn)設(shè)置是相反的,在這種情況下,需要顯式打開(kāi)用于這種反射訪問(wèn)的包,從而影響許多Java應(yīng)用程序。
總的來(lái)說(shuō),我認(rèn)為Jigsaw的封裝比當(dāng)前的通用開(kāi)放性更好。 如果我想讓Hibernate訪問(wèn)我的bean,則JPMS允許我僅通過(guò)合格的出口將bean打開(kāi)到Hibernate。 使用安全管理器,即使不是不可能實(shí)現(xiàn),也很難控制這種細(xì)粒度的訪問(wèn)。 但是,這種過(guò)渡會(huì)帶來(lái)很多麻煩,并且許多庫(kù)的維護(hù)不夠積極,無(wú)法適應(yīng)這些新要求。 因此,添加此限制肯定會(huì)殺死某些原本可以提供價(jià)值的庫(kù)。
此外,仍然存在一些反射的用例。 對(duì)于模擬庫(kù)Mockito(我?guī)椭S護(hù)),例如,我們需要一種在任何類(lèi)加載器中定義類(lèi)的方法。 這過(guò)去只能通過(guò)使用內(nèi)部Java API來(lái)實(shí)現(xiàn),而目前還沒(méi)有其他選擇。 由于Mockito僅在測(cè)試環(huán)境中使用,因此在這種情況下,安全性無(wú)需關(guān)注。 但是,由于sun.misc.Unsafe開(kāi)放性,我們已經(jīng)依靠該開(kāi)放性來(lái)實(shí)例化模擬類(lèi)而無(wú)需構(gòu)造函數(shù)調(diào)用,因此我們可以通過(guò)使用其直接內(nèi)存API更改其可訪問(wèn)性來(lái)簡(jiǎn)單地打開(kāi)這些API。
當(dāng)然,這在接下來(lái)的幾年中還不夠好解決,但是我堅(jiān)信可以在完全刪除不安全類(lèi)之前解決這些問(wèn)題。 作為一種可能性,可以使用需要在命令行上明確解決的測(cè)試模塊來(lái)擴(kuò)展JVM,并允許這種擴(kuò)展訪問(wèn)。 另一種選擇是要求任何測(cè)試運(yùn)行程序都附加Java代理,因?yàn)樗鼈兙哂型黄颇K障礙的能力。 但是就目前而言,任何維護(hù)的軟件都有機(jī)會(huì)解決其非標(biāo)準(zhǔn)Java使用問(wèn)題,并在未來(lái)幾年繼續(xù)討論缺少的API。
尋找共識(shí)
考慮到社交焦慮的計(jì)算機(jī)呆子的刻板印象,軟件開(kāi)發(fā)可能是一件相當(dāng)感性的事情。 甲骨文一直以來(lái)都是Java開(kāi)發(fā)人員所討厭的公司,當(dāng)前的討論在一定程度上推動(dòng)了這一潮流。 但是,從Java作為一種語(yǔ)言和平臺(tái)的成功來(lái)看,我確實(shí)認(rèn)為Oracle值得稱(chēng)贊的是它在管理方面的客觀出色表現(xiàn)。 考慮到未來(lái)的成功,當(dāng)今破解軟件是一項(xiàng)微妙而無(wú)濟(jì)于事的任務(wù)。 任何重構(gòu)正確但復(fù)雜的代碼的人都應(yīng)該同情這個(gè)挑戰(zhàn)。
拼圖項(xiàng)目經(jīng)常因不必要的努力而受到批評(píng),我承認(rèn)這種想法已經(jīng)超出了我的想法。 但是,歸功于模塊系統(tǒng),像CORBA或RMI這樣的自重組件最終可以從JVM中刪除。 隨著模塊化Java應(yīng)用程序大小的隱式減小,JVM對(duì)于在容器化應(yīng)用程序和云計(jì)算中的使用變得越來(lái)越有吸引力,這對(duì)于Oracle的市場(chǎng)策略而言肯定不是巧合。 雖然當(dāng)然可以將這種工作進(jìn)一步推遲到更高的Java版本,但是JVM必須在某個(gè)時(shí)候解決功能的刪除。 現(xiàn)在是一個(gè)好時(shí)機(jī)。
為了簡(jiǎn)化即將到來(lái)的過(guò)渡,將重大更改降低到最小很重要。 因此,我堅(jiān)信擴(kuò)展Jigsaw的范圍并不符合更廣泛的Java社區(qū)的最大利益。 最近投票的許多否決票都要求有關(guān)各方就懸而未決的問(wèn)題達(dá)成共識(shí)。 不幸的是,在只有一方放棄立場(chǎng)的情況下才能達(dá)成共識(shí)的情況下,可以實(shí)施或放棄所討論的功能。
考慮到典型的Java應(yīng)用程序,我希望Oracle不會(huì)通過(guò)范圍擴(kuò)展來(lái)滿足需求,只是為了確保對(duì)Jigsaw JSR進(jìn)行成功的投票。 相反,我想呼吁那些對(duì)JSR表示反對(duì)的專(zhuān)家組成員重新考慮他們對(duì)整個(gè)Java生態(tài)系統(tǒng)需求的投票,因?yàn)楝F(xiàn)有企業(yè)模塊解決方案的需求只是眾多因素中的一個(gè)因素。 隨著Java從商業(yè)應(yīng)用程序到低延遲系統(tǒng)的廣泛使用,自然而然地,不同的各方會(huì)為平臺(tái)的發(fā)展確定不同的優(yōu)先級(jí)。 我相信Oracle已經(jīng)為服務(wù)于大多數(shù)用戶的模塊系統(tǒng)找到了共同點(diǎn)。
翻譯自: https://www.javacodegeeks.com/2017/05/yet-another-jigsaw-opinion-piece.html
總結(jié)
- 上一篇: ddos能防御吗(ddos能防多少人)
- 下一篇: java 死锁的检测与修复_调查死锁–第