從JDK8升級到JDK17

正在战斗中發表於2024-06-22

一、概述

鑑於JDK8已經是老古董,還有效能問題,兼且各個公司已經不再維護1.8JDK,所以升級公司的核心產品之一的後端到JDK17是相對要緊的事情。

透過升級到jdk17,具有以下好處:

  • 不要在頭疼同時適應兩個jdk放下適應JDK8的負擔
  • 在生產環境基本上只需要部署一個jdk即可
  • 具有更好的效能
  • 能夠利用上更好更新的元件版本。例如springboot3,spring6.x都是基於jdk17的。
  • 更好的安全性。這對於專案很重要。因為許多客戶會安排安全測試,過時的jdk是一個不好解決的問題。升級到jdk17能夠更好解決這個問題
  • 更好的競爭能力。當我們的核心jdk是17的時候,毫無疑問比那些還沉滯在jdk8的競爭對手更好,尤其是功能相差不大的情況下。

本次升級後端,大概耗費了一週的時間,其次httpsecurity耗費了比較多的時間。

整體上,還算順利。

二、步驟詳情

總體上遵循以下步驟:

(1) 升級準備

(2) 確定spring元件版本

spring.io上看了下,選擇springboot-3.3.0

(3) 確定其它元件版本

(4) 升級有關程式碼

(5) 調整其它配置

(6) 解決有關異常

(7) 測試

(8) 完成升級

2.1. 升級準備

由於是一個大的版本升級,所以需要做以下準備:

(1) 確認是否能升級

除了前文提到的原因,還需要確定當前這個產品是否可以升級,畢竟JDK17JDK1.8不一樣,且升級了JDK17後,有關的元件都要一起升級(是否有相關的版本,相關版本是否穩定?)

考慮到我們的產品沒有使用太多的三方軟體,即使有,也都是流行的

現在jdk17都已經發布了快3年了;有很多其它公司也升級到了jdk17;部分公司已經把他們的產品升級到JDK21了。

結合這些因素,產品升級jdk17不存在技術障礙!

之所以沒有考慮立刻升級到JDK21,是因為其它很多產品都在JDK17,其次一次性到21,沒有那麼大把握。步子太大,會不會扯蛋了?

雖然官方的文件說springboot3.3.x支援JDK22。但是由於三方元件的存在,導致不敢一次性邁出太大的步子。

(2) git/svn上開一個分支,或者直接開一個新的倉庫,不要影響現有的主幹程式碼

2.2. spring元件版本

spring.io上可以看到可用的版本,本著使用最新可用版本的原則,選擇了:

  • springboot-3.3.1 (ga)
  • spring-6.1.10 這是springboot限定的版本,所以只需要選擇springboot版本即可

按照spring慣例,當升級的時候,通常相關的元件都是一期升級的,所以總的來說,只要指定springboot版本即可。

2.3.確定其它元件版本

分類

元件

功能描述

舊版本

升級

新版本

說明

資料存取

druid-spring-boot-starter

資料連線和連線池管理

1.2.11

1.2.23

核心元件,必須升級

jdbc驅動

*

jdbc連線

主要看各個廠家,考慮到jdbc驅動都是比較成熟的,在jdk17中執行,問題應該也不大

訊息佇列

org.apache.rocketmq/rocketmq-client

amqp

5.1.0

暫時不升級,這個需要較長時間的測試

http請求

httpclient,httpcore

rest請求

4.5.13

5.3.1/5.2.4

原來是:

org.apache.httpcomponents/httpclient

現在是:

org.apache.httpcomponents.client5/httpclient5

http

javax.servlet/javax.servlet-api

servlet

4.0.1

移除

http

jakarta.servlet/jakarta.servlet-api

servlet

6.0.0

新增。用於替代javax.servlet-api

JSON

fastjson2

JSON

2.0.32

2.0.51

fastjson2bug較多,儘可能升級下

JSON

com.jayway.jsonpath/json-path

JSON路徑分析

2.8.0

ORM

mybatis-spring-boot-starter

orm

2.2.2

3.0.3

不升級會導致mybatis有關bean初始化異常

ORM

pagehelper-spring-boot-starter

