java 8 - java 17 升級指北
2014年釋出的java SE 8和2017年釋出的java EE 8,至今還是使用最廣泛的java版本,大部分java開發者對於java 8之後的升級總是敬而遠之,這跟java 9以後的破壞性升級和oracle的商用協議有關,但隨著9月java 17的釋出,我們有更多理由去升級和在新專案中使用更新的java了。
為什麼要升級?
- java 9之後的Java改變了更新策略,java 11是8之後的第一個LTS版本,之後每隔半年更新一個小版本,三年更新一個LTS版本,所以java 17是下一個LTS版本。
- 最顯著的改善是幾乎免費獲得的效能提升。java 8預設GC是Parallel GC,java 9 之後預設是G1 GC,且就算是同一個GC,新版本中的表現也會比舊版本效能好,我們的程式觸發full GC的次數和GC造成的程式暫停會更短。關於這一點,有一篇文章分析了效能
- oracleJdk在11版本之後商用是需要付費的,17這個版本又改回了商用免費,openJdk和oracleJdk之間又可以自由選擇了
- spring剛剛官宣2022年即將釋出的spring framework 6.0和springboot 3.0版本最低要求java 17,且kafka 3.0版本之後也會棄用java 8,升級已經是一個趨勢,未來更多框架和中介軟體會棄用java 8,作為開發人員也不能停止腳步
升級的注意事項
人們在java 8這個版本不願意升級,除了怕影響專案穩定性,還有就是java 9之後的釋出頻率太快,但在多瞭解一點java 9之後的更新策略後就會知道非LTS版本是不需要升級,也不建議升級的,只有每隔三年釋出的LTS版本才有必要升級(另外java 17之後oracle有個提議改動LTS版本釋出頻率,之後的LTS版本可能每兩年釋出一次);另一方面是有很多破壞性更改,升級後舊專案可能直接報錯。但在java 8釋出至今的7年後,升級的解決方案已經很成熟了,這些問題不應該還是我們升級的阻力
關於升級時可能遇到的問題我做了個彙總,以版本區分,升級時,也建議一步步升級,比如先升級到java11,沒問題再升級到java17,便於發現升級時的問題。以下解決方案基於maven
第一步建議先升級依賴項
如果你的專案基於java 8,在升級前最好先升級依賴項,從java 8升級到java 17是一個很大的跨越,依賴項不升級則出問題的概率會比較高,maven可以用mvn versions:display-dependency-updates
命令檢查依賴項更新,輸出會類似這樣
然後可以把依賴項升級到輸出的對應版本,大部分包升級不會出問題,如果有問題,建議去出問題的依賴官方倉庫尋找解決方案。這個命令是直接查詢maven遠端倉庫,如果依賴項多的話會執行比較長的時間
各版本升級需要修改的內容
java 11
java 11刪除了這些原本在jdk中的包:
- javaFX
- jdk自帶的一些字型,主要影響Apache POI這類依賴字型的庫,解決方法是自行在作業系統安裝字型,比如Ubuntu,需要安裝fontconfig包
apt install fontconfig
- JMC:Java Mission Control,java自帶的效能分析工具,自java11後從jdk刪除,可自行下載
- 刪除java EE和CORBA模組,SE中刪除java EE相關的包是因為這些包已經由java EE提供,而且由於oracle的政策,一些包的名稱空間也改變了,例如JAXB包下的
javax.xml.bind.*
更改為jakarta.xml.bind.*
,下圖列舉了包名的改動,如果專案使用了這些包,需要在程式碼和pom.xml中更改相應包名
java 14
刪除了CMS GC,對於老專案或針對CMS專門調優過的專案,建議升級後使用G1 GC
java 15
Nashorn JavaScript Engine在這個版本被移除,如果使用了則需要手動新增這個依賴項
<dependency>
<groupId>org.openjdk.nashorn</groupId>
<artifactId>nashorn-core</artifactId>
<version>15.2</version>
</dependency>
java 16
java 16也是一個改動很大的版本,這個版本預設對jdk內部的很多api做了強封裝,預設情況下不可訪問(可以通過選項--illegal-access
更改這個行為,但官方不建議),這個主要影響一些工具,比如lombok,而lombok在java 16釋出後不久更新了版本解決這個問題。
如果實在找不到相容的方法,則在pom.xml修改compiler plugin引數可以解決問題:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<fork>true</fork>
<compilerArgs>
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED</arg>
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED</arg>
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED</arg>
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED</arg>
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED</arg>
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED</arg>
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED</arg>
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED</arg>
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.jvm=ALL-UNNAMED</arg>
</compilerArgs>
</configuration>
</plugin>
如果升級到java 16,但lombok沒有更新,則會報一個讓你一頭霧水的錯誤:
[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]
除此之外沒有更多資訊了,這一點可以說是一個很大的坑
java 17
主要刪除了一些實驗性特性和老舊的API:
- applet API被棄用,估計也沒有什麼專案用到這個
- 實驗性的AOT和JIT被刪除,最近AOT的特性還是很火的,想使用這個特性的可以使用graalVM,spring native是spring基於graalVM的實現,使用spring native的java程式的啟動時間會縮短到毫秒級,但也犧牲了一些執行時優化,可以說是java在雲原生時代的進化,這裡就不過多介紹了。
- 還有一個最大的變化是之前的
--illegal-access
引數不在可用,如果在java 17使用這個引數訪問受限的api則會報出InaccessibleObjectException
,大多數情況下只要升級了依賴項是不會碰到這個情況的,但如果出現問題,則可以使用--add-opens
來對不可訪問的api授權。
升級完成後可以做的事情
對開發人員來說最想做的自然是使用新的特性,包括var records instanceof switch這些新關鍵字和舊語法的改進,以及Stream和Optional等API的改進等,此處不在贅述。
結語
這裡總結的是一些我自己升級過程中遇到的問題,只要將依賴項同步升級,基本可以解決升級會遇到的所有問題。當然不是所有專案都適合升級的,這裡需要根據專案的情況仔細斟酌,如果是新專案,想跟上技術迭代的腳步,還是非常推薦升級到java 17的。
參考文章及連結:
- https://www.optaplanner.org/blog/2021/09/15/HowMuchFasterIsJava17.html
- https://www.infoq.com/articles/why-how-upgrade-java17/
- https://github.com/johanjanssen/JavaUpgrades