關于從先前的長期支持版本(Java 11 和 Java 8)遷移代碼,你需要知道的是什么?整理 | 王曉曼出品 | CSDN(ID:CSDNnews)Spring Framework 6 將采用 Java 17 和 Jakarta EE 9正如在 SpringOne 上宣布的那樣,Spring Framework 6 和Spring Boot 3 計劃在 2022 年第四季度實現總體可用性的高端基線:Java 17+(來自 Spring Framework 5.3.x 線中的 Java 8-17)Jakarta EE 9+(來自Spring框架5.3.x 線中的 Java EE 7-8)這一前瞻性的基線將為我們的 APl 設計和集成工作帶來巨大的好處,在未來的許多年里,它將為應用程序代碼和框架以及應用程序帶來光明。然而,這當然是有代價的:Spring Framework 6 和 Spring Boot 3 的應用程序在運行時至少需要 JDK 17,以及 Tomcat 10 /Jetty 11(為了兼容 Jakarta EE 9)。更重要的是,在你的應用程序源代碼中可能需要一些更改:例如在 jakarta EE 9 中 javax 到 jakarta 命名空間的更改,無論你在哪里接觸 Servlet API、JPA、Bean Validation 等。雖然一開始這聽起來有點咄咄逼人,但請記住,我們談論的是2022年第四季度的發布:屆時,不僅 JDK 17 將取代 JDK 11 成為下一個長期支持版本,而且它本身也將被 JDK 18 和 JDK 19 作為當時可用的特性版本取代,而 JDK 20 的特性已經接近凍結。Jakarta EB 9 也一樣:我們預計屆時 Jakarta EE 10 將會推出,而另一代 Tomcat、Jetty 和 co 將作為運行時選項被支持。保持以上基線作為最小值,這允許在 Spring Framework 6.x 中進一步了解 Java 的發展,Java 17 和 Jakarta EE 9 只是開始。同時,Spring Framework 5.3.x 和 Spring Boot 2.x 目前仍在積極開發中,今年 11 月將推出 Spring Boot 2.6,然后在 2022 年 5 月推出 Spring Boot 2.7,最新一次 Spring Boot 2.x 特性分支已經到達(還沒有確定這是 2.7 還是可能的更高版本),該分支將與 Spring Framework 5.3 一起進入擴展的開源維護階段。與 Spring Framework 6 和 Spring Boot 3 并行,有好幾年的重疊。考慮我們之前對 Spring Framework 3.2.x 和4.3.x 的分支,以了解在實踐中將如何實現。因此,如果你打算繼續使用 JDK 8 或 11 幾年,或者即使你打算繼續使用 JDK17上的 Spring Framework 5.3 基礎設施幾年,請放心,我們對 Spring Framework 5.3.x 和 Spring Boot 2.x 的擴展維護是可以的,會讓你覆蓋。一旦你準備使用下一代應用程序的 Java 生態系統,以自己的節奏隨時升級到 Spring 框架 6 和 Spring Boot 3,隨著 Tomcat、Jetty 和 co——新基礎設施的好處和新建筑成為可用的選項。我們希望在接下來的幾年里你們會欣賞這些選擇。注:如果你想知道 JDK 11 作為一個 LTS 的生成,請注意,JDK 11 的商業支持時間框架比 JDK 8 短,JDK 11 的 LTS 將在 2023 年底淘汰。JDK 17 作為下一代 LTS 將提供至少到 2026 年的支持時間框架。我們認為 JDK 8 在生態系統中具有獨特的作用;相比之下,JDK 11 只是一個過渡版本。此外,JDK 17 提供了一組最近積累起來的語言、API 和 JVM 增強功能,使其成為一個更有吸引力的升級。同樣重要的是,在同一個 Spring Framework 6.x 中,JDK 23 LTS(2024年)和 JDK 29 LTS(2027年)仍然會支持,最終的支持范圍會更廣 JDK 17-29。為什么要升級到 Java 17?Java 17, Java 語言和運行平臺的下一個長期支持(LTS)版本,將于 9 月 14 日正式發布。不幸的是,許多應用程序仍然在舊版本的 Java 上運行,比如以前的 LTS 版本:Java 11 和 Java 8。本文解釋了為什么應該升級應用程序,并幫助您實際升級到Java 17。但首先,你們很多人可能會問:“為什么升級?”為什么會有人想要升級到最新的 Java 版本?特別是如果你的應用程序在 Java 8、Java 11、Java 14 或您正在使用的任何版本上運行得很好時,這是有理由懷疑的。升級到 Java 17 需要付出努力,尤其是對那些目標是真正利用 JVM 中的新語言特性和功能的人。沒錯,根據環境和應用程序的不同,可能需要進行一些升級。開發人員和其他團隊成員需要更新他們的本地環境。此外,構建環境和運行環境(如生產環境)也需要升級。幸運的是,許多項目和團隊使用 Docker,它在這方面幫助很大。在我自己的團隊中,團隊定義了他們自己的持續集成/持續部署(CI/CD)管道,他們用 Docker 映像運行一切。團隊可以通過在他們的 Docker 映像中指定該版本升級到最新的 Java 版本——這不會影響其他可能在舊 Java 版本上運行的團隊,因為這些團隊使用的是舊的 Docker 映像。在 Kubernetes 上運行的測試和生產環境也是如此。當一個團隊想要升級到一個新的 Java 版本時,他們可以自己更改 Docker 映像,然后部署所有內容。(當然,如果您仍然有共享的構建環境,或者其他管理您環境的團隊,這個過程可能會有點挑戰性。)應用程序也可能需要做一些更改。我注意到,團隊發現有這么多的工作量是很有挑戰性的,以致將一個應用程序從 Java 8 升級到 Java 11 需要幾周甚至幾個月的時間。過高的估計往往會導致公司因為其他優先事項而推遲升級。我試圖升級了一個應用程序,估計需要幾周時間,但實際只花了幾天時間,主要是因為等待構建的完成。這部分是由于多年的升級經驗,但這也是一個剛剛起步的問題,并試圖在過程中解決問題。這是一個周五下午的好工作內容;看看你已經完成了多少工作,還有哪些挑戰,這樣就更容易估算剩下的工作。然而,即使有多年的經驗,在沒有關于項目深入信息的情況下,我也無法估計升級需要多長時間。這很大程度上取決于應用程序有多少依賴項。通常,將依賴項升級到最新版本可以解決 Java 升級過程中可能出現的許多問題。LTS 版本本文一直引用 Java 8、Java 11 和 Java 17 作為 LTS 版本。這是什么意思?下面是對 Oracle Java SE 支持路線圖的引用:對于 Java SE 8 之后的產品版本,Oracle 將每三年指定一個版本作為長期支持的(LTS)版本。Java SE 11 是一個 LTS 版本。出于 Oracle Premier Support 的目的,非 LTS 版本被認為是最新 LTS 版本的累積實現增強集。一旦一個新的特性版本可用,任何以前的非 LTS 版本都將被認為是可取代的。例如,Java SE 9 是一個非 LTS 版本,并立即被 Java SE 10 (也是非 LTS )所取代,而 Java SE 10 又立即被 Java SE 11 所取代。然而,Java SE 11 是一個 LTS 版本,因此,即使 Java SE 12 已經發布,Oracle 客戶也將收到 Oracle Premier 支持和定期更新的版本。在 Java 升級期間需要更改什么?你的應用程序包含你和你的團隊編寫的代碼,它可能還包含依賴項。如果從 JDK 中刪除了某些內容,可能會破壞代碼、依賴關系,或者兩者都破壞。這通常有助于確保這些依賴項是最新的,以解決這些問題。有時,在開始升級過程之前,你可能必須等待框架發布與最新 Java 版本兼容的新版本。這意味著,作為升級前評估過程的一部分,您對依賴項要有很好的了解。大多數功能不會一下子從 JDK 中全部刪除。首先,功能被標記為棄用。例如,用于 XML 綁定的 Java 體系結構(JAXB)在 Java 9 中被標記為棄用,然后在Java 11 中被刪除。如果您不斷更新,則會看到被棄用的部分,并且可以在功能被刪除前解決這些特性的任何使用問題。但是,如果直接從 Java 8 跳到 Java 17,那么這個特性的刪除將會立刻給您造成影響。要查看 API 的變化,例如,查看在特定的 Java 版本中哪些方法被刪除或添加到 String APl 中,可以查看 Marc Hoffmann 和 Cay Horstmann 的 Java 版本年鑒,或者 Foojay 的 Java 版本年鑒。Multirelea JAR 功能如果你的應用程序被仍然使用舊 JDK 的客戶使用,并且站點的升級不在你的控制范圍內,該怎么辦?在 Java 9 和 JEP 238 中引入的多版本 JAR 功能可能很有用,因為它允許將多個 Java 版本(包括比 Java 9更老的版本)的代碼打包到一個 JAR 文件中。例如,創建一個 Application 類(清單1)和一個 Student 類(清單2),并將它們放在 src/main/java/com/example 文件夾中。Student 類是一個在 Java 8 上運行的類。清單1:應用程序類public class Application {public static void main(String[] args) { Student student = new Student("James "); System.out.println("Implementation " + student.implementation()); System.out.println("Student name James contains a blank: " + student.isBlankName()); }}
清單2:為 Java 8 編寫的 Student 類:public class Student {final private String firstName;public Student(String firstName) {this.firstName = firstName; }boolean isBlankName() {return firstName == || firstName.trim().isEmpty(); }static String implementation() { return "class"; }}
接下來,創建一個 Student 記錄(清單3),它不僅使用記錄(在 Java 14 中引入),還使用 String.isBlank() 方法(在 Java 11 中引入),并將其放在文件夾 src/main/java17/com/example 中。清單3:使用較新 Java 特性的 Student 記錄public record Student(String firstName) {boolean isBlankName() {return firstName.isBlank(); }static String implementation() { return "record"; }}
需要一些配置,但這是取決于你使用的構建工具。可以在我的 GitHub 存儲庫中找到一個 Maven 示例。該示例構建在 Java 17 上,并創建 JAR 文件。當在 JDK 17 或更新版本上執行 JAR 文件時,將使用 Student 記錄。在舊版本上執行 JAR 文件時,將使用 Student 類。這個特性是非常有用的,例如,如果新的 API 提供更好的性能,因為你可以使用那些有最新 Java 版本的客戶的 API 。使用舊 JDK 的客戶可以使用相同的 JAR 文件,而不需要提高性能。請注意,在本示例中,所有的實現,即 Student,都應該具有相同的公共 API,以防止出現運行問題。但是,構建工具不驗證公共 API,但一些 IDE 可以。此外,在 JDK 17 中,你可以使用 jar -validate 命令來驗證 JRA 文件。需要注意的是,JDK 某些版本中提供的預覽功能。一些較大的特性首先以預覽的形式發布,可能會在下一個 JDK 中形成最終的特性。這些預覽特性在 Java 的 LTS 和非 LTS 版本中都有,并且,這些特性是通過 enable-preview 標志啟用的,默認情況下是關閉的。如果你在產品代碼中使用這些預覽特性,請注意它們可能會在 JDK 版本之間發生變化,這可能會導致需要進行一些調試或重構。關于 Java 棄用和特性移除的更多信息在升級 JDK 之前,請確保你的 IDE、構建工具和依賴項是最新的。Maven 版本插件和 Gradle 版本插件會顯示你有哪些依賴項,并列出最新的可用版本。請注意,這些工具只顯示您所使用文件的新版本——但有時文件名稱會更改,會產生分叉,或者代碼會移動。例如,JAXB 首先是通過 jakarta.xml.bind: jaxb-api,但改為 jakarta.xml.bind: jakarta.xml.bindapi 在它過渡到 Eclip Foundation 之后。要找到這樣的變化,你可以使用 Jonathan Lermitage 的 Maven Old Grouplds Alerter 插件或者它的 Gradle 插件。JavaFX。從 Java 11 開始,平臺不再將 JavaFX 作為規范的一部分,大多數 JDK 構建已經刪除了它。你可以使用來自 Gluon 的獨立 JavaFX 構建,或者將 OpenJFX 依賴項添加到你的項目中。字體。曾經 JDK 包含一些字體,但是從 Java 11 開始,它們被刪除了。例如,如果你使用 Apache POI(用于 Microsoft Office 兼容文檔的 Java API)。你需要字體,操作系統需要提供字體,因為它們不再出現在 JDK 中。但是,在諸如 Alpine Linux 這樣的操作系統中,字體必須使用 apt install fontconfigcommand 手動安裝,根據您使用的字體,可能需要額外的字體包。Java 任務控制。對于監視和分析應用程序,這是一個非常有用的工具。我強烈建議你調查一下。Java Mission Control 曾經包含在 JDK 中,但現在它可以作為一個單獨的下載,并以新的名稱提供:JDK Mission Control。Java EE。JDK 11 中最大的變化是刪除了 Java EE 模塊。前面提到的 JAXB 等 Java EE 模塊被許多應用程序使用。既然這些模塊不再存在于 JDK 中,你應該添加相關的依賴項。表 1 列出了各種模塊及其依賴關系。請注意,JAXB 和 JAX-WS 都需要兩個依賴項:一個用于 API,另一個用于實現。另一個變化是命名約定,現在 Java EE 由 Eclip Foundation 以 Jakarta EE 的名稱維護。你的包導入需要反映這種變化,例如 jakarta.xml。綁定。應該使用 * 而不是 javax.xml.bind。表1:Java EE 模塊及其當前替換CORBA。Java 的 CORBA 模塊沒有正式的替代,它在 Java 11 中被刪除了。然而,Oracle GlassFish Server 包含 CORBA 的實現。Nashorn。Java 15 刪除了 Nashorn JavaScript 引擎。如果你仍然想使用引擎,您可以使用 nashorncore 依賴項。實驗的編譯器。Java 17 刪除了對 GraalVM 實驗性的提前(AOT)和即時(JIT)編譯器的支持,這在 JEP 410 的文檔中有解釋。注意不支持的主文件你可能會看到錯誤:Unsupported class file major version 61。我 在JaCoCo 代碼覆蓋庫和各種其他 Maven 插件中看到過它。消息的主要版本 61指的是 Java 17。在這種情況下,這意味著你使用的框架或工具的版本不支持 Java 17。因此,你應該將框架或工具升級到新版本。(如果你看到一條包含主版本 60 的消息,那么它與 Java 16 有關。)請注意,一些工具,如 Kotlin 和 Gradle 還不支持 Java 17,至少在我寫這篇文章的時候(2021年8月中旬)是這樣的。有時可以解決這個問題,例如,將 Java 16 指定為 Kotlin 的 JVM 目標。不過,我希望 Java 17 支持很快就會加入。封裝 JDK 內部 APIJava 16 和 Java 17 封裝了 JDK 內部 API,這影響了各種框架,比如 Lombok。你可能會看到類似module jdk.compiler 沒有導出 com.sun.tools 這樣的錯誤,這意味著你的應用程序不再能夠訪問 JDK 的那一部分。總的來說,我建議升級所有使用這些內部組件的依賴項,并確保您自己的代碼不再使用它們。如果這實現不了,有一個解決方案仍然使你的應用程序訪問內部。例如,如果你需要訪問 comp 模塊,請使用以下命令:——add-opens = jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED
但是,只能將此解決方案作為最后的手段,最好是臨時使用,因為您正在規避 Java 團隊添加的重要保護措施。了解關于 JEP 396 中的 Java 16 和 JEP 403 中的 Java 17 的更多信息。Java升級資源建議查看我創建的 JavaUpgrades GitHub 存儲庫,其中包含一些示例、常見錯誤和解決方案,可以在升級過程中幫助你。結論升級依賴關系并為已刪除的 JDK 特性添加依賴關系可以解決許多 Java 升級挑戰。我建議采用一種結構化的方法逐步升級:首先,確保代碼已編譯,然后運行測試,再運行應用程序。如果你告訴自己、團隊和公司你可以在 JDK 17 上編譯和測試所有的東西,而不是告訴他們它幾乎已經完成了,或者更糟的是,它只完成了 80%,那么遷移的過程就會容易很多。我個人的經驗是,從 JDK 11 升級到 JDK 17 要比從 JDK 8 升級到 JDK 11 容易得多。然而,在這兩種情況下,對于重要的應用程序來說都是幾個小時到幾天的時間不等,這主要是由于等待構建完成。希望本文能簡化你的升級過程。參考鏈接:https://spring.io/blog/2021/09/02/a-java-17-and-jakarta-ee-9-baline-for-spring-framework-6https://blogs.oracle.com/javamagazine/migrate-to-java-17