杰尔·地狱
什么是JAR地獄? (或者是classpath地獄?還是依賴地獄?)在考慮使用Maven或OSGi等現(xiàn)代開發(fā)工具時,哪些方面仍然有意義?
有趣的是,似乎沒有對這些問題的結(jié)構(gòu)化答案(即,即使第二頁也沒有希望的頭條新聞)。 該職位應(yīng)填補這一空白。
總覽
我們將從構(gòu)成JAR地獄的一系列問題開始,暫時忽略構(gòu)建工具和組件系統(tǒng)。 當(dāng)我們評估當(dāng)前的狀況時,我們將回到第二部分。
杰爾·地獄
JAR Hell是一個可愛的術(shù)語,指的是Java的類加載機制的特性所引起的問題。 其中一些相互依存。 其他人是獨立的。
未表達的依賴性
JAR無法以JVM可以理解的方式來表示它依賴的其他JAR。 需要一個外部實體來識別和實現(xiàn)依賴關(guān)系。 開發(fā)人員必須通過閱讀文檔,找到正確的項目,下載JAR并將其添加到項目中來手動執(zhí)行此操作。 可選的依賴項(如果開發(fā)人員要使用某些功能,則JAR可能僅需要另一個JAR)會使過程進一步復(fù)雜化。
運行時在需要訪問依賴之前,將不會檢測到它們。 這將導(dǎo)致NoClassDefFoundError崩潰正在運行的應(yīng)用程序。
傳遞依存關(guān)系
為了使應(yīng)用程序正常工作,可能只需要少數(shù)幾個庫。 每個反過來可能需要少量其他庫,依此類推。 由于未表達的依賴關(guān)系的問題變得更加復(fù)雜,因此它變得越來越費力且容易出錯。
遮蔽
有時,類路徑上的不同JAR包含具有相同完全限定名稱的類。 可能由于不同的原因而發(fā)生這種情況,例如,當(dāng)同一庫有兩個不同版本時,或者當(dāng)胖JAR包含也作為獨立JAR引入的依賴項時,或者當(dāng)一個庫被重命名并在不知不覺中兩次添加到類路徑時。
因為將從類路徑上的第一個JAR加載類以包含它們,所以該變體將“遮蓋”所有其他類并使它們不可用。
如果變體在語義上有所不同,則可能導(dǎo)致從微妙到通知錯誤的行為到破壞破壞性錯誤的一切。 更糟糕的是,此問題本身表現(xiàn)出來的形式似乎不確定。 這取決于搜索JAR的順序。 這在不同的環(huán)境中可能完全不同,例如在開發(fā)人員的IDE和最終將運行代碼的生產(chǎn)機器之間。
版本沖突
當(dāng)兩個必需的庫依賴于第三個庫的不同的,不兼容的版本時,就會出現(xiàn)此問題。
如果兩個版本都存在于類路徑中,則該行為將不可預(yù)測。 首先,由于存在陰影,兩個版本中都存在的類將僅從其中一個加載。 更糟糕的是,如果訪問一個中存在但另一個不存在的類,則該類也將被加載。 因此,調(diào)用庫的代碼可能會同時找到這兩個版本。
由于需要不兼容的版本,如果缺少其中一個版本,則該程序很可能無法正確運行。 同樣,這可以表現(xiàn)為意外行為或NoClassDefFoundErrors。
復(fù)雜類加載
默認情況下,所有應(yīng)用程序類均由同一類加載器加載,但是開發(fā)人員可以自由添加其他類加載器。
這通常是通過組件系統(tǒng)和Web服務(wù)器之類的容器完成的。 理想情況下,這種隱式使用對應(yīng)用程序開發(fā)人員是完全隱藏的,但是,眾所周知, 所有抽象都是泄漏的 。 在某些情況下,開發(fā)人員可能會明確添加類加載器以實現(xiàn)功能,例如,允許其用戶通過加載新類來擴展應(yīng)用程序,或者能夠使用具有相同依賴性的沖突版本。
無論多個類加載器如何進入畫面,它們都可以Swift導(dǎo)致顯示出意料之外且難以理解的行為的復(fù)雜機制。
類路徑地獄和依賴地獄
Classpath地獄和JAR地獄本質(zhì)上是同一件事,盡管后者似乎更加關(guān)注由復(fù)雜的類加載器層次結(jié)構(gòu)引起的問題。 這兩個術(shù)語都特定于Java和JVM。
另一方面, 依賴地獄是一個使用更廣泛的術(shù)語。 它描述了軟件包及其依賴項的一般問題,適用于操作系統(tǒng)以及各個開發(fā)生態(tài)系統(tǒng)。 考慮到它的通用性,它并不涵蓋特定于單個系統(tǒng)的問題。
從上面的列表中,它包括可傳遞的和可能未表達的依賴關(guān)系以及版本沖突。 類的加載和屏蔽是Java特定的機制,依賴地獄將不會涵蓋這些機制。
發(fā)布時間由惠康圖書館在CC-BY 4.0
事態(tài)
構(gòu)建工具
查看問題列表,我們看到構(gòu)建工具如何幫助解決其中的一些問題。 它們擅長使依賴關(guān)系顯式化,以便可以沿著傳遞依賴關(guān)系樹的無數(shù)邊沿搜尋每個所需的JAR。 這在很大程度上解決了未表達和傳遞依賴的問題。
但是Maven等。 對陰影不做任何事情。 盡管它們通常致力于減少重復(fù)的類, 但不能阻止它們 。 除了指出版本沖突之外,構(gòu)建工具也無助于解決版本沖突。 而且由于類加載是運行時構(gòu)造,因此它們也不涉及。
組件系統(tǒng)
我從未使用過OSGi或Wildfly之類的組件系統(tǒng),因此無法證明它們的工作情況。 從他們的說法看來,他們似乎能夠解決大多數(shù)JAR地獄問題。
但是,這帶來了額外的復(fù)雜性,并且經(jīng)常要求開發(fā)人員更深入地研究類加載器機制。 具有諷刺意味的是,上面的列表中也有一點。
但是,無論組件系統(tǒng)是否確實在很大程度上緩解了JAR地獄的痛苦,我的印象是,絕大多數(shù)項目都沒有使用它們。 在這種假設(shè)下,絕大多數(shù)人仍然遭受與類路徑有關(guān)的問題。
這在哪里離開我們?
由于它們未被廣泛使用,因此組件系統(tǒng)不會影響大局。 但是,構(gòu)建工具的普遍存在大大改變了JAR地獄不同圈子的嚴重性。
我參與或聽說過的任何構(gòu)建工具都不支持的項目都花費了大量的時間來處理來自未表達或可傳遞依賴的問題。 陰影有時會抬起丑陋的頭,需要花費不同的時間來解決-但最終總是如此。
版本沖突是JAR地獄中最棘手的問題。
但是,每個項目遲早都要依靠相互沖突的版本進行斗爭,并且必須做出一些艱難的決定才能解決這些問題。 通常,必須推遲某些所需的更新,因為這將強制執(zhí)行當(dāng)前無法執(zhí)行的其他更新。
我敢說,對于大多數(shù)大小合適的應(yīng)用程序,服務(wù)和庫,版本沖突是何時以及如何更新依賴項的主要決定因素之一。 我覺得這是無法忍受的。
對于非平凡的類加載器層次結(jié)構(gòu),我經(jīng)驗不足,無法評估它們有多少重復(fù)出現(xiàn)的問題。 但是考慮到我迄今為止從事的所有項目都不需要它們,我敢說它們并不常見。 在網(wǎng)絡(luò)上搜索使用它們的原因通常會發(fā)現(xiàn)我們已經(jīng)討論的內(nèi)容:依賴關(guān)系導(dǎo)致版本沖突。
因此,根據(jù)我的經(jīng)驗,我會說版本沖突是JAR地獄中最棘手的問題。
反射
我們已經(jīng)討論了JAR地獄的構(gòu)成要素:
- 未表達的依賴性
- 傳遞依賴
- 遮蔽
- 版本沖突
- 復(fù)雜類加載
根據(jù)為游戲帶來的構(gòu)建工具和組件系統(tǒng)以及它們的使用范圍,我們得出結(jié)論,很大程度上解決了未表達的和可傳遞的依賴關(guān)系,這至少掩蓋了緩解和復(fù)雜類加載的不常見性。
這使版本沖突成為JAR地獄中最有問題的方面,影響了大多數(shù)項目中的日常更新決策。
翻譯自: https://www.javacodegeeks.com/2015/10/jar-hell.html
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎總結(jié)
- 上一篇: Java多线程:易失性变量,事前关联和内
- 下一篇: junit4 单元测试框架_超越JUni