一、概述
鑑於JDK8已經是老古董,還有效能問題,兼且各個公司已經不再維護1.8的JDK,所以升級公司的核心產品之一的後端到JDK到17是相對要緊的事情。
透過升級到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) 確認是否能升級
除了前文提到的原因,還需要確定當前這個產品是否可以升級,畢竟JDK17和JDK1.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 |
|
注:
- 主要考慮到核心元件即可,其它的小元件遇到了再解決。
- 有什麼版本可用,可以訪問https://mvnrepository.com,或則各個元件官網(一般是github),或者是國內映象網站,例如阿里的https://developer.aliyun.com/mvn/search
- 部分元件版本必須在升級中除錯後才可以確定
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過時,改用apache的Base64
(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上程式碼的許可權,設定為只讀,並通知有關人。