java platform_Java Platform Module系统中的可选依赖项
java platform
Java平臺模塊系統(JPMS)對依賴項有很強的見解:默認情況下,需要它們(可以訪問),然后在編譯時和運行時都將它們存在。 但是,這不適用于可選的依賴項,因為代碼是針對運行時不一定存在的工件編寫的。 幸運的是,JPMS有一個require靜態子句,可以在這些確切情況下使用。
我將向您展示幾個示例,其中默認行為的嚴格性會導致問題,然后將模塊系統的解決方案介紹給可選的依賴項:需要靜態。 但是,對它們進行編碼并非易事,因此我們也將對此進行仔細研究。
總覽
一些示例建立在名為Service Monitor 的小型演示應用程序 的optional-dependencies分支上。
不需要的依賴之謎
為了確定常規的require子句的嚴格性會導致問題的原因,我想從兩個示例開始。 盡管在某些方面相似,但是稍后在我們討論如何針對可能缺少的依賴項進行編碼時,差異變得很重要。
實用程序庫
讓我們從我們正在維護的虛構庫uber.lib開始,該庫與少數其他庫集成。 它的API提供了基于它們的功能,從而公開了它們的類型。 我們將通過com.google.guava的示例進行演示 ,在我們的假設場景中,該示例已經變成了uber.lib想要針對其進行編碼的Java模塊。
作為uber.lib的維護者,我們假設沒有使用Guava的人永遠不會調用我們庫的Guava部分。 在某些情況下,這很有意義:如果沒有這樣的圖,為什么還要在uber.lib中調用為com.google.common.graph.Graph實例創建漂亮報告的方法?
對于uber.lib ,這意味著它無需com.google.guava即可完美運行:如果Guava將其放入模塊圖中 ,則客戶端可能會調用uber.lib API的該部分。 如果沒有,他們也不會,圖書館也會很好。 我們可以說uber.lib從不需要它自己的依賴。
具有常規依賴性,無法實現可選關系。
但是,使用常規的require子句無法實現這種可選關系。 根據可讀性和可訪問性規則, uber.lib必須要求com.google.guava對其類型進行編譯,但這會強制所有客戶端在啟動其應用程序時始終在模塊路徑上使用Guava。
如果與圖書館屈指可數uber.lib集成,它將使客戶依賴于所有的人,即使他們可能永遠不會使用超過一個。
這不是我們的好舉動。
花式統計圖書館
第二個示例來自演示應用程序 ,該應用程序包含一個模塊monitor.statistics 。 假設有一些高級統計信息庫,其中包含monitor.statistics要使用的模塊stats.fancy ,但是對于應用程序的每次部署,該信息都不會出現在模塊路徑中。 (這樣做的原因無關緊要,但讓我們一起使用一個許可證,該許可證可以防止將花哨的代碼“用于邪惡”,但是,由于我們是邪惡的策劃者,我們有時只是想這樣做。)
我們想在monitor.statistics中編寫代碼,該代碼使用fancy模塊中的類型,但是要使其正常工作,我們需要使用require子句來依賴它。 但是,如果執行此操作,則在不存在stats.fancy的情況下,模塊系統將不會啟動應用程序。
僵局。 再次。
帶有“需要靜態”的可選依賴項
當一個模塊需要針對另一個模塊的類型進行編譯,但又不想在運行時依賴它時,可以使用require靜態子句。 如果foo需要靜態bar,則模塊系統在編譯和運行時的行為會有所不同:
- 在編譯時,必須存在bar ,否則會出現錯誤。 在編譯過程中的酒吧是FOO可讀。
- 在運行時,可能不存在bar ,這將不會導致錯誤或警告。 如果存在,則foo可以讀取。
我們可以立即將其付諸實踐,并創建一個可選的依賴項,從monitor.statistics到stats.fancy :
module monitor.statistics {requires monitor.observer;requires static stats.fancy;exports monitor.statistics; }如果在編譯過程中缺少stats.fancy,則在編譯模塊聲明時會出現錯誤:
monitor.statistics/src/main/java/module-info.java:3:error: module not found: stats.fancyrequires static stats.fancy;^ 1 error但是,在啟動時 ,模塊系統不在乎stats.fancy是否存在。
同樣, uber.lib的模塊描述符將所有依賴項聲明為可選:
module uber.lib {requires static com.google.guava;requires static org.apache.commons.lang;requires static org.apache.commons.io;requires static io.javaslang;requires static com.aol.cyclops; }現在我們知道了如何聲明可選的依賴項,還有兩個問題需要回答:
- 在什么情況下會出現?
- 我們如何針對可選依賴項進行編碼?
接下來,我們將回答兩個問題。
喜歡我的帖子? 然后拿我的書! Java 9模塊系統
- 模塊系統的深入介紹:
- 基本概念和高級主題
- 曼寧(Manning)發布:
- 自2017年賽事開始提供搶先體驗
- 訂閱我的時事通訊以保持關注。 (甚至可以偷看。)
直到4月6日:使用代碼mlparlog可獲得 50%的折扣!
解決可選依賴項
模塊解析是這樣的過程:給定初始模塊和可觀察模塊的范圍,該模塊通過解析require子句構建模塊圖。 解析模塊時,必須在可觀察模塊的范圍中找到它需要的所有模塊。 如果是,則將它們添加到模塊圖;否則,將它們添加到模塊圖。 否則會發生錯誤。 重要的是要注意,在解析期間未放入模塊圖中的模塊在以后的編譯或執行期間也不可用。
在編譯時,模塊解析會像常規依賴項一樣處理可選的依賴項。 但是,在運行時,要求靜態子句通常被忽略。 當模塊系統遇到一個模塊系統時,它不會嘗試實現它,這意味著它甚至不檢查命名模塊是否存在于可觀察模塊的范圍中。
僅是可選依賴項的模塊在運行時將不可用。
結果,即使模塊存在于模塊路徑上(或與此相關的JDK中),也不會僅僅由于可選的依賴關系而將其添加到模塊圖中。 僅當它也是正在解析的某個其他模塊的常規依賴項,或者因為它是使用命令行標志–add-modules顯式添加的,它才會進入圖表。
也許您偶然發現了“ 大部分都忽略了可選依賴項”這一短語。 為什么大多數? 嗯,模塊系統要做的一件事是,如果一個可選的依賴關系使其成為一個圖形,則會添加一個可讀性邊緣。 這樣可以確保如果存在可選模塊,則可以立即訪問其類型。
針對可選依賴項進行編碼
可選的依賴項在針對它們編寫代碼時需要多加考慮,因為這是在monitor.statistics使用stats.fancy中的類型但運行時不存在該模塊時發生的:
Exception in thread "main" java.lang.NoClassDefFoundError:stats/fancy/FancyStatsat monitor.statistics/monitor.statistics.Statistician.<init>(Statistician.java:15)at monitor/monitor.Main.createMonitor(Main.java:42)at monitor/monitor.Main.main(Main.java:22) Caused by: java.lang.ClassNotFoundException: stats.fancy.FancyStats... many more哎呀。 我們通常不希望我們的代碼這樣做。
一般而言,當當前正在執行的代碼引用類型時,Java虛擬機會檢查它是否已加載。 如果不是,它將告訴類加載器執行此操作,如果失敗,則結果為NoClassDefFoundError,該錯誤通常使應用程序崩潰或至少從正在執行的邏輯塊中失敗。
對于可選的依賴項,我們選擇退出使模塊系統安全的檢查。
這是JAR hell著名的東西,模塊系統希望通過在啟動應用程序時檢查聲明的依賴項來克服 。 但是,由于需要static,因此我們選擇退出該檢查,這意味著我們最終可能會遇到NoClassDefFoundError。 我們該怎么做呢?
建立的依存關系
但是,在研究解決方案之前,我們需要查看我們是否確實有問題。 對于uber.lib,我們希望僅在調用庫的代碼已使用它們的情況下才使用來自可選依賴項的類型,這意味著類加載已成功。
換句話說,調用uber.lib時,必須存在所有必需的依賴項,否則將無法進行調用。 因此,我們畢竟沒有問題,也不需要做任何事情。
內部依賴
不過,一般情況有所不同。 帶有可選依賴項的模塊很可能會首先嘗試從中加載類,因此NoClassDefFoundError的風險非常高。
一種解決方案是確保在訪問依賴項之前,必須對具有可選依賴項的模塊進行所有可能的調用。 該檢查點必須評估該依賴項是否存在,如果不存在,則將到達它的所有代碼發送到不同的執行路徑。
模塊系統提供了一種檢查模塊是否存在的方法。 我在時事通訊中解釋了如何到達那里以及為什么使用新的stack-walking API ,所以當我說這是可行的方式時,在這里您只需要信任我:
public class ModuleUtils {public static boolean isModulePresent(String moduleName) {return StackWalker.getInstance(RETAIN_CLASS_REFERENCE).walk(frames -> frames.map(StackFrame::getDeclaringClass).filter(declaringClass ->declaringClass != ModuleUtils.class).findFirst().orElse((Class) ModuleUtils.class));.getModule();.getLayer().findModule(moduleName).isPresent();// chain all the methods!}}(在實際的應用程序中,緩存值可能并不總是重復相同的檢查。)
用“ stats.fancy”之類的參數調用此方法將返回該模塊是否存在。 如果使用常規依賴項的名稱(簡單的require子句)進行調用,則結果將始終為true,因為否則模塊系統將無法啟動應用程序。 如果使用可選依賴項的名稱(需要static子句)進行調用,則結果將為true或false。
如果存在可選依賴項,則模塊系統將建立可讀性,因此沿著使用模塊中類型的執行路徑進行操作是安全的。 如果不存在,選擇這樣的路徑將導致NoClassDefFoundError,因此必須找到其他路徑。
摘要
有時您想針對運行時并不總是存在的依賴關系編寫代碼。 為了使依賴項的類型在編譯時可用,但在啟動時不強制其存在,模塊系統提供了require靜態子句。 但是請注意,如果僅以這種方式引用模塊,則在解析過程中不會拾取該模塊,并且需要特別注意確保在運行時不存在可選依賴項時代碼不會崩潰。
要了解有關模塊系統的更多信息,請查看JPMS標簽或獲取我的書《 Java 9模塊系統 (帶Manning)》。 如果您對歷史觀點感興趣,請查看Project Jigsaw標簽 。
翻譯自: https://www.javacodegeeks.com/2017/04/optional-dependencies-java-platform-module-system.html
java platform
總結
以上是生活随笔為你收集整理的java platform_Java Platform Module系统中的可选依赖项的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux2008(linux 2015
- 下一篇: ljc.framework_Java 9