分頁

1.4.3

2.1.0

mybatis依賴

ORM

jsqlparser

sql解析

4.2

4.7

pageHelper依賴

XML

javax.xml.bind/jaxb-api

XML解析

2.3.1

暫時不可替代,不可刪除,也不需要升級

文件

swagger

文件

從現有版本移除

定時/排程

quartz

定時/排程

2.3.2

通用工具

org.apache.commons/common-lang3

3.12.0

3.14.0

通用工具

org.apache.commons/commons-pool2

2.9.0

2.12.0

通用工具

commons-io/commons-io

2.11.0

通用工具

commons-fileupload/commons-fileupload

1.4

儲存

minio

8.2.1

編譯

maven-compiler-plugin

編譯

3.1

3.13.0

注:

  1. 主要考慮到核心元件即可,其它的小元件遇到了再解決。
  2. 有什麼版本可用,可以訪問https://mvnrepository.com,或則各個元件官網(一般是github),或者是國內映象網站,例如阿里的https://developer.aliyun.com/mvn/search
  3. 部分元件版本必須在升級中除錯後才可以確定

2.4. 升級有關程式碼

當更換了以下元件之後,需要儘快修改程式碼,修改的原因主要包含:

(1) 配置變更

主要是spring升級導致,可能需要修改配置。當然也可能是其它的元件

(2) 包路徑變更

(3) 方法不存在

(4) 方法過時

這個需要特別注意-如果可能應該儘量把過時的方法移除掉,替換為正常的方法。

2.4.1. 修改yml配置

  • 修改範圍-spring.redis修改為spring.data.redis--這是spring要擴大spring.data的範圍

在spring.data的域名之下,有很多的內容,遠不止redis.除了基本的JDBC,還有Rest,elasticsearch,jpd,ldap等等。

  • 新增引數(bean相關)

spring:

main:

allow-circular-references: true

allow-bean-definition-overriding: true

spring6.1.10中,這兩個屬性預設是false.如果你的專案不存在迴圈引用,或者覆蓋定義的情況,那麼可以不新增.

  • 移除配置

移除swagger配置-這個太垃圾,過分入侵,還浪費了自有的註釋,增大程式設計師的工作量

希望有直接能夠利用javaDoc的類似元件。

2.4.2. java基礎型別有關的

基礎型別主要指Integer,Long,BigDecimal,BigInteger等等。

jdk17中,許多方法已經被標註為過時(deprecated)

(1) java基類 new Class("xxx")需要修改為 Class.valueOf("xxx")

new Long("xx"),new Integer("xxx"),new Byte("xx"),new Short("")

這些都要修改為對應的valueOf("xxx")

jdk這麼做,主要是出於效能考慮,尤其針對Integer

(2) Class.newInstance()

需要把這個替換為getDeclaredConstructor().newInstance()

(3) Spring.Base64Util過時,改用apacheBase64

(4) ruoyi自身的Base64,移除掉,避免和apache的衝突。

(5) ruoyi自身的md5Util移除

spring自身的改變來看,spring也逐漸向java標準和阿帕奇基金會靠近,一個是為了標準,其次是避免浪費

時間,最後是不要給spring使用者帶來困擾。spring只要做好自己的就行了。

ruoyi如果用於專案還是可以的,但是用於產品開發還是需要進行較多的改造。 因為產品要求更高的安全、適應度、效能等。

2.4.3. servlet相關

由於在jdk17中移除了javax的部分包,所以很多javax.xxx都需要修改jakarta.xxx

這裡主要包含:

(1) javax.servlet

(2) javax.annotation

其它javax.net,javax.sql等則繼續保留著。

2.4.4. httpclient相關

具體略,總之需要修改。

httpclient5有重大變更:支援http2,非同步支援,更好的連線池等

2.4.5. spring-security

這個改變比較大,在spring6.x主要透過註解和定義bean來實現spring-security配置,而在5.x中,則是透過擴充套件WebSecurityConfigurerAdapter來實現。

@Configuration

@EnableWebSecurity

@EnableGlobalAuthentication

@EnableMethodSecurity(prePostEnabled = true, securedEnabled = true)

public class SecurityConfig {

}

注意,不要去override已有的實現,否則配置還是比較麻煩的。

spring的思路就是你可以改配置,改零件,但是不要改核心。如果要改核心,那麼太費勁了。

在這個類中實現以下幾個bean即可:

  • AuthenticationProvider
  • AuthenticationManager
  • SecurityFilterChain
  • BCryptPasswordEncoder

其中SecurityFilterChain是關鍵,這裡主要配置白名單

另外一個變化是,禁用了預設的logout,而是新增了一個/logout介面:

/**

* 執行退出

* @param request

* @return

* @since 1.5

*/

@PostMapping("/logout")

public AjaxResult logout(HttpServletRequest request) {

try {

LoginUser user=SecurityUtils.getLoginUser();

String key=CacheConstants.LOGIN_TOKEN_KEY + user.getToken();

redisCache.deleteObject(key);

return AjaxResult.success();

}

catch(Exception e) {

//如果有異常,則證明已經退出了,不要阻攔

return AjaxResult.success();

}

}

2.4.6. jsqlparser

主要是因為pageHelper升級了。

當然產品本身也有用到jsqlparser

2.5. 調整其它配置

主要是編譯方面的配置。

由於升級了jdk,包括核心元件maven-compiler-plugin所以有些原來的預設設定需要進行調整。

2.5.1. 調整編譯選項

eclipse中,其實只需要設定pom.xml中配置即可,無需修改工程的環境配置。

<plugin>

<groupId>org.apache.maven.plugins</groupId>

<artifactId>maven-compiler-plugin</artifactId>

<version>3.13.0</version>

<configuration>

<source>${java.version}</source>

<target>${java.version}</target>

<encoding>${project.build.sourceEncoding}</encoding>

<parameters>true</parameters>

</configuration>

</plugin>

紅色部分新增上去,並設定為true

如果不新增這個,那麼spirng中很多需要透過反射獲取資訊的方法可能存在問題。

因為這個選項會讓java.java編譯為.class的時候,保留方法的名稱,而不是把方法名稱隨意修改為不認識的名稱。

2.6. 解決有關異常

2.6.1. bean異常

2.6.1.1. 迴圈引用和覆蓋

如前,主要新版本中,有些引數修改了預設值,所以修改如下:

spring:

main:

allow-circular-references: true

allow-bean-definition-overriding: true

2.6.1.2. @Primary問題

當有多個Datasource型別的Bean,或者類似其它的,則必須為Bean新增@Primary的註解,否則回報告異常。

Parameter 0 of method sqlSessionFactory in com.ruoyi.framework.config.db.MyBatisConfig required a single bean, but 3 were found:

觸發異常的具體程式碼如下:

而在以前的版本中不存在這個!

解決方式有兩個:

(1) 在引數上簡單新增@Qualifier("masterDataSource") -- 解決了mybatis,但是還要解決quartz等等。放棄這個方法

(2) 直接修改定義DataSource的地方,為主bean新增@Primary ,就用這個✔

2.6.2. jackson序列化異常

無法處理key型別不是String型別,例如如果是以下型別的JSON

{

"batchDetail": {

1: "good"

}

}

在序列化的時候會報告異常:

org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: class java.lang.Integer cannot be cast to class java.lang.String (java.lang.Integer and java.lang.String are in module java.base of loader 'bootstrap')

必須自定義HttpMessgeConverter,以便可以自定義處理這種型別的key

但這會導致自定義白名單功能錯誤,所以還需要調整白名單功能

2.6.3. factoryBeanObjectType異常

經過定位,這是mybatis沒有升級導致的。

注:一開始的時候,並沒有立刻要升級mybatis,雖然意識到了,但是並沒有那麼做。

2.7. 測試

(1) 每個地方都需要測試

(2) 反覆測試

這是總的原則。

測試需要持續較長時間,嚴格而言,需要再考費一個月左右。

從目前來看,總體是可用!

從效能上看,JDK17的程式的確響應更快一些,從頁面的響應也可以看出來!

2.8. 完成升級

完成升級後,關閉原來git上程式碼的許可權,設定為只讀,並通知有關人。

相關文章