爲社麼以及如何升級至Java 16或17

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在2021年4月27日的InfoQ直播中,我探討了爲什麼應該考慮"},{"type":"link","attrs":{"href":"https:\/\/www.infoq.com\/presentations\/upgrade-java16-java17\/","title":"","type":null},"content":[{"type":"text","text":"升級到Java 16或Java 17"}]},{"type":"text","text":"(一旦發佈),並就如何完成升級提供了一些實用的建議。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"直播的內容基於我個人的GitHub庫"},{"type":"link","attrs":{"href":"https:\/\/github.com\/johanjanssen\/JavaUpgrades","title":"","type":null},"content":[{"type":"text","text":"JavaUpgrades"}]},{"type":"text","text":",其中有文檔和示例介紹了升級到Java 16或Java 17時常見的難題和異常。其中也有具體的解決方案,你可以用在自己的應用程序中。示例要用Docker運行,是用Maven構建的,但是你當然也可以設置自己的Gradle構建。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本文以及那次直播都是爲了讓用戶可以輕鬆升級到Java 16或Java 17。大部分常見的升級任務都討論到了,所以你可以更容易地解決它們,並專注於克服應用程序所特有的挑戰。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"爲什麼要升級?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Java的每個新版本,尤其是大版本,都會解決安全漏洞,提升性能,增加新特性。保持Java版本最新有助於保持應用程序的健康,也有助於組織留住現有的開發人員,並有可能吸引來新員工,因爲開發人員一般更希望使用比較新的技術。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"升級有時會被視爲一項挑戰"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"人們認爲,升級到Java的新版本需要很大的工作量。這是因爲代碼庫需要變更,還需要在所有構建和運行應用程序的服務器中安裝Java的最新版本。幸運的是,有些公司使用了Docker,團隊可以讓它們自己升級這些內容。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"許多人將Java 9模塊系統(即Jigsaw)視爲一項重大的挑戰。然而,Java 9並不需要你顯式地使用模塊系統。事實上,大多數運行在Java 9以及更高版本上的應用程序並沒有在代碼庫中配置Java模塊。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"評估任何升級所需的工作量都是一項挑戰。那取決於多種因素,如依賴項數量及其現狀。舉例來說,如果你使用的是Spring Boot,那麼升級Spring Boot可能已經解決大部分升級問題。遺憾的是,由於存在不確定性,大部分開發人員會將升級工作量評估爲許多天、周甚或是月。如此一來,考慮成本、時間或其他優先事項,組織或管理層就會推遲升級。我以前見過人們對將Java 8應用程序升級到Java 11的工作量評估從數週到數月不等。不過,我曾在幾天內完成了一次類似的升級。這一部分是因爲我之前的經驗,不過,這也得益於我沒有多想就開始了升級過程。週五下午升級Java就很理想,看看會發生什麼。我最近將一個Java 11應用程序升級到了Java 16,我唯一需要完成的任務就是升級一個"},{"type":"link","attrs":{"href":"https:\/\/projectlombok.org\/","title":"","type":null},"content":[{"type":"text","text":"Lombok"}]},{"type":"text","text":"依賴項。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"升級可能很困難,評估所需的時間似乎是不可能的,但通常,實際的升級過程不會花那麼多時間。在許多應用程序升級中,我都見過同樣的問題。我希望幫助團隊快速解決重複出現的問題,讓他們可以集中精力克服應用程序獨有的挑戰。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Java的發版節奏"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"過去,"},{"type":"link","attrs":{"href":"https:\/\/en.wikipedia.org\/wiki\/Java_version_history","title":"","type":null},"content":[{"type":"text","text":"Java每兩年發佈一個新版本"}]},{"type":"text","text":"。然而,從Java 9發佈之後,新版本發佈變成了每6個月一次,長期支持版本(LTS)每3年一次。大多數非長期支持版本都通過小版本升級提供大約6個月的支持,直到下一個版本發佈。另一方面,LTS版本幾年內都會收到小版本升級,至少到下個LTS版本發佈。實際提供支持的時間可能會更長,這取決於OpenJDK的供應商(Adoptium、Azul、Corretto等)。舉例來說,Azul對於非LTS版本提供的支持時間就比較長。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/imgopt.infoq.com\/fit-in\/625x1000\/filters:quality(80)\/filters:no_upscale()\/articles\/why-how-upgrade-java17\/en\/resources\/1Release%20cadence-1630173649116.jpg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你可能會問自己,“我應該總是升級到最新版本,還是應該停留在一個LTS版本上?”保證應用程序使用的是LTS版本意味着你可以利用小版本升級帶來的各種改進,尤其是與安全相關的那些。另一方面,在使用最新的非LTS版本時,你應該每隔6個月就升級到一個新的非LTS版本,否則就無法利用小版本升級了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然而,每6個月升一次級是一項不小的挑戰,因爲在升級應用程序之前,你可能不得不等待你所使用的框架完成升級。但是,你應該也不會等待太長時間,因爲非LTS版本的小版本很快就會不再發布了。在我們公司,我們目前決定停留在LTS版本上,因爲我們覺得自己沒有時間每6個月升級一次,這樣一個時間窗口太小。不過也不絕對,如果團隊真得需要,或者一個非LTS版本帶來了有趣的Java新特性,那麼我們也可能改變決定。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"升級到什麼版本?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一般來說,應用程序由依賴項和你自己的代碼(打包後在JDK上運行)構成。如果JDK中有什麼修改,那麼依賴項或\/和你自己的代碼就需要修改。在大多數情況下,這是由JDK移除了某項特性導致的。如果你的依賴項使用了一項已經移除的JDK特性,那麼請保持耐心,等待該依賴項的新版本發佈。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"多JDK版本"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當升級應用程序時,你可能希望使用JDK的不同版本,如最新版本用於實際的升級,老版本用於保持應用程序的運行。用於應用程序開發的當前JDK版本可以通過環境變量"},{"type":"codeinline","content":[{"type":"text","text":"JAVA_HOME"}]},{"type":"text","text":"指定,也可以藉助包管理工具"},{"type":"link","attrs":{"href":"https:\/\/sdkman.io\/","title":"","type":null},"content":[{"type":"text","text":"SDKMAN!"}]},{"type":"text","text":"或"},{"type":"link","attrs":{"href":"https:\/\/www.infoq.com\/news\/2021\/08\/jdkmon-java\/","title":"","type":null},"content":[{"type":"text","text":"JDKMon"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對於我"},{"type":"link","attrs":{"href":"https:\/\/github.com\/johanjanssen\/JavaUpgrades","title":"","type":null},"content":[{"type":"text","text":"GitHub庫"}]},{"type":"text","text":"中的示例,我使用Docker和不同的JDK版本來說明特定的特性如何工作或造成破壞。你可以試一下相關特性,而不必安裝多個JDK版本。遺憾的是,使用Docker容器的反饋迴路有點長。需要首先構建並運行鏡像。所以一般來說,我建議你儘可能從IDE內升級。但是,在一個乾淨的、沒有個性化設置的Docker容器環境中試驗一些東西或構建應用程序或許是一個不錯的注意。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲了說明這一點,我們創建了一個標準的"},{"type":"codeinline","content":[{"type":"text","text":"Dockerfile"}]},{"type":"text","text":" 文件,其中包含下面的內容。該示例使用了Maven JDK 17鏡像,並將你的應用程序代碼複製到裏面。"},{"type":"codeinline","content":[{"type":"text","text":"RUN"}]},{"type":"text","text":" 命令會運行所有測試,出錯了也不會失敗。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"FROM maven:3.8.1-openjdk-17-slim\n\nADD . \/yourproject\nWORKDIR \/yourproject\n\nRUN mvn test --fail-at-end\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"要想構建上述鏡像,則運行"},{"type":"codeinline","content":[{"type":"text","text":"docker build"}]},{"type":"text","text":" 命令,並通過"},{"type":"codeinline","content":[{"type":"text","text":"-t"}]},{"type":"text","text":" 指定標籤(或名稱),通過"},{"type":"codeinline","content":[{"type":"text","text":"."}]},{"type":"text","text":" 配置上下文,在本例中是當前目錄。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"docker build -t javaupgrade .\n"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"準備工作"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"大多數開發人員都是從升級本地環境開始,然後是構建服務器,最後是各部署環境。不過,我有時候會直接在構建服務器上使用新版本的Java進行構建,而不是針對這個特定的項目做好所有配置,然後看看會出什麼問題。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一次性從Java 8升級到17也是可以的。不過,如果你遇到任何問題,可能會很難確定這兩個Java版本間的哪個新特性導致了問題。小步升級,比如從Java 8升級到Java 11,定位問題會比較容易。而且,在你搜索問題原因時,加上Java版本也是有幫助的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我建議在舊版本的Java上升級依賴項。那樣你可以專注於讓依賴項可以正常工作,而不必同時升級Java。遺憾的是,有時候沒法這樣做,因爲有些依賴項需要更新的Java版本。如果是這樣,你就別無選擇,只能同時升級Java和依賴項了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Maven和Gradle提供了一些插件,可以顯示依賴項的新版本。"},{"type":"codeinline","content":[{"type":"text","text":"mvn versions:display-dependency-updates"}]},{"type":"text","text":" 命令會調用"},{"type":"link","attrs":{"href":"https:\/\/www.mojohaus.org\/versions-maven-plugin\/","title":"","type":null},"content":[{"type":"text","text":"Maven版本插件"}]},{"type":"text","text":"。該插件會列出有新版本可用的依賴項:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"[INFO] --- versions-maven-plugin:2.8.1:display-dependency-updates (default-cli) @ mockito_broken ---\n[INFO] The following dependencies in Dependencies have newer versions:\n[INFO] org.junit.jupiter:junit-jupiter .................... 5.7.2 -> 5.8.0-M1\n[INFO] org.mockito:mockito-junit-jupiter ................... 3.11.0 -> 3.11.2\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在"},{"type":"codeinline","content":[{"type":"text","text":"build.gradle"}]},{"type":"text","text":" 文件中配置好插件後,"},{"type":"codeinline","content":[{"type":"text","text":"gradle dependencyUpdates -Drevision=release"}]},{"type":"text","text":" 命令會調用"},{"type":"link","attrs":{"href":"https:\/\/github.com\/ben-manes\/gradle-versions-plugin","title":"","type":null},"content":[{"type":"text","text":"Gradle版本插件"}]},{"type":"text","text":":"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"plugins {\n id \"com.github.ben-manes.versions\" version \"$version\"\n }\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"升級完依賴項後,就可以升級Java了。要想把代碼改到在新版本的Java上運行,最好是在IDE中進行,以確保它支持Java的最新版本。最後,將構建工具升級到最新版本,並配置Java版本:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"\n org.apache.maven.plugins\n maven-compiler-plugin\n 3.8.1\n \n 17\n \n\nplugins {\n java {\n toolchain {\n languageVersion = JavaLanguageVersion.of(16)\n }\n }\n}\ncompile 'org.apache.maven.plugins:maven-compiler-plugin:3.8.1'\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不要忘了把Maven和Gradle插件升級到最新版本。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"JDK中移除的特性"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"JDK中總是有些元素可能被移除,包括方法、證書、垃圾收集算法、JVM選項,甚至是整個工具。不過,在大多數情況下,這些被移除的部分在刪除之前已經被標記爲“已廢棄”或“將移除”。舉例來說,JAXB在Java 9中已廢棄,但最終移除是在"},{"type":"link","attrs":{"href":"https:\/\/openjdk.java.net\/projects\/jdk\/11\/","title":"","type":null},"content":[{"type":"text","text":"Java 11"}]},{"type":"text","text":"中。如果你已經解決了與已廢棄的特性相關的問題,那麼在特性真正被移除時也就不用擔心了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以參考"},{"type":"link","attrs":{"href":"https:\/\/javaalmanac.io\/","title":"","type":null},"content":[{"type":"text","text":"Java Version Almanac"}]},{"type":"text","text":"和"},{"type":"link","attrs":{"href":"https:\/\/foojay.io\/almanac\/jdk-16\/","title":"","type":null},"content":[{"type":"text","text":"Foojay Almanac"}]},{"type":"text","text":"對Java不同版本的比較,看看增加了哪些項,廢棄了哪些項,或者是移除了哪些項。以"},{"type":"link","attrs":{"href":"https:\/\/openjdk.java.net\/jeps\/0","title":"","type":null},"content":[{"type":"text","text":"Java增強提案(JEP)"}]},{"type":"text","text":"這種形式所做的高級變更可以在"},{"type":"link","attrs":{"href":"https:\/\/openjdk.java.net\/jeps\/0","title":"","type":null},"content":[{"type":"text","text":"OpenJDK"}]},{"type":"text","text":"網站上查看。關於每個Java版本的詳細信息,可以查閱Oracle公佈的"},{"type":"link","attrs":{"href":"https:\/\/www.oracle.com\/java\/technologies\/javase\/16-relnote-issues.html","title":"","type":null},"content":[{"type":"text","text":"發佈說明"}]},{"type":"text","text":"。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Java 11"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https:\/\/openjdk.java.net\/projects\/jdk\/11\/","title":"","type":null},"content":[{"type":"text","text":"Java 11"}]},{"type":"text","text":"移除了多個特性。首先是JavaFX,它已經不在規範中,也不再捆綁在OpenJDK中。不過,有的供應商提供的JDK構建包含的內容比規範裏的多。例如,"},{"type":"link","attrs":{"href":"https:\/\/github.com\/ojdkbuild\/ojdkbuild","title":"","type":null},"content":[{"type":"text","text":"ojdkbuild"}]},{"type":"text","text":"和"},{"type":"link","attrs":{"href":"https:\/\/bell-sw.com\/pages\/downloads\/","title":"","type":null},"content":[{"type":"text","text":"Liberica JDK"}]},{"type":"text","text":"的完整JDK都包含了"},{"type":"link","attrs":{"href":"https:\/\/openjfx.io\/","title":"","type":null},"content":[{"type":"text","text":"OpenJFX"}]},{"type":"text","text":"。此外,你也可以使用"},{"type":"link","attrs":{"href":"https:\/\/gluonhq.com\/","title":"","type":null},"content":[{"type":"text","text":"Gluon"}]},{"type":"text","text":"提供的"},{"type":"link","attrs":{"href":"https:\/\/gluonhq.com\/products\/javafx\/","title":"","type":null},"content":[{"type":"text","text":"JavaFX"}]},{"type":"text","text":"構建,或者嚮應用程序添加OpenJFX依賴。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在JDK 11之前,有些字體是包含在JDK中的。例如,"},{"type":"link","attrs":{"href":"https:\/\/poi.apache.org\/","title":"","type":null},"content":[{"type":"text","text":"Apache POI"}]},{"type":"text","text":"可以把這些字體用於Word和Excel文檔。然而,在JDK 11開始,就不再提供那些字體了。如果操作系統也沒有提供,那麼你可能就會遇到一些奇怪的錯誤。解決方案是在操作系統上安裝字體。根據你在應用程序中使用的字體,你可能需要安裝更多的包:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"apt install fontconfig\nOptional: libfreetype6 fontconfig fonts-dejavu\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https:\/\/www.oracle.com\/java\/technologies\/jdk-mission-control.html","title":"","type":null},"content":[{"type":"text","text":"Java Mission Control"}]},{"type":"text","text":"(JMC)是一個監控和性能分析應用程序,它開銷很小,可以在包括生產環境在內的任何環境中對應用程序做性能分析。如果你沒用過,我強烈建議你用一下。它不再是JDK的一部分,但"},{"type":"link","attrs":{"href":"https:\/\/adoptopenjdk.net\/jmc.html","title":"","type":null},"content":[{"type":"text","text":"AdoptOpenJDK"}]},{"type":"text","text":"和"},{"type":"link","attrs":{"href":"https:\/\/www.oracle.com\/java\/technologies\/jdk-mission-control.html","title":"","type":null},"content":[{"type":"text","text":"Oracle"}]},{"type":"text","text":"給它起了一個新名字JDK Mission Control,並提供了單獨的下載包。Java 11的最大變化是移除了"},{"type":"link","attrs":{"href":"https:\/\/openjdk.java.net\/jeps\/320","title":"","type":null},"content":[{"type":"text","text":"Java EE和CORBA"}]},{"type":"text","text":"模塊,如4個Web服務API——"},{"type":"link","attrs":{"href":"https:\/\/jcp.org\/en\/jsr\/detail?id=224","title":"","type":null},"content":[{"type":"text","text":"JAX-WS"}]},{"type":"text","text":"、"},{"type":"link","attrs":{"href":"https:\/\/jcp.org\/en\/jsr\/detail?id=222","title":"","type":null},"content":[{"type":"text","text":"JAXB"}]},{"type":"text","text":"、"},{"type":"link","attrs":{"href":"https:\/\/jcp.org\/en\/jsr\/detail?id=925","title":"","type":null},"content":[{"type":"text","text":"JAF"}]},{"type":"text","text":"和"},{"type":"link","attrs":{"href":"https:\/\/jcp.org\/en\/jsr\/detail?id=250","title":"","type":null},"content":[{"type":"text","text":"Common Annotations"}]},{"type":"text","text":"——因爲已經包含在Java EE中,所以被認爲是多餘的。在2017年發佈後不久,Oracle就將Java EE 8貢獻給了Eclipse基金會,旨在使Java EE開源。考慮到Oracle的品牌策略,有必要"},{"type":"link","attrs":{"href":"https:\/\/www.infoq.com\/news\/2018\/03\/java-ee-becomes-jakarta-ee\/","title":"","type":null},"content":[{"type":"text","text":"將Java EE重命名爲Jakarta EE"}]},{"type":"text","text":",並"},{"type":"link","attrs":{"href":"https:\/\/www.infoq.com\/news\/2020\/10\/the-road-to-jakartaee-9\/","title":"","type":null},"content":[{"type":"text","text":"將命名空間從"}]},{"type":"codeinline","content":[{"type":"text","text":"javax"}]},{"type":"link","attrs":{"href":"https:\/\/www.infoq.com\/news\/2020\/10\/the-road-to-jakartaee-9\/","title":"","type":null},"content":[{"type":"text","text":" 遷移到"}]},{"type":"codeinline","content":[{"type":"text","text":"jakarta"}]},{"type":"text","text":"。因此,在使用像JAXB這樣的依賴項時,確保自己使用了比較新的Jakarta EE工件。例如,JAXB工件的Java EE 8版本名爲"},{"type":"codeinline","content":[{"type":"text","text":"javax.xml.bind:jaxb-api"}]},{"type":"text","text":" ,後續開發於2018年停止。JAXB的Jakarta EE版本在新工件"},{"type":"codeinline","content":[{"type":"text","text":"jakarta.xml.bind:jakarta.xml.bind-api"}]},{"type":"text","text":" 下繼續開發。務必確保應用程序中所有的導入都已經改爲了新命名空間"},{"type":"codeinline","content":[{"type":"text","text":"jakarta"}]},{"type":"text","text":" 。例如,對於JAXB,將"},{"type":"codeinline","content":[{"type":"text","text":"javax.xml.bind.*"}]},{"type":"text","text":" 改爲"},{"type":"codeinline","content":[{"type":"text","text":"jakarta.xml.bind.*"}]},{"type":"text","text":" ,並添加相關依賴項。下圖中左邊的列是受這項變更影響的模塊。右邊兩列顯示了可以用作依賴項的"},{"type":"codeinline","content":[{"type":"text","text":"groupId"}]},{"type":"text","text":" 和"},{"type":"codeinline","content":[{"type":"text","text":"artifactId"}]},{"type":"text","text":" 。請注意,JAXB和JAX-WS都需要兩個依賴項:一個用於API,一個用於實現。官方沒有提供CORBA的替代方案,但Glassfish還是提供了一個可用的工件。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/imgopt.infoq.com\/fit-in\/625x1000\/filters:quality(80)\/filters:no_upscale()\/articles\/why-how-upgrade-java17\/en\/resources\/1Jakarta-1630173649116.jpg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Java 15"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https:\/\/openjdk.java.net\/projects\/jdk\/15\/","title":"","type":null},"content":[{"type":"text","text":"Java 15"}]},{"type":"text","text":"移除了JavaScript引擎Nashorn,不過,你仍然可以通過添加以下依賴項來使用:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"\n org.openjdk.nashorn\n nashorn-core\n 15.2\n\n"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Java 16"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在這個"},{"type":"link","attrs":{"href":"https:\/\/openjdk.java.net\/projects\/jdk\/16\/","title":"","type":null},"content":[{"type":"text","text":"版本"}]},{"type":"text","text":"中,JDK開發者封裝了一些JDK內部構件。他們不希望應用程序再使用JDK的底層API。這主要影響了Lombok這樣的工具。所幸,Lombok幾個周內就發佈了一個新版本,解決了這個問題。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果你有任何代碼或依賴項仍然使用JDK內部構件,那麼可以嘗試使用JDK的高級API來解決這個問題。如果不行的話,Maven還提供了一種變通方法:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"\n org.apache.maven.plugins\n maven-compiler-plugin\n 3.8.1\n \n true\n \n -J--add-opens=jdk.compiler\/com.sun.tools.javac.comp=ALL-UNNAMED\n -J--add-opens=jdk.compiler\/com.sun.tools.javac.file=ALL-UNNAMED\n -J--add-opens=jdk.compiler\/com.sun.tools.javac.main=ALL-UNNAMED\n -J--add-opens=jdk.compiler\/com.sun.tools.javac.model=ALL-UNNAMED\n -J--add-opens=jdk.compiler\/com.sun.tools.javac.parser=ALL-UNNAMED\n -J--add-opens=jdk.compiler\/com.sun.tools.javac.processing=ALL-UNNAMED\n -J--add-opens=jdk.compiler\/com.sun.tools.javac.tree=ALL-UNNAMED\n -J--add-opens=jdk.compiler\/com.sun.tools.javac.util=ALL-UNNAMED\n -J--add-opens=jdk.compiler\/com.sun.tools.javac.jvm=ALL-UNNAMED\n \n \n\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我曾嘗試使用"},{"type":"link","attrs":{"href":"https:\/\/maven.apache.org\/guides\/mini\/guide-using-toolchains.html","title":"","type":null},"content":[{"type":"text","text":"Maven Toolchains"}]},{"type":"text","text":"通過在"},{"type":"codeinline","content":[{"type":"text","text":"pom.xml"}]},{"type":"text","text":" 文件中指定JDK版本來實現JDK切換。很遺憾,當使用Lombok的舊版本在Java 16上運行應用程序時報錯了:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project broken: Compilation failure -> [Help 1]\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上面就是全部報錯信息。我不知道你怎麼看,但在我看來,這沒什麼用,所以我提交了這個"},{"type":"link","attrs":{"href":"https:\/\/issues.apache.org\/jira\/browse\/MCOMPILER-460","title":"","type":null},"content":[{"type":"text","text":"問題"}]},{"type":"text","text":"。如果這個問題修復了,那麼使用Maven Toolchains切換版本是一種不錯的方法。後來,我直接在Java 16上運行代碼,得到了一個更具描述性的錯誤,其中提到了我之前展示的部分變通方案:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"… class lombok.javac.apt.LombokProcessor (in unnamed module @0x21bd20ee) cannot access class com.sun.tools.javac.processing.JavacProcessingEnvironment \n(in module jdk.compiler) because module jdk.compiler does not export com.sun.tools.javac.processing \nto unnamed module …\n"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Java 17"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"JDK維護人員已經就"},{"type":"link","attrs":{"href":"https:\/\/openjdk.java.net\/projects\/jdk\/17\/","title":"","type":null},"content":[{"type":"text","text":"9月份要發佈的內容"}]},{"type":"text","text":"達成了一致。"},{"type":"link","attrs":{"href":"https:\/\/openjdk.java.net\/jeps\/398","title":"","type":null},"content":[{"type":"text","text":"Applet API將被廢棄"}]},{"type":"text","text":",因爲瀏覽器停止支持Applet已經很長時間了。實驗性的AOT和JIT編譯器也將被移除。作爲實驗性編譯器的替代方案,你可以使用"},{"type":"link","attrs":{"href":"https:\/\/www.graalvm.org\/","title":"","type":null},"content":[{"type":"text","text":"GraalVM"}]},{"type":"text","text":"。最大的變化是JEP-403:"},{"type":"link","attrs":{"href":"https:\/\/www.infoq.com\/news\/2021\/06\/internals-encapsulated-jdk17\/","title":"","type":null},"content":[{"type":"text","text":"強封裝的JDK內部構件"}]},{"type":"text","text":"。Java選項"},{"type":"codeinline","content":[{"type":"text","text":"--illegal-access"}]},{"type":"text","text":" 已經無效,如果你仍然試圖訪問一個內部API,則會拋出如下異常:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"java.lang.reflect.InaccessibleObjectException: \n Unable to make field private final {type} accessible:\n module java.base does not \"opens {module}\" to unnamed module {module}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"大多數時候,這可以通過升級依賴項或使用高級API來解決。如果不行的話,你可以使用"},{"type":"codeinline","content":[{"type":"text","text":"--add-opens"}]},{"type":"text","text":" 參數來獲得對內部API的訪問。不過,除非不得已不要這樣做。注意,有些工具在Java 17上還無法運行。例如,Gradle就無法構建項目,而Kotlin不能使用"},{"type":"codeinline","content":[{"type":"text","text":"jvmTarget = \"17\""}]},{"type":"text","text":" 。有些框架,如"},{"type":"link","attrs":{"href":"https:\/\/site.mockito.org\/","title":"","type":null},"content":[{"type":"text","text":"Mockito"}]},{"type":"text","text":",在Java 17上也有些小問題。"},{"type":"codeinline","content":[{"type":"text","text":"enum"}]},{"type":"text","text":" 字段中的方法會導致這個特定的"},{"type":"link","attrs":{"href":"https:\/\/github.com\/mockito\/mockito\/issues\/2315","title":"","type":null},"content":[{"type":"text","text":"問題"}]},{"type":"text","text":"。不過,我估計大部分問題都會在Java 17發佈之前或發佈之後短期內得到解決。對於任何插件或依賴項,你可能會在構建應用程序時看到這條消息“"},{"type":"text","marks":[{"type":"italic"}],"text":"不支持的類文件主版本61"},{"type":"text","text":"”。類文件主版本61用於Java 17,60用於Java 16。這基本上是說該插件或依賴項不能用於那個Java版本。大多數時候,升級到最新版本就可以解決問題。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"完工"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在解決了所有挑戰之後,你終於可以在Java 17上運行應用程序了。經過努力,你現在可以使用令人興奮的Java新特性了,如"},{"type":"link","attrs":{"href":"https:\/\/www.infoq.com\/news\/2020\/08\/java16-records-instanceof\/","title":"","type":null},"content":[{"type":"text","text":"記錄和模式匹配"}]},{"type":"text","text":"。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"小結"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"升級Java是一項挑戰,不過這也要看你的Java版本和依賴項有多老,你的環境配置有多複雜。本文旨在幫助你解決Java升級時最常見的挑戰。一般來說,很難評估實際的升級工作要花費多長時間。我覺得,大多數時候,從Java 11升級到Java 17要比從Java 8升級到Java 11簡單。對於大多數應用程序,從一個LTS版本升級到下一個LTS版本需要幾個小時到幾天的時間。大部分時間都花在了構建應用程序上。重要的是先開始,然後逐步更改。這樣可以激勵自己、團隊和管理層繼續努力。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你開始升級應用程序了嗎?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"作者簡介:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"Johan Janssen"},{"type":"text","text":"是Sanoma Learning教育部門的一名軟件架構師。他特別喜歡分享Java相關的知識。他在Devoxx、Oracle Code One、Devnexus等會議上做過演講。他通過參與計劃委員會來協助大會組織,發起並組織了JVMCON。他得過的獎項有JavaOne Rock Star和Oracle Code One Star。他在數字和印刷媒體上撰寫了各種文章。他是Chocolatey各種Java JDK\/JRE包的維護者,每月有大約10萬次下載。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"原文鏈接:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https:\/\/www.infoq.com\/articles\/why-how-upgrade-java17\/","title":"","type":null},"content":[{"type":"text","text":"Why and How to Upgrade to Java 16 or 17"}]}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章