## 版本更新說明
* SpringBlade 4.0 版本已適配 Spring Boot 3 和 JDK 17,并從 Swagger 2 遷移到 OpenAPI 3。此次更新包含重大更改,屬于破壞性更新。
* **若從SpringBlade 4.0版本開始使用,本章節可以直接跳過**
* 若之前版本的工程已處于生產階段或接近開發完成,請在升級前仔細權衡。
* 接下來,我們將詳細列出工程升級的步驟及注意事項。
## 項目編譯級別升級為JDK17
### 下載安裝jdk17
1. 下載openjdk17,前往下載地址找到符合自己機器的版本并安裝配置環境變量,三種推薦版本選其一或公司選型的Jdk均可
2. 下載地址(Azul Zulu):[https://www.azul.com/downloads/?version=java-17-lts&package=jdk#zulu](https://www.azul.com/downloads/?version=java-17-lts&package=jdk#zulu)
3. 下載地址(Eclipse Temurin?):[https://adoptium.net/zh-CN/temurin/releases/?version=17](https://adoptium.net/zh-CN/temurin/releases/?version=17)
4. 下載地址(Amazon Corretto):[https://aws.amazon.com/cn/corretto/?filtered-posts.sort-by=item.additionalFields.createdDate&filtered-posts.sort-order=desc](https://aws.amazon.com/cn/corretto/?filtered-posts.sort-by=item.additionalFields.createdDate&filtered-posts.sort-order=desc)
#### 配置工程編譯級別為jdk17
1. 打開工程后選擇 `Project Structure`

2. 依次配置版本為JDK17

3. 打開設置找到Java Compiler設置為17

4. 打開設置找到Maven Runner設置為17

## API升級
1. Spring Boot 3 與 JDK17開始,由 Java EE 升級為 Jakarta EE,我們需要對工程源碼進行全局替換。
~~~
javax.validation ? ? → ? ? jakarta.validation
javax.servlet ? ? ? ?→ ? ? jakarta.servlet
javax.annotation ? ? → ? ? jakarta.annotation
javax.transaction ? ?→ ? ? jakarta.transaction
javax.persistence ? ?→ ? ? jakarta.persistence
~~~
2. 由于部分api已經過期刪除但業務代碼用到相關api,我們需要重新引入 (Tool工程已經處理,業務代碼若需要則加上對應依賴)
~~~
<!-- jakarta -->
<dependency>
? ?<groupId>jakarta.servlet</groupId>
? ?<artifactId>jakarta.servlet-api</artifactId>
</dependency>
<!-- javax -->
<dependency>
? ?<groupId>javax.xml.bind</groupId>
? ?<artifactId>jaxb-api</artifactId>
</dependency>
<dependency>
? ?<groupId>com.sun.xml.bind</groupId>
? ?<artifactId>jaxb-core</artifactId>
</dependency>
<dependency>
? ?<groupId>com.sun.xml.bind</groupId>
? ?<artifactId>jaxb-impl</artifactId>
</dependency>
<dependency>
? ?<groupId>javax.activation</groupId>
? ?<artifactId>activation</artifactId>
</dependency>
~~~
3. 原 `spring.redis` 配置升級為 `spring.data.redis` ,以下為最新版本配置
~~~
spring:
data:
? redis:
? ? host: 127.0.0.1
? ? port: 6379
? ? password:
? ? database: 0
? ? ssl:
? ? ? enabled: false
~~~
4. 針對反射工具類進行全局替換
~~~
BeanUtil.copy ? ?→ ? ? BeanUtil.copyProperties
~~~

5. 工程pom.xml版本升級
~~~
<revision>3.4.0.RELEASE</revision>
?
<java.version>1.8</java.version>
<maven.plugin.version>3.8.1</maven.plugin.version>
<maven.flatten.version>1.2.2</maven.flatten.version>
~~~
【替換為】
~~~
<revision>4.0.0.RELEASE</revision>
?
<java.version>17</java.version>
<maven.plugin.version>3.11.0</maven.plugin.version>
<maven.flatten.version>1.3.0</maven.flatten.version>
~~~
6. 序列化加上注解 `@Serial`
~~~
package org.springblade.system.entity;
?
import java.io.Serial;
import java.io.Serializable;
?
public class Entity implements Serializable {
?
@Serial
private static final long serialVersionUID = 1L;
}
~~~
## Swagger升級
1. 由原本的Swagger2 改用 OpenAPI3。Knife4J采用支持OpenAPI3的版本,我們需要對依賴、注解、引用進行全局替換
2. Knife4j前端ui依賴
~~~
<dependency>
? ?<groupId>com.github.xiaoymin</groupId>
? ?<artifactId>knife4j-openapi2-ui</artifactId>
</dependency>
~~~
【替換為】
~~~
<dependency>
? ?<groupId>com.github.xiaoymin</groupId>
? ?<artifactId>knife4j-openapi3-ui</artifactId>
</dependency>
~~~
3. Knife4j核心依賴
~~~
<dependency>
? ?<groupId>com.github.xiaoymin</groupId>
? ?<artifactId>knife4j-openapi2-spring-boot-starter</artifactId>
</dependency>
~~~
【替換為】
~~~
<dependency>
? ?<groupId>com.github.xiaoymin</groupId>
? ?<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
</dependency>
~~~
4. SpringFox移除改為新版依賴
~~~
<dependency>
? ?<groupId>io.springfox</groupId>
? ?<artifactId>springfox-swagger2</artifactId>
? ?<exclusions>
? ? ? ?<exclusion>
? ? ? ? ? ?<groupId>io.swagger</groupId>
? ? ? ? ? ?<artifactId>swagger-models</artifactId>
? ? ? ?</exclusion>
? ?</exclusions>
</dependency>
<dependency>
? ?<groupId>io.swagger</groupId>
? ?<artifactId>swagger-models</artifactId>
</dependency>
~~~
【替換為】
~~~
<dependency>
? ?<groupId>io.swagger.core.v3</groupId>
? ?<artifactId>swagger-annotations</artifactId>
</dependency>
~~~
5. Swagger2注解全局替換為OpenAPI3(Swagger3)注解
* 全局替換如下注解(箭頭前為swagger2注解需要被全局替換為箭頭后的openapi3注解)
* 具體參數使用對比如下(快捷替換可以將`@Api(value`全局替換為`@Tag(name`,其他的以此類推)
~~~
@Api(value = "xx", tags = "xxx") → @Tag(name = "xx", description = "xxx")
?
@ApiIgnore → @Hidden (若是在方法入參則將@Hidden改為@Parameter(hidden = true)替換)
?
@ApiModel(value = "xx", description = "xx") → @Schema(description = "xx")
?
@ApiModelProperty(hidden = true) → @Schema(accessMode = READ_ONLY)
@ApiModelProperty(value = "xx") → @Schema(description = "xx")
?
@ApiImplicitParams → @Parameters
?
@ApiImplicitParam → @Parameter
?
@ApiOperation(value = "xx", notes = "xx") → @Operation(summary = "xx", description = "xx")
?
@ApiParam("xx") → @Parameter(name = "xx")
@ApiParam(value = "xx") → @Parameter(name = "xx")
?
@ApiResponse(code = 404, message = "msg") → @ApiResponse(responseCode = "404", description = "msg")
~~~
* 較為復雜的組合式注解前后對比如下
~~~
@ApiImplicitParams({
?@ApiImplicitParam(name = "category", value = "公告類型", paramType = "query", dataType = "integer"),
?@ApiImplicitParam(name = "title", value = "公告標題", paramType = "query", dataType = "string")
? })
@ApiOperation(value = "分頁", notes = "傳入notice")
~~~
? ?
~~~
@Parameters({
?@Parameter(name = "category", description = "公告類型", in = ParameterIn.QUERY, schema = @Schema(type = "integer")),
?@Parameter(name = "title", description = "公告標題", in = ParameterIn.QUERY, schema = @Schema(type = "string"))
? })
@Operation(summary = "分頁", description = "傳入notice")
~~~
* 剩余無法全局替換的再手動處理
* 比如我們可以先全局替換`@ApiImplicitParam`為`@Parameter`
* 接著全局替換`paramType = "query", dataType = "integer"`為`in = ParameterIn.QUERY, schema = @Schema(type = "integer")`
* 經過兩波替換,剩下一個`, value = "`再全局替換為`, description = "`
* 前面帶一個逗號可以避免將 `@ApiOperation(value`的value值替換為`description`,因為`@ApiOperation`對應的是`summary`
6. 包路徑全局替換
~~~
import springfox.documentation.annotations.ApiIgnore; → import io.swagger.v3.oas.annotations.Hidden;
?
import io.swagger.annotations.ApiModelProperty; → import io.swagger.v3.oas.annotations.media.Schema;
?
import io.swagger.annotations.ApiModel; → import io.swagger.v3.oas.annotations.media.Schema;
?
import io.swagger.annotations.ApiImplicitParams; → import io.swagger.v3.oas.annotations.Parameters;
?
import io.swagger.annotations.ApiImplicitParam; → import io.swagger.v3.oas.annotations.Parameter;
?
import io.swagger.annotations.ApiOperation; → import io.swagger.v3.oas.annotations.Operation;
?
import io.swagger.annotations.ApiParam; → import io.swagger.v3.oas.annotations.Parameter;
?
import io.swagger.annotations.Api; → import io.swagger.v3.oas.annotations.tags.Tag;
?
import io.swagger.annotations.ApiResponse; → import io.swagger.v3.oas.annotations.responses.ApiResponse;
?
import io.swagger.annotations.*; ?→ import io.swagger.v3.oas.annotations.*;
~~~
7. Swagger2配置類升級為OpenAPI3寫法
* 原寫法如下
~~~
public class SwaggerConfiguration {
?
@Bean
public Docket authDocket() {
return docket("授權模塊", Collections.singletonList(AppConstant.BASE_PACKAGES + ".modules.auth"));
}
?
@Bean
public Docket sysDocket() {
return docket("系統模塊",
Arrays.asList(AppConstant.BASE_PACKAGES + ".modules.system", AppConstant.BASE_PACKAGES + ".modules.resource"));
}
?
@Bean
public Docket flowDocket() {
return docket("工作流模塊", Collections.singletonList(AppConstant.BASE_PACKAGES + ".flow"));
}
?
}
~~~
* 新寫法如下
~~~
public class SwaggerConfiguration {
?
@Bean
public GroupedOpenApi authApi() {
return GroupedOpenApi.builder()
.group("授權模塊")
.packagesToScan(AppConstant.BASE_PACKAGES + ".modules.auth")
.build();
}
?
@Bean
public GroupedOpenApi sysApi() {
return GroupedOpenApi.builder()
.group("系統模塊")
.packagesToScan(AppConstant.BASE_PACKAGES + ".modules.system", AppConstant.BASE_PACKAGES + ".modules.resource")
.build();
}
?
@Bean
public GroupedOpenApi flowApi() {
// 創建并返回GroupedOpenApi對象
return GroupedOpenApi.builder()
.group("工作流模塊")
.packagesToScan(AppConstant.BASE_PACKAGES + ".flow")
.build();
}
?
}
~~~
8. Swagger文檔訪問地址,springcloud與springboot地址通用
* 文檔地址:[http://localhost/doc.html](http://localhost/doc.html)
9. 由于需要同時生效兩套swagger的ui,我們刪除了blade-swagger服務,重新改為springcloud聚合文檔的實現方式。文檔默認使用knife4j的ui,地址:[http://localhost/doc.html](http://localhost/doc.html),若需要使用swagger-ui,在blade-gateway的pom.xml加入如下配置,然后訪問[http://localhost/swagger-ui.html](http://localhost/swagger-ui.html) 便可
~~~
<dependency>
? ?<groupId>org.springdoc</groupId>
? ?<artifactId>springdoc-openapi-starter-webflux-ui</artifactId>
</dependency>
~~~
10. springboot無需操作,直接訪問 [http://localhost/swagger-ui.html](http://localhost/swagger-ui.html) 便可
### Mybatis-Plus升級
1. mybatis-plus啟動依賴
~~~
<dependency>
? ?<groupId>com.baomidou</groupId>
? ?<artifactId>mybatis-plus-boot-starter</artifactId>
? ?<version>${mybatis.plus.version}</version>
</dependency>
~~~
【替換為】
~~~
<dependency>
? ?<groupId>com.baomidou</groupId>
? ?<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
? ?<version>${mybatis.plus.version}</version>
</dependency>
~~~
2. dynamic-datasource啟動依賴
~~~
<dependency>
? ?<groupId>com.baomidou</groupId>
? ?<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
? ?<version>${mybatis.plus.dynamic.version}</version>
</dependency>
~~~
【替換為】
~~~
<dependency>
? ?<groupId>com.baomidou</groupId>
? ?<artifactId>dynamic-datasource-spring-boot3-starter</artifactId>
? ?<version>${mybatis.plus.dynamic.version}</version>
</dependency>
~~~
3. 由于mybatis-plus依賴的mybatis-spring版本會導致工程無法啟動,需要將mybatis-plus指定適配版本
~~~
<dependency>
? ?<groupId>org.mybatis</groupId>
? ?<artifactId>mybatis-spring</artifactId>
? ?<version>3.0.3</version>
</dependency>
~~~
4. 以上升級均在tool工程處理完畢,大家業務工程無需處理,若有自定義二開tool工程的情況請知悉
### Druid升級
1. druid啟動依賴
~~~
<dependency>
? ?<groupId>com.alibaba</groupId>
? ?<artifactId>druid-spring-boot-starter</artifactId>
</dependency>
~~~
【替換為】
~~~
<dependency>
? ?<groupId>com.alibaba</groupId>
? ?<artifactId>druid-spring-boot-3-starter</artifactId>
</dependency>
~~~
2. 以上升級均在tool工程處理完畢,大家業務工程無需處理,若有自定義二開tool工程的情況請知悉
### Ureport升級
1. 由于ureport還未支持springboot3與jdk17,bladex官方對其進行了升級適配,直接升級tool工程便可以無縫使用
2. 具體源碼請看:https://gitee.com/smallc/blade-tool/tree/master/blade-core-report
## 反射
1. 升級到Java17后,如果未進行模塊化命名,使用反射則會報如下錯誤:`module java.base does not "opens java.lang" to unnamed module)`
2. 從Java 9開始引入的模塊系統增加了對JVM內部API的封裝,以促進Java平臺的封裝性和安全性。默認情況下,Java核心庫中的很多內部API和類都不再對外公開,除非顯式地使用`--add-opens`選項來開放它們。
3. 為了解決老版本源碼使用反射但未模塊化到問題,可以通過在Java命令行中添加`--add-opens`參數來實現,格式如下:
~~~
java --add-opens java.base/java.lang=ALL-UNNAMED -jar your-application.jar
~~~
## JDK17+ZGC
JDK 17引入了很多重要的特性,其中之一是Z Garbage Collector (ZGC) 的正式支持。ZGC是一種可伸縮的垃圾收集器,旨在減少應用程序停頓時間,無論是在小型還是大型堆上。它最初在JDK 11中作為實驗性特性引入,但到了JDK 17,ZGC成為了正式支持的特性。
#### 1\. ZGC的主要特點包括:
* **低延遲**:ZGC的設計目標是將停頓時間限制在幾毫秒內,甚至在大堆上也是如此。
* **可伸縮性**:ZGC旨在處理從幾百兆字節到幾個太字節大小的堆,而不會顯著增加停頓時間。
* **無內存抖動**:ZGC幾乎不產生內存抖動,這對于需要穩定和可預測性能的應用程序來說非常重要。
#### 2\. 使用ZGC
在JDK 17中使用ZGC非常簡單。只需要在啟動Java應用程序時通過命令行參數指定使用ZGC即可。以下是一個示例命令行,它啟動了一個使用ZGC的Java應用程序:
~~~
java -XX:+UseZGC -jar your-application.jar
~~~
這個命令告訴JVM使用ZGC作為垃圾收集器。請注意,使用ZGC可能需要您根據應用程序的具體情況調整其他JVM參數,以獲得最佳性能。
#### 3.示例
假設有一個簡單的Java應用程序,它在運行過程中不斷創建新對象,模擬了一個典型的業務應用負載。在使用ZGC的情況下,可以觀察到即使在高負載下,應用程序的停頓時間也保持在很低的水平。
~~~
public class ZGCDemo {
? ?public static void main(String[] args) {
? ? ? ?List<Object> list = new ArrayList<>();
? ? ? ?while (true) {
? ? ? ? ? ?list.add(new Object());
? ? ? ? ? ?if (list.size() > 10000) {
? ? ? ? ? ? ? ?list.clear();
? ? ? ? ? }
? ? ? }
? }
}
~~~
在這個示例中,我們不斷地創建并清理對象,以模擬一個持續的負載。使用ZGC時,即使這種類型的工作負載可能導致大量的垃圾收集活動,應用程序的停頓時間也可以保持在極低的水平。
請注意,雖然ZGC能夠顯著減少停頓時間,但選擇合適的垃圾收集器還需考慮應用程序的具體需求和工作負載特性。在某些情況下,其他垃圾收集器(如G1 GC)可能更適合您的應用程序。
#### 4\. 推薦使用的ZGC配置
~~~
# 垃圾收集器
# 使用ZGC垃圾收集器
JAVA_OPTS="-XX:+UseZGC"
# 解鎖診斷VM選項
JAVA_OPTS="-XX:+UnlockDiagnosticVMOptions"
# 設置ZGC統計信息的記錄間隔(秒)
JAVA_OPTS="-XX:ZStatisticsInterval=10"
?
# 內存
# 設置JVM最小堆大小為512m
JAVA_OPTS="-Xms512m"
# 設置JVM最大堆大小為1024m
JAVA_OPTS="-Xmx1024m"
# 允許未命名模塊訪問java.base模塊中java.lang包的所有成員
JAVA_OPTS="--add-opens=java.base/java.lang=ALL-UNNAMED"
# 允許未命名模塊訪問java.base模塊中java.util包的所有成員
JAVA_OPTS="--add-opens=java.base/java.util=ALL-UNNAMED"
# 將java.base模塊中sun.security.ssl包的所有成員導出給所有未命名模塊
JAVA_OPTS="--add-exports=java.base/sun.security.ssl=ALL-UNNAMED"
# 允許未命名模塊訪問java.base模塊中sun.security.ssl.internal.ssl包的所有成員
JAVA_OPTS="--add-opens=java.base/sun.security.ssl.internal.ssl=ALL-UNNAMED"
?
# 日志
# 打印出JVM啟動時接收的命令行標志
JAVA_OPTS="-XX:+PrintCommandLineFlags"
# 配置垃圾收集日志,包括日志文件的位置、名稱、記錄的信息(時間、運行時間)、日志文件的數量和大小限制
JAVA_OPTS="-Xlog:gc*:file=${LOG_DIR}/${PROJ_NAME}-gc-%p.log:time,uptime:filecount=10,filesize=50M"
# 在內存溢出時生成堆轉儲
JAVA_OPTS="-XX:+HeapDumpOnOutOfMemoryError"
# 指定堆轉儲文件的路徑和名稱
JAVA_OPTS="-XX:HeapDumpPath=${LOG_DIR}/${PROJ_NAME}-`date +%s`-pid$$.hprof"
# 指定JVM崩潰日志(如JVM錯誤日志)的路徑和名稱
JAVA_OPTS="-XX:ErrorFile=${LOG_DIR}/${PROJ_NAME}-`date +%s`-pid%p.log"
~~~
## 工程啟動準備
1. 由于升級了JDK17,Java 模塊化系統(Java Module System)的安全限制導致的針對反射等場景有可能會出現如下錯誤:
~~~
Cause: java.lang.reflect.InaccessibleObjectException: Unable to make field protected java.lang.reflect.InvocationHandler java.lang.reflect.Proxy.h accessible: module java.base does not "opens java.lang.reflect" to unnamed module @223aa2f7
~~~
2. 這種情況,我們需要在啟動時增加對應配置:
~~~
--add-opens java.base/java.lang.reflect=ALL-UNNAMED
~~~
3. 具體完整命令,格式如下:
~~~shell
java --add-opens java.base/java.lang.reflect=ALL-UNNAMED -jar your-application.jar
~~~
4. 若依舊報錯可以增加第二個參數`--add-opens java.base/java.lang=ALL-UNNAMED`,格式如下
~~~shell
java --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED -jar your-application.jar
~~~
5. 若出現如下錯誤:`module java.base does not "opens java.io" to unnamed module`,則額外增加如下配置
~~~shell
-–add-opens java.base/java.io=ALL-UNNAMED
~~~
6. 若出現如下錯誤:`module java.base does not "opens java.util" to unnamed module`,則額外增加如下配置
~~~shell
-–add-opens java.base/java.util=ALL-UNNAMED
~~~
7. 其他更多錯誤則可以通過搜索工具,搜索具體報錯來查詢具體對應的配置,配置較多本文檔便不再一一列舉
8. 具體操作如下

9. jar包啟動時也需要加入此配置,具體命令如下(增加的命令以最終可運行為準)
~~~
java --add-opens java.base/java.lang.reflect=ALL-UNNAMED -jar your-application.jar
~~~
- 第零章 序
- 序言
- 系統架構
- 視頻公開課
- 開源版介紹
- 商業版介紹
- 功能對比
- 答疑流程
- 第一章 快速開始
- 升級必看
- 環境要求
- 環境準備
- 基礎環境安裝
- Docker安裝基礎服務
- Nacos安裝
- Sentinel安裝
- 插件安裝
- 建數據庫
- 工程導入
- 導入Cloud版本
- 導入Nacos配置
- 導入Boot版本
- 工程運行
- 運行Cloud版本
- 運行Boot版本
- 工程測試
- 測試Cloud版本
- 測試Boot版本
- 第二章 技術基礎
- Java
- Lambda
- Lambda 受檢異常處理
- Stream 簡介
- Stream API 一覽
- Stream API (上)
- Stream API (下)
- Optional 干掉空指針
- 函數式接口
- 新的日期 API
- Lombok
- SpringMVC
- Swagger
- Mybatis
- Mybatis-Plus
- 開發規范
- 第三章 開發初探
- 新建微服務工程
- 第一個API
- API鑒權
- API響應結果
- Redis緩存
- 第一個CRUD
- 建表
- 建Entity
- 建Service和Mapper
- 新增 API
- 修改 API
- 刪除 API
- 查詢 API
- 單條數據
- 多條數據
- 分頁
- 微服務遠程調用
- 聲明式服務調用 Feign
- 熔斷機制 Hystrix
- 第四章 開發進階
- 聚合文檔
- 鑒權配置
- 跨域處理
- Xss防注入
- 自定義啟動器
- Secure安全框架
- Token認證簡介
- Token認證配置
- PreAuth注解配置
- Token認證實戰
- Token認證加密
- 日志系統
- 原理解析
- 功能調用
- Seata分布式事務
- 簡介
- 編譯包啟動
- 配置nacos對接
- docker啟動
- 對接微服務
- 代碼生成配置
- 前言
- 數據庫建表
- 代碼生成
- 前端配置
- 優化效果
- 第五章 功能特性
- SaaS多租戶
- 概念
- 數據隔離配置
- 線程環境自定義租戶ID
- 多終端令牌認證
- 概念
- 系統升級
- 使用
- 第三方系統登錄
- 概念說明
- 對接說明
- 對接準備
- 配置說明
- 操作流程
- 后記
- UReport2報表
- 報表簡介
- 對接配置
- 報表后記
- 接口報文加密
- 簡介
- 運行邏輯
- 對接準備
- 功能配置
- 接口測試
- 改造查詢
- 改造提交
- 改造刪除
- 動態數據權限
- 數據權限簡介
- 數據權限開發
- 純注解配置
- Web全自動配置
- 注解半自動配置
- 數據權限注意點
- 動態接口權限
- 樂觀鎖配置
- 統一服務登陸配置
- Skywalking追蹤監控
- Minio分布式對象存儲
- Boot版本對接至Cloud
- 第六章 生產部署
- windows部署
- linux部署
- jar部署
- docker部署
- java環境安裝
- mysql安裝
- docker安裝
- docker-compose安裝
- harbor安裝
- 部署步驟
- 寶塔部署
- 準備工作
- 安裝工作
- 部署準備
- 部署后端
- 部署前端
- 部署域名
- 結束工作
- k8s平臺部署
- 第七章 版本控制
- Git遠程分支合并
- Git地址更換
- 第八章 學習資料
- 第九章 FAQ
- 第十章 聯系我們