遷移!往Java 9前進!
文章推薦指數: 80 %
修改 java.base 的module-info顯然不是可行的方式,在Oracle JDK中提供了一個非標準引數 --add-exports , 可用來放寬(或說是破壞)模組封裝,例如,可以在編譯或執行 ...
回
程式人目錄
iThome網站首載:遷移!往Java
9前進!
在閱讀了一些的文件,也對模組化做了不少認識之後,下一步可能會想,試著來把某個專案放到JDK9上跑跑看吧!可能的話,進一步玩玩模組化,不過要從
何開始呢?套句現代的玩笑話,如果人品不好的話,你連IDE都開不起來!
解決基於JDK9的問題
正如先前專欄〈Hello,JDK9?〉中談到的,在Java
9中,JDK/JRE重新架構了,不再有rt.jar、tools.jar等檔案,而有了JMOD與JIMAGE,IDE之類的工具若依賴在這類資訊上,工具本身必須更新
才能在JDK9上運行,然後,準備好面對一大堆的編譯錯誤!
既有的專案一開始基於類別路徑而運行時,正如〈當拼圖遇上反射〉提過,為了相容性,類別路徑上的類別都會被歸入未具名模組,這時若使用的是java.sql、java.util.logging套
件中的API基本上沒問題,然而若使用到javax.xml.bind.*、javax.rmi等
套件,就會出現編譯錯誤,這是因為這些套件雖然包含在JavaSE中,然而實際上是與Java
EE相關的API,在JDK9中雖然有這些套件,然而被劃分到java.se.ee模組,因此編譯與執行時,必須使用--add-modules
java.se.ee,模組圖中才找得到這些套件。
同樣可能是因為找不到套件而編譯錯誤的另一個情況是,既有的程式使用了JDK內部的非標準API,像是sun.*套件或子套件下的類別,雖然未具名模
組可以讀取所有模組,然而能不能使用模組中的API,還是要看模組有沒有exports,而java.base模組的sun.*套件或子套件並沒有
exports,因此發生編譯錯誤。
修改java.base的module-info顯然不是可行的方式,在OracleJDK中提供了一個非標準引數--add-exports,
可用來放寬(或說是破壞)模組封裝,例如,可以在編譯或執行時期加上--add-exports
java.base/sun.net.ftp=ALL-UNNAMED,這就可以將java.base模
組的sun.net.ftp匯出給未具名模組,如果有多個套件必須exports,編譯或執行
時可以指定多個--add-exports。
如果專案運用了反射或者類別載入器,記得,JDK9的類別載入器也有了變動,這可能會影響類別是由哪個載入器載入的判斷,另一方面,過去版本的JDK
中,Extended與System載入器是java.net.URLClassLoader的實例,然而,JDK9中
Platform與System載入器是JDK內部定義的類別,如果既有的專案中依賴URLClassLoader型態來
操作Extended與System載入器,在JDK9中就會發生錯誤。
另一個延伸的問題是,既然類別不再只是來自JAR檔案,那麼像ClassLoader的getSystemResource、getResource等
方法,還是使用jar:file:$javahome/lib/rt.jar!$path這樣的URL嗎?為了因應模組化,以及有JAR、JMOD與
JIMAGE的選擇,現在要取得系統資源時,URL會是像jrt:/$module/$path的形式。
既有JAR成為自動模組
如果既有的專案,可以在JDK9上執行了,下一步就是試著朝模組化前進了,若專案沒有依賴第三方程式庫,既有程式庫都是自行開發,而且沒有複雜依賴關
係時,會最簡單的情況,〈TheStateoftheModuleSystem〉的〈Bottom-up
migration〉示範了自底而上的遷移方式可供參考。
如果有些程式庫並不在專案控制之下,而且該程式庫還沒有(或將來也不會)模組化,將之置於類別路徑成為未命名模組會有許多不便之處,這時可以將之置於
模組路徑,使之成為自動模組,〈TheStateoftheModuleSystem〉的〈Automatic
modules〉說明了這種情況下的遷移,自動模組成為未命名模組至顯式模組間的橋樑。
然而情況往往不會那麼順利,如果有一個套件的API分散在兩個JAR中,當這兩個JAR都被置於模組路徑中的話,就會發生找不到套件或類別的編譯錯
誤,因為在模組化的規範中,一個套件不能存在於兩個模組之中,這種情況若發生,該套件被視為分裂套件(Split
Package),若發現分裂套件,模組路徑中較後頭的JAR會被忽略,因此就會找不到套件或類別。
如果可以自行控制這些套件,最好的方式將是將兩個JAR中的分裂套件合併為同一個JAR,然而若做不到這點,另一個方式就是在編譯或執行時透過非標準
屬性--patch-module來修補模組,cc.openhome.jar與cc.openhome.abc.jar
中包含了同一套件,這時可以使用--patch-module
cc.openhome=path/to/cc.openhome.abc.jar,將後者包含的套件、類別等,加入
cc.openhome模組之中,這時模組路徑中就不用包含cc.openhome.abc.jar了。
定義顯式模組
接下來,可能會開始對一些置於模組路徑的專案進行模組化,而不是讓它成為自動模組,該模組可能會依賴在其他模組,因此必須知道依賴的模組名稱,方式之
一是透過JDK的jdeps工具程式,它也可以協助找出分裂套件,有了模組名稱之後,就知道在module-info裏定
義哪些requires了,而一旦定義了顯式模組,就不再是自動exports全部套件了,若
因此而使得專案產生編譯錯誤的話,記得在目前定義的顯式模組中,加上必要的exports。
如果需要實際的案例,在〈PainlesslyMigratingto
JavaJigsawModules-aCaseStudy〉中示範了方才談到的這個過程。
那麼自動模組的名稱從哪來的?如果既有的JAR沒有任何進一步更新的話,自動模組會從JAR檔案名稱試著擷取出模組名稱,例如,若是
cc.openhome-1.0.jar的話,先取得主檔名,然後去除版本號區段,版本號必須是連字號(-)或底線(_)後跟隨著數字,然後將名稱中
非字母部份替換為句號(.),因此最後會得到cc.openhome這個模組名稱。
在這樣的規則之下,不見得每個JAR都可以正確產生模組名稱,例如cc.openhome.util_1.0-spec-1.0.jar就會失敗,編
譯時期就沒有名稱可以requires,而執行時期模組路徑上存在這種JAR的話,就會產生IllegalArgumentException,
從而使得JVM無法初始模組層而發生FindException。
若不想基於檔名決定自動模組名稱,既有的JAR中,可以在META-INF/MANIFEST.MF裏增加Automatic-Module-Name,
指定自動模組名稱,然而對於第三方程式庫的既有JAR,不建議自己做這個動作,最好是讓第三方程式庫的釋出者決定自動模組名稱,免得以後產生名稱上的
困擾。
問題就在這邊了,若第三方程式庫官方還沒決定模組化,或者決定自動模組名稱之前,依檔名來產生模組名稱,並於定義顯式模組時requires,
實際上也會產生困擾,有興趣瞭解的話,可參考〈JavaSE9-JPMS
automaticmodules〉;在決定自己的應用程式是否遷移至模組化之前,看看使用到的第三方程式庫,官方是不是都決定好(自
動)模組名稱了,可以免去後續還得修改模組名稱的麻煩。
黑魔法的用與不用!
在遷移至Java9,甚至是進往模組化的路上,會遇到的問題實際上還不只這些,有興趣還可以參考〈Java
9MigrationGuide:TheSevenMostCommonChallenges〉。
活用-classpath、--module-path、--add-modules等
標準屬性,甚至是--add-exports非標準屬性,可以暫時性地解決問題,除了--add-exports之
外,還有--add-opens可以放寬(破壞)模組封裝,如果你的專案在深層反射遇上的問題,就可以試試看,另外也有--add-reads非
標準屬性,可以臨時性地在目前模組增加requires的模組,前面談到的非標準屬性--patch-module,
也是黑魔法的一種,有興趣進一步看看這些黑魔法的應用,可以參考〈Five
CommandLineOptionsToHackTheJava9ModuleSystem〉。
不過,黑魔法終究只是權宜之計,畢竟非標準屬性並不保證未來版本還會存在,而且就算想使用黑魔法,還是得知道許多模組細節才能得心應手,系統性且深入
地認識模組化是必要的,這部份的話,可以參考《Java9Revealed》或者是《Java9
Modularity》,想接觸更多遷移到JDK9時會遇上的怪問題?「WTF,
Java9?!」會是你想要的!
延伸文章資訊
- 1遷移!往Java 9前進!
修改 java.base 的module-info顯然不是可行的方式,在Oracle JDK中提供了一個非標準引數 --add-exports , 可用來放寬(或說是破壞)模組封裝,例如,可以在...
- 2Java模組化 - 恆逸教育訓練中心
Java 5新增加強型迴圈、Annotation與Generic,Java 8新增的Lambda,都讓Java程式設計語言產生很大的變化。Java 9新增的模組化,雖然對程式設計語言本身並 ...
- 3【JDK 11】關於Java 模組系統,看這一篇就夠了 - 古詩詞庫
開放模組內的哪些包(允許通過Java 反射訪問); 提供哪些服務; 依賴哪些服務. 圖-1: Java 9 Module. 也就是說, ...
- 4理解Java 9 的模組. 模組是什麼及什麼時候要使用它們 - Medium
本文中,我將介紹Java 9 Platform Module System (JPMS) [譯註:這專案名稱就不翻譯了,翻成中文也沒什麼意思],自Java 出現以來,最重要的新軟體工程 ...
- 5Java9系列第8篇-Module模組化程式設計 - IT人
我計劃在後續的一段時間內,寫一系列關於java 9的文章,雖然java 9 不像Java 8或者Java 11那樣的核心java版本,但是還是有很多的特性值得關注。