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