java 迁移数据_Java 9迁移指南:七个最常见的挑战
java 遷移數據
我敢肯定,您已經聽說過更新到Java 9并不是一件容易的事,甚至可能是不兼容的更新,而且對于大型代碼庫而言,遷移毫無意義。 這樣做之后,我遷移了一個相當大的舊代碼庫,我可以告訴你,這還不錯。 比碰到Java 8確實要花更多的時間,但是要花很多時間。 遷移最重要的是,發現了一些小問題,甚至很小的問題,無論遷移本身如何,都需要解決,我們借此機會做到了。
我在java9.wtf上收集了一些令人驚訝的細節,但將這七個最大的問題濃縮到了Java 9遷移指南中。 它既是帖子,又是可以返回的資源,因此請將其放在快速撥號上,并在遇到具體問題時進行搜索。 還要注意,盡管您需要了解一些有關模塊系統的信息 (這是一個動手指南 ),但這并不是要模塊化應用程序,而只是要使它在Java 9上編譯并運行。
非法訪問內部API
模塊系統的最大賣點之一是堅固的封裝。 它確保從模塊外部無法訪問非公共類以及非導出包中的類。 首先,這當然適用于JDK附帶的平臺模塊,其中僅完全支持java。*和javax。*軟件包。 另一方面,大多數com.sun。*和sun。*軟件包是內部的,因此默認情況下無法訪問。
盡管Java 9編譯器的行為完全符合您的預期并防止了非法訪問,但在運行時卻并非如此。 為了提供少量的向后兼容性,它通過允許對內部類的訪問,簡化了遷移并提高了在Java 8上構建的應用程序在Java 9上運行的機會。 如果使用反射進行訪問,則會發出警告。
病征
在針對Java 9進行編譯期間,您會看到類似于以下內容的編譯錯誤:
error: package com.sun.java.swing.plaf.nimbus is not visible import com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel;^(package com.sun.java.swing.plaf.nimbus is declaredin module java.desktop, which does not export it) 1 error發出反射的警告如下所示:
Static access to [Nimbus Look and Feel] WARNING: An illegal reflective access operation has occurred WARNING: Illegal reflective access by j9ms.internal.Nimbus(file:...) to constructor NimbusLookAndFeel() WARNING: Please consider reporting thisto the maintainers of j9ms.internal.Nimbus WARNING: Use --illegal-access=warn to enable warningsof further illegal reflective access operations WARNING: All illegal access operations will be denied in a future release Reflective access to [Nimbus Look and Feel]修正
依賴內部API的最明顯,可持續的解決方案是擺脫它們。 用維護的API替換它們,您還清了一些高風險的技術債務。
如果由于某種原因不能做到這一點,那么下一個最好的事情就是確認依賴關系,并告知模塊系統您需要訪問它。 為此,您可以使用兩個命令行選項:
- 選項–add-exports $ module / $ package = $ readingmodule可用于將$ module的 $ package導出到$ readingmodule 。 因此, $ readingmodule中的代碼可以訪問$ package中的所有公共類型,而其他模塊則不能。 將$ readingmodule設置為 ALL-UNNAMED時,模塊圖中的所有模塊和類路徑中的代碼都可以訪問該包。 在遷移到Java 9的過程中,您將始終使用該占位符。 該選項可用于java和javac命令。
- 這涵蓋了對公共類型的公共成員的訪問,但是反射可以做的更多:通過大量使用setAccessible(true),它可以與非公共類,字段,構造函數和方法(有時稱為Deep Reflection )進行交互,甚至在導出的包中仍然被封裝。 java選項–add-opens使用與–add-exports相同的語法,并將包打開以進行深度反射,這意味著所有類型及其成員均可訪問,而無論其可見性修飾符如何。
您顯然需要–add-exports來安撫編譯器,但為運行時收集–add-exports和–add-opens也具有一些優點:
走得更遠
根據Java 9進行編譯有助于在項目代碼庫中查找對內部API的依賴性。 但是您的項目使用的庫和框架也很可能會造成麻煩。
JDeps是要找到在您的項目和依賴JDK-內部API編譯依賴的完美工具。 如果您不熟悉它,我已經寫了入門入門。 這是將其用于手頭任務的方法:
jdeps --jdk-internals -R --class-path '$libs/*' $project在這里,$ libs是一個包含所有依賴項的文件夾,$ project是您項目的JAR。 分析輸出超出了本文的范圍,但并不難–您將進行管理。
找到反射性訪問要困難一些。 運行時的默認行為是對首次非法訪問軟件包的警告一次,這是不夠的。 幸運的是,有–illegal-access = $ value選項,其中$ value可以是:
- 允許:允許訪問所有JDK內部API,以在類路徑上進行編碼。 對于反射式訪問,將為首次訪問每個包裝發出單個警告。 (Java 9中的默認值。)
- 警告:行為類似許可證,但每次反射訪問都會發出警告。
- 調試:行為類似警告,但每個警告中都包含堆棧跟蹤。
- 拒絕:那些相信強封裝的人的選擇:
默認情況下,禁止所有非法訪問。
特別否認對于尋找反射訪問非常有幫助。 一旦收集了所有必需的–add-exports和–add-opens選項,它也是一個很好的默認值。 這樣,如果沒有注意,就不會出現新的依賴項。
帖子中只有那么多事實-幸運的是,有一本書包含了更多的事實:
Java 9模塊系統
- 模塊系統的深入介紹:
- 基本概念和高級主題
- 曼寧(Manning)發布:
- 自2017年賽事開始提供搶先體驗
- 訂閱我的時事通訊以保持關注。
(甚至可以偷看。)
使用代碼fccparlog可獲得 37%的折扣 !
對Java EE模塊的依賴
Java SE中有很多與Java EE相關的代碼。 它最終分為以下六個模塊:
- 帶有javax.activation包的java.activation
- java.corba javax.activity中有,javax.rmi中,javax.rmi.CORBA中,并org.omg。*包
- 帶有javax.transaction包的java.transaction
- java.xml.bind和所有javax.xml.bind。*軟件包
- 帶有javax.jws,javax.jws.soap,javax.xml.soap和所有javax.xml.ws。*軟件包的java.xml.ws
- 帶有javax.annotation包的java.xml.ws.annotation
由于各種兼容性原因(其中一個是拆分包,我們將在下面討論),默認情況下,類路徑上的代碼看不到這些模塊,這會導致編譯或運行時錯誤。
病征
這是java.xml.bind模塊中使用JAXBException的類的編譯錯誤:
error: package javax.xml.bind is not visible import javax.xml.bind.JAXBException;^(package javax.xml.bind is declared in module java.xml.bind,which is not in the module graph) 1 error如果您無法通過編譯器,卻忘記了運行時間,則會收到NoClassDefFoundError:
Exception in thread "main" java.lang.NoClassDefFoundError: javax/xml/bind/JAXBExceptionat monitor.Main.main(Main.java:27) Caused by: java.lang.ClassNotFoundException: javax.xml.bind.JAXBExceptionat java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:582)at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:185)at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:496)... 1 more修正
模塊化代碼后,可以在模塊的聲明中聲明常規依賴項。 在此之前,–add-modules $ module可以幫助您確保$ module可用,并且可以將其添加到java和javac中。 如果添加java.se.ee ,則可以訪問所有Java EE模塊。
分包
這有點棘手…為了增強一致性,不允許一個模塊從兩個不同的模塊讀取同一包。 但是,實際的實現更為嚴格,甚至不允許兩個模塊包含相同的程序包(已導出或未導出)。 模塊系統在該假設下運行,并且每當需要加載一個類時,它都會查找哪個模塊包含該程序包,然后在其中查找該類(這將提高類的加載性能)。
為了保護這一假設,模塊系統檢查是否找到了兩個命名模塊都將包和barfs拆分(如果找到了)。 但是,在遷移期間,您并沒有完全處于這種情況。 您的代碼來自類路徑,該類路徑將其放入所謂的未命名模塊中。 為了最大程度地提高兼容性,不對其進行檢查,并且不對模塊進行任何檢查。
現在,在拆分包的情況下,這意味著未發現命名模塊(例如,在JDK中)和未命名模塊之間的拆分。 如果混用了類加載行為,這聽起來可能很幸運:相反:如果在模塊和類路徑之間拆分包,則對于來自該包的類,類加載將始終且僅查看模塊。 這意味著包的類路徑部分中的類實際上是不可見的。
病征
癥狀是,即使絕對存在,也無法加載來自類路徑的類,從而導致如下編譯錯誤:
error: cannot find symbolsymbol: class Nonnulllocation: package javax.annotation或者,在運行時,出現上述NoClassDefFoundErrors。
可能發生這種情況的一個示例是各種JSR-305實現。 例如,使用注釋javax.annotation.Generated(來自java.xml.ws.annotation )和java.annotation.Nonnull(來自com.google.code.findbugs:jsr305 )的項目在編譯時會遇到麻煩。 它要么缺少Java EE批注,要么在如上所述添加模塊時會遇到拆分包,而看不到JSR 305模塊。
修正
遷移路徑將有所不同,具體取決于拆分JDK軟件包的工件。 在某些情況下,進入隨機JDK包的類可能不只是某些類,而是整個JDK模塊的替換類,例如,因為它覆蓋了認可的標準 。 在這種情況下,您正在尋找–upgrade-module-path $ dir選項– $ dir中的模塊用于在運行時替換可升級模塊。
如果確實只有幾個拆分包的類,則長期解決方案是刪除拆分。 如果短期內無法實現,則可以使用類路徑中的內容修補命名模塊。 選項–patch-module $ module = $ artifact會將$ artifact中的所有類合并到$ module中,將split包的所有部分放入同一模塊中,從而刪除了split。
不過,還有一些注意事項。 首先,打補丁的模塊必須實際上將其放入模塊圖中,為此可能需要使用–add-modules。 然后,它必須有權訪問成功運行所需的所有依賴項。 由于命名模塊無法從類路徑訪問代碼,因此可能有必要開始創建一些自動模塊 ,這超出了本文的范圍。
走得更遠
通過嘗試和錯誤查找拆分包非常令人不安。 幸運的是, JDeps報告了它們,因此,如果您分析您的項目及其依賴性,輸出的第一行將報告已拆分的軟件包。 您可以使用與上面相同的命令:
jdeps --jdk-internals -R --class-path '$libs/*' $project投射到URL類加載器
我剛剛描述的類加載策略是用一種新類型實現的,在Java 9中,應用程序類加載器就是這種類型。 這意味著它不再是URLClassLoader,因此偶爾的(URLClassLoader)getClass()。getClassLoader()序列將不再執行。 這是另一個典型的示例,其中Java 9在嚴格意義上是向后兼容的(因為從未指定過URLCassLoader),但是仍然可能導致移植挑戰。
病征
這是非常明顯的。 您將收到ClassCastException抱怨新的AppClassLoader不是URLClassLoader:
Exception in thread "main" java.lang.ClassCastException:java.base/jdk.internal.loader.ClassLoaders$AppClassLoadercannot be cast to java.base/java.net.URLClassLoaderat monitor.Main.logClassPathContent(Main.java:46)at monitor.Main.main(Main.java:28)修正
類加載器可能已強制轉換為特定于URLClassLoader的訪問方法。 如果是這樣,您只需很小的改動就可以進行遷移。 新的AppClassLoader唯一受支持(因此可訪問)的超類型是SecureClassLoader和ClassLoader ,在9中僅添加了一些方法。不過,請看一下,它們可能會滿足您的需求。
在運行時圖像中拖影
隨著JDK的模塊化,從根本上改變了運行時映像的布局。 rt.jar,tools.jar和dt.jar等文件不見了; 現在,JDK類已捆綁到jmod文件(每個模塊一個)中,jmod文件是一種有目的的未指定文件格式,允許將來進行優化而無需考慮向后兼容性。 而且,JRE和JDK之間的區別消失了。
所有這些都未指定,但這并不意味著沒有依賴于這些細節的代碼。 特別是像IDE這樣的工具(盡管大多數工具已經被更新了),這些更改將具有兼容性問題,并且除非更新,否則它們將以無法預測的方式停止工作。
這些更改的結果是,您從系統資源(例如從ClasLoader :: getSystemResource)獲取的URL發生了更改。 它過去的格式如下:jar:file:$ javahome / lib / rt.jar!$ path,其中$ path類似于java / lang / String.class。 現在看起來像jrt:/ $ module / $ path。 當然,所有創建或使用此類URL的API均已更新,但是非手工制作這些URL的非JDK代碼必須針對Java 9進行更新。
此外,Class :: getResource *和ClassLoader :: getResource *方法不再讀取JDK內部資源。 而是使用Module :: getResourceAsStream來訪問模塊內部資源或創建JRT文件系統,如下所示:
FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/")); fs.getPath("java.base", "java/lang/String.class"));引導類路徑
我在這里很迷茫,因為我從未使用過-Xbootclasspath選項,該選項已被刪除。 顯然,它的功能已被各種新的命令行選項所取代(此處是JEP 220的解釋):
- javac選項–system可用于指定系統模塊的備用源
- javac選項–release可用于指定備用平臺版本
- 上面提到的java選項–patch-module選項可用于將內容注入到初始模塊圖中的模塊中
新版本字符串
經過20多年的努力,Java終于并正式接受它不再在1.x版中使用。 萬歲! 因此,從Java 9開始,系統屬性java.version及其兄弟姐妹不再以1.x開頭,而是以x開頭,即Java 9中的9。
病征
沒有明確的癥狀-如果某些實用程序功能確定錯誤的版本,幾乎一切都可能出錯。 不過,找到它并不難。 對以下字符串的全文搜索應導致所有特定于版本字符串的代碼:java.version,java.runtime.version,java.vm.version,java.specification.version,java.vm.specification.version。
修正
如果您愿意將項目的要求提高到Java 9,則可以避開整個系統屬性的探測和解析,而可以使用新的Runtime.Version類型 ,這使所有這些操作變得更加容易。 如果要保持與Java 9之前版本的兼容性,您仍可以通過創建多發行版JAR來使用新的API。 如果這也不可能,則看起來您實際上必須編寫一些代碼(很多!)并根據主要版本進行分支。
摘要
現在,您知道如何使用內部API(–add-export和–add-opens),如何確保存在Java EE模塊(–add-modules)以及如何處理拆分包(–patch-module)。 這些是您在遷移期間最可能遇到的問題。 URLClassLoader轉換為較不常見且較不易于修復而無法訪問有問題的代碼,這是由于新的運行時映像布局和資源URL,已刪除的-Xbootclasspath和新版本字符串導致的問題。
知道如何解決這些問題將為您提供很好的機會來克服所有的遷移難題,并使您的應用程序可以在Java 9上編譯和運行。如果沒有,請查看JEP 261的Risks and Assumptions部分 ,其中列出了其他一些潛在的陷阱。
如果您對所有這些都有點不知所措,請等待我的下一篇文章,其中提供了一些有關如何將這些單獨的修補程序納入全面的遷移策略的建議,例如通過包括構建工具和持續集成。 或索取我的書 ,在那里我將解釋所有這些以及更多內容。
翻譯自: https://www.javacodegeeks.com/2017/07/java-9-migration-guide-seven-common-challenges.html
java 遷移數據
總結
以上是生活随笔為你收集整理的java 迁移数据_Java 9迁移指南:七个最常见的挑战的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 有的电脑加了内存条后速度提升明显只增加内
- 下一篇: java美元兑换,(Java实现) 美元