java9遷移注意事項

本文主要研究下遷移到java9的一些注意事項。html

遷移種類

一、代碼不模塊化,先遷移到jdk9上,好利用jdk9的api
二、代碼同時也模塊化遷移java

幾點注意事項

不可讀類

好比sun.security.x509,在java9中歸到java.base模塊中,可是該模塊沒有export該package

能夠經過運行的時候添加--add-exports java.base/sun.security.x509=ALL-UNNAMED來修改exports設定spring

內部類

好比sun.misc.Unsafe,本來只想讓oracle jdk team來使用,不過因爲這些類應用太普遍了,爲了向後兼容,java9作了妥協,只是將這些類歸到了jdk.unsupported模塊,並無限定其可讀性。
➜  ~ java -d jdk.unsupported
jdk.unsupported@9
exports com.sun.nio.file
exports sun.misc
exports sun.reflect
requires java.base mandated
opens sun.misc
opens sun.reflect

刪除的類

java9刪除了sun.misc.BASE64Encoder,這種狀況只能改用其餘api,好比java.util.Base64

classpath vs module-path

java9引入了模塊系統,同時自身的jdk也模塊化了,引入了module-path,來屏蔽classpath,也就是說在java9優先使用module-path,畢竟jdk自己都模塊化了,應用自己沒有模塊化的話,java9經過unnamed modules及automatic modules機制來隱式模塊化,固然classpath在java9上還能繼續使用,好比配合module-path使用等。

沒有模塊化的jar在classpath會被歸到unnamed modules;在module-path則會被自動建立爲automatic modules(一個automatic modules會聲明transitive依賴全部named和unnamed module,而後導出自身的package)shell

一個包名不能在多個模塊中出現(split packages)

由於模塊中能夠exports指定包給其餘模塊,若是多個模塊exports一樣的包名會形成混亂,特別如有其餘類庫同時requires這兩個模塊,就不知道該引用那個模塊的了。api

傳遞依賴

若是一個模塊的接口參數或返回類型使用了其餘模塊的類,則建議requires transitive它依賴的模塊oracle

當心循環依賴

在設計模塊的時候,要儘量考慮到是否會有循環依賴的問題,若是有則須要從新設計app

使用services來實現optional依賴

services特別適合用來解耦調用方與實現類依賴的問題,若是接口有多種實現類,調用方沒必要要requires全部的實現類,只須要requires接口便可,使用services類型來加載實現類的實例。經過在module-path去動態添加實現模塊實現解耦。maven

模塊版本管理

module-info.java不支持聲明版本號,可是建立jar包的時候,能夠經過--module-version設置。不過模塊系統查找模塊的時候仍是使用模塊名來查找(若是module-path裏頭有多個重名模塊,則模塊系統知會使用找到的第一個,自動忽略後續的同名模塊),版本依賴問題不在模塊系統解決範疇內,交由maven之類的依賴管理工具去管理。模塊化

模塊資源訪問

模塊化以後資源文件也收到保護,只能由該模塊去訪問本模塊自身的資源文件,若是須要跨模塊訪問,也必須藉助ModuleLayer找到目標模塊,再調用目標模塊去加載該模塊的資源文件。工具

反射的使用

這裏涉及到deep reflection問題,所謂的deep reflection就是經過反射去調用一個class的非public元素。module-info.java的exports聲明package只是容許該package直接所屬的類容許訪問其public元素,並不容許反射調用非public元素。

反射在模塊系統裏頭須要特殊聲明才容許使用(使用opens聲明容許deep reflection),這樣就致使不少使用反射的類庫諸如spring,須要額外配置才能遷移到java9。解決方案有兩個:一個是opens package包名給須要反射的模塊,好比spring.beans等;一個就是直接opens整個模塊。

默認--illegal-access=permit,同時該設置只適用於java9以前的package在java9被不容許訪問,不適用於java9中新的不容許訪問的package.( 建議遷移到模塊化系統時設置爲deny)

不過就是在模塊系統中包名不同就屬於不一樣的包,沒有繼承關係,好比com.service.func1與com.service.func2這兩個是不一樣的包,你不能只opens com.service,必須分別指定這樣就致使須要open的的package比較多。所以open整個module可能更省事一點,但也屬於比較粗暴的作法。

上面的作法是在原來module-info.java裏頭去作修改,另一種是在執行java或javac的時候經過指定的命令來修改原來的關係。好比

java ... --add-opens source-module/source-package=target-module
若是須要導出給unnamed modules,則target-module爲ALL-UNNAMED

固然若是是新的系統,那就不建議使用反射了,可使用MethodHandles及VarHandles。

常見問題和措施

ClassNotFoundException/NoClassDefFoundError

好比javax.xml.bind.JAXBException,JAXB已經納入到java.xml.bind模塊,在java命名後面添加
--add-modules java.xml.bind
若是圖省事,把$JAVA_HOME及全部第三方類庫添加到module-path,而後來個
--add-modules ALL-MODULE-PATH

illegal reflective access by xxx to method java.lang.ClassLoader.defineClass

反射緣由引發,因爲舊系統沒有module-info,所以在java命名添加參數加以修改
--add-opens java.base/java.lang=ALL-UNNAMED

肯定依賴的模塊

經過IDE或者jdeps分析
jdeps --class-path 'classes/lib/*' -recursive -summary app.jar
jdeps只是靜態代碼分析,若是有使用反射用的類jdeps分析不出來,須要本身手工requires,若是dependency是optional的,能夠requires static

對模塊單元測試的可讀性問題

若是單元測試時單獨模塊的話,能夠在運行時經過--add-exports或--add-opens來授予單元測試模塊對目標模塊的可讀性及反射能力。另外因爲split packages問題,單元測試類的包名不能跟目標模塊包名重複。原來maven工程那種test

小結

能夠分兩步走遷移到java9,首先是先不模塊化,只先跑在jdk9上;而後再模塊化。

doc

相關文章
相關標籤/搜索