概要
由于SpringBoot3.x全面擁抱JDK17,兼容jdk21,jdk17乃是大勢所趨。這里是從SpringBoot2.7-->SpringBoot3.1踩坑指南。
提前閱讀:jdk8升級JDK17避坑指南(適用于SpringBoot2.3—SpringBoot2.7升級)
國內(nèi)頂級開源項目升級情況
國內(nèi)頂級開源項目升級到springBoot3情況,可以作為升級SpringBoot3的風(fēng)向標(biāo)。僅對比國內(nèi)規(guī)模使用,落地過萬企業(yè)的開源項目
參考:國內(nèi)頂級開源項目:芋道、ruoyi、JeecgBoot、pig、SpringBlade功能對比
評價項/項目名 | yudao-cloud | Ruoyi-Cloud | RuoYi-Cloud-Plus | Dante Cloud | pig | bladex | JeecgBoot |
---|---|---|---|---|---|---|---|
官網(wǎng) | 芋道yudao-cloud 開發(fā)指南 | 若依 | plus-doc.dromara.org | Dante Cloud | pig4cloud | bladex.cn、看云-SpringBlade開發(fā)手冊 | JeecgBoot |
源碼收費 | 免費 | 免費 | 免費 | 免費 | 免費 + 收費(3999) | 免費 + 收費(5000) | 免費 + 收費(100000) |
文檔收費 | 文檔收費 | 免費、視頻收費 | 文檔免費、視頻收費 | 免費 | 免費、授權(quán)收費 | 文檔收費 | 文檔免費、授權(quán)收費 |
github | yudao-cloud | RuoYi | RuoYi-Vue-Plus | Dante Cloud | pig | SpringBlade | jeecg-boot |
gitee | yudao-cloud | RuoYi | RuoYi-Vue-Plus | Dante Cloud | 暫無 | SpringBlade | jeecg-boot |
jdk17分支 | master-jdk21 | RuoYi-Cloud-Plus 2.X | dante-cloud 3.1.X | pig jdk17 | jeecg-boot/springboot3 |
適配SpringBoot3指南
- 參考1-微信公眾號-這可能是最全的SpringBoot3新版本變化了!、
- 參考2-SpringBoot官網(wǎng)-Spring Boot 3.0 Release Notes、
- 參考3-微信公眾號-Swagger升級指南:Swagger2與Swagger3注解差異揭秘、
- 參考4-微信公眾號-Dante Cloud 3.2.0.0 發(fā)布,首個適配 Spring Boot 3.2版本及經(jīng)驗分享
- 參考5-JeecgBoot 文檔中心-升級SpringBoot3
SpringBoot3升級要點
前提說明,建議先完成springboot2.x—>springBoot2.7.x+jdk17的適配,這里升級難度會小很多。參考:文章最前面的文章。
1、jdk17變動(如javax)
詳見: jdk8升級JDK17避坑指南(適用于SpringBoot2.3—SpringBoot2.7升級)
-
模塊化對反射的影響==>對系統(tǒng)類的反射增加了限制,需要打開限制增加jvm啟動參數(shù)
add-opens
,自己寫的類,可以正常使用反射。 -
刪除sun.misc 下的包,如sun.misc.BASE64Encoder==>
java.util.Base64
替換 -
刪除JAXB、soup相關(guān)==>maven倉庫上面有新的maven坐標(biāo),
引入新依賴即可
-
刪除javax.annotation==>maven倉庫上面有新的maven坐標(biāo),
引入依賴即可
2、redis修改spring.redis.host ===> spring.data.redis.host
redis配置命令空間進行了修改,需要注意。
參考:Spring Boot3.0(九):整合Redis
--- # redis 配置,注意springboot 3.x 有 data,2.x 沒有 data
## spring.redis.host ===> spring.data.redis.host
spring:
data:
redis:
host: 10.16.58.180
port: 6379
password: Admin123
database: 6
3、SpringCloudApplication注解被刪除
使用@SpringBootApplication替換
4、不兼容升級import java.servlet====>import jakarta.servlet
servlet捐獻給社區(qū),為了避免版權(quán)問題,修改了包名,導(dǎo)致不兼容.
- javax.servlet===> jakarta.servlet
- javax.validation ===> jakarta.validation
- javax.annotation ===> jakarta.annotation
- javax.mail ===> jakarta.mail
- javax.websocket ==> jakarta.websocket
<!-- Java Servlet jakarta -->
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>6.0.0</version>
</dependency>
5、swagger集成 棄用springfox—>springdoc不兼容升級
springfox不維護了,springboot3使用springdoc,并啟用openapi3.0,相關(guān)注解進行了變化。
參考:spring boot 3 整合 swagger3、
參考:Swagger升級指南:Swagger2與Swagger3注解差異揭秘、
參考:OpenApi3.0注解說明
注解作用 | swagger2 | swagger2示例 | swagger3-openApi3.0 | swagger示例 | 替換 |
---|---|---|---|---|---|
用于Controller | @Api | @Api(value = "/app/child/v2", tags = "兒童檔案") |
@Tag | @Tag(name = "/app/child/v2", description = "兒童檔案") |
@Api(value = "User Management", description = "Operations pertaining to users") —>@Tag(name = "User Management", description = "Operations pertaining to users") 、@Api(tags = "小程序端Core Controller") —>@Tag(description = "小程序端Core Controller")
|
用于Controller接口 | @ApiOperation | @ApiOperation(value = "新增兒童檔案綁定監(jiān)護人", httpMethod = "POST", produces = "application/json") |
@Operation | @Operation(summary = "新增兒童檔案綁定監(jiān)護人", method = "POST") |
@ApiOperation(value = "根據(jù)兒童證件號碼查詢兒童檔案", httpMethod = "POST", produces = "application/json") –>@Operation(summary = "根據(jù)兒童證件號碼查詢兒童檔案", method = "POST")
|
用于Controller接口參數(shù)注解 | @ApiParam或@ApiImplicitParam | @ApiParam("預(yù)約id,字段名:personApptId,默認無") |
@Parameter | @Parameter(description = "預(yù)約id,字段名:personApptId,默認無", required = true) |
@ApiParam(value = "追溯碼 形如:81900920216939751445,max=32", defaultValue = "81900920216939751445") —>@Parameter(description = "追溯碼 形如:81900920216939751445,max=32", example = "81900920216939751445")
|
參數(shù)隱藏 | @ApiIgnore | @ApiIgnore HttpServletRequest request |
/ | / | / |
實體字段 | @ApiModelProperty |
@ApiModelProperty("接種人員-姓名(冗),max=32") 、@ApiModelProperty(notes = "The database generated user ID")
|
@Schema | @Schema(description = "主鍵 自增") |
@ApiModelProperty(value —>@Schema(description 、@ApiModelProperty(hidden = true) –>@Schema(hidden = true 、@ApiModelProperty(value = "更新人員", example = "張三",hidden = true) —>@Schema(description = "更新人員", example = "張三",hidden = true)
|
實體類 | @ApiModel |
@ApiModel("接種人員-姓名(冗),max=32") 、@ApiModelProperty(notes = "The database generated user ID")
|
@Schema | @Schema (description = "根據(jù)兒童證件號碼查詢兒童檔案")") |
@ApiModel(value —>@Schema(description 、@ApiModel(description —>@Schema(description
|
// idea正則替換01
import io.swagger.annotations.Api;
import io.swagger.v3.oas.annotations.tags.Tag;
@Api\(tags = "([^\"]+)", hidden = ([^\"]+), description = "([^\"]+)"\)
@Tag(name = "$1", description = "$3")
@Api\(value = "([^\"]+)", tags = "([^\"]+)"\)
@Tag(name = "$1", description = "$2")
@Api\(tags = "([^\"]+)", description = "([^\"]+)"\)
@Tag(name = "$1", description = "$2")
@Api\(tags = "([^\"]+)"\)
@Tag(name = "$1")
// idea正則替換02
import io.swagger.annotations.ApiOperation;
import io.swagger.v3.oas.annotations.Operation;
@ApiOperation\(value = "([^\"]+)", notes = "([^\"]+)", position = ([^\"]+)\)
@Operation(summary = "$1", description= "$2")
@ApiOperation\(value = "([^\"]+)", httpMethod = "([^\"]+)", produces = "([^\"]+)"\)
@Operation(summary = "$1", method = "$2")
@ApiOperation\(value = "([^\"]+)", notes = "([^\"]+)"\)
@Operation(summary = "$1", description = "$2")
@ApiOperation\(value = "([^\"]+)", httpMethod = "([^\"]+)"\)
@Operation(summary = "$1", method = "$2")
@ApiOperation\(value = "([^\"]+)"\)
@ApiOperation\("([^\"]+)"\)
@Operation(summary = "$1")
// idea正則替換03
import io.swagger.annotations.ApiModel;
import io.swagger.v3.oas.annotations.media.Schema;
@ApiModel\(value = "([^\"]+)"\)
@Schema(description = "$1")
@ApiModel\(description = "([^\"]+)"\)
@Schema(description = "$1")
// idea正則替換04
import io.swagger.annotations.ApiModelProperty;
'';
@ApiModelProperty\(value = "([^\"]+)", example = "([^\"]+)", required = ([^\"]+)\)
@Schema(description = "$1", example = "$2", required= $3)
@ApiModelProperty\(value = "([^\"]+)", example = "([^\"]+)", hidden = ([^\"]+)\)
@Schema(description = "$1", example = "$2", hidden = $3)
@ApiModelProperty\(value = "([^\"]+)", example = "([^\"]+)"\)
@Schema(description = "$1", example = "$2")
@ApiModelProperty\(value = "([^\"]+)", hidden = ([^\"]+)\)
@Schema(description = "$1", hidden = $2)
@ApiModelProperty\(value = "([^\"]+)"\)
@Schema(description = "$1")
@ApiModelProperty\(hidden = ([^\"]+)\)
@Schema(hidden = $1)
@ApiModelProperty\("([^\"]+)"\)
@Schema(description = "$1")
@ApiModelProperty\(value="([^\"]+)"\)
@Schema(description = "$1")
// idea正則替換05
import io.swagger.annotations.ApiParam;
import io.swagger.v3.oas.annotations.Parameter;
@ApiParam\(value = "([^\"]+)", example = "([^\"]+)", defaultValue = "([^\"]+)"\)
@Parameter(name = "$1", example = "$2", description="$3")
@ApiParam\(value = "([^\"]+)", defaultValue = "([^\"]+)"\)
@ApiParam\(value = "([^\"]+)", example = "([^\"]+)"\)
@Parameter(name = "$1", example = "$2")
@ApiParam\(value = "([^\"]+)"\)
@ApiParam\("([^\"]+)"\)
@Parameter(name = "$1")
// idea正則替換06
import springfox.documentation.annotations.ApiIgnore;
import io.swagger.v3.oas.annotations.Hidden;
@ApiIgnore
@Hidden
@ApiIgnore HttpServletResponse response
@ApiIgnore HttpServletRequest request
// idea正則替換07
@ApiImplicitParams
@Parameters
@ApiImplicitParam\(name = "([^\"]+)", value = "([^\"]+)",required = ([^\"]+), example = "([^\"]+)", paramType = "query"\)
@Parameter(name = "$1", description = "$2", required = $3, example = "$4", in = ParameterIn.QUERY)
@ApiImplicitParam\(name = "([^\"]+)", value = "([^\"]+)", required = ([^\"]+), paramType = "query"\)
@Parameter(name = "$1", description = "$2", required = true, in = ParameterIn.QUERY)
@ApiImplicitParam\(name = "([^\"]+)", value = "([^\"]+)", dataType = "([^\"]+)", required = ([^\"]+)\)
@Parameter(name = "$1", description = "$2", schema = @Schema(type = "$3"), required = $4)
@ApiImplicitParam\(name = "([^\"]+)", value = "([^\"]+)", dataType = "([^\"]+)"\)
@Parameter(name = "$1", description = "$2", schema = @Schema(type = "$3"))
@ApiImplicitParam\(name = "([^\"]+)", value = "([^\"]+)", paramType = "form"\)
@Parameter(name = "$1", description = "$2", in = ParameterIn.QUERY)
@ApiImplicitParam\(name = "([^\"]+)", value = "([^\"]+)", dataType = "__file", paramType = "form"\)
@Parameter(name = "$1", description = "$2", example = "__file", in = ParameterIn.QUERY)
@ApiImplicitParam\(value = "([^\"]+)"\)
@Parameter(description = "$2")
swaggr2實例
// Swagger2 實體類
@Getter
@Setter
@NoArgsConstructor
@ApiModel(value = "根據(jù)兒童證件號碼查詢兒童檔案")
public class ChildGetByChildNoVo implements Serializable {
@ApiModelProperty(value = "身份證")
private String childNo;
private Long personId;
}
// Swagger2 Controller
@AllArgsConstructor
@RestController
@RequestMapping("/app/child/v2")
@Api(value = "/app/child/v2", tags = "兒童檔案")
public class ChildController extends BaseController
@ApiOperation(value = "根據(jù)監(jiān)護人ID分頁查詢被監(jiān)護人", httpMethod = "GET", produces = "application/json")
@GetMapping("/getByPersonId")
@ApiImplicitParam(value = "預(yù)約id")
public AjaxResult getByPersonId(@RequestParam(value = "pageNum",required = false,defaultValue = "1") Integer pageNum,
@ApiParam("預(yù)約id,字段名:personApptId,默認無") @RequestParam(value = "personApptId") Long personApptIdpersonId,
@ApiIgnore HttpServletRequest request
){
IPage<ApptChildRelationship> page = new Page<>(pageNum,pageSize);
IPage<ChildApiVo> pageData = childService.getByPersonId(page,personId);
TableDataInfo rspData = new TableDataInfo();
rspData.setCode(200);
rspData.setRows(pageData.getRecords());
rspData.setMsg("查詢成功");
rspData.setTotal(pageData.getTotal());
return AjaxResult.success(rspData);
}
@PostMapping("/getCode")
@Operation(summary = "獲取預(yù)約碼", method = "POST")
@ApiImplicitParam(value = "預(yù)約id")
@ApiImplicitParams({
@ApiImplicitParam(name = "personId", value = "用戶id", paramType = "form"),
@ApiImplicitParam(name = "apptId", value = "預(yù)約id", paramType = "form"),
@ApiImplicitParam(name = "collectLocationId", value = "采樣點id", paramType = "form"),
})
public String getCode(Long personId, Long apptId, Long collectLocationId) {
return vficPersonService.generateApptQrCode(apptId, personId, collectLocationId);
}
}
swagger3示例
6、動態(tài)數(shù)據(jù)源baomidou的dynamic-datasource依賴變動
經(jīng)過測試,該條也可以不升級
參考1:baomidou dynamic-datasource、
參考2:kancloud tracy5546 dynamic-datasource
<!-- spring-boot 1.5.x 2.x.x 3.xx -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>${version}</version>
</dependency>
<!-- spring-boot 3.x.x -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot3-starter</artifactId>
<version>${version}</version>
</dependency>
7、Spring Framework 6.0 中刪除了對 Apache HttpClient 支持(RestTemplate受影響)
升級到SpringBoot3發(fā)現(xiàn)依賴提示缺少:rg.apache.httpcomponents:httpclient,一些三方庫可能依賴httpclient,就需要自己手工引入依賴。三方包如:spring-data-elasticsearch5、nacos-client 1.4.6、weixin-java-pay 4.0.0、htmlunit 3.6
參考: Spring-Boot-3.0-Migration-Guide#apache-httpclient-in-resttemplate、
<!-- spring framwork 5.x中默認引入apcache httpclient4,在spring framwork6.x中如果需要要手工引入 -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.14</version>
</dependency>
<!-- spring framwork 6.x中的apcache httpclient5 -->
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<version>5.3</version>
</dependency>
8、SpringBoot3.0整合RocketMQ時出現(xiàn)未能加載bean文件
springboot2.x使用rocketmq沒有問題,springboot3出現(xiàn),required a bean of type ‘org.apache.rocketmq.spring.core.RocketMQTemplate’ that could not be found.
參考1:SpringBoot3.0整合RocketMQ時出現(xiàn)未能加載bean文件
參考2:Spring boot 3.0整合RocketMQ及不兼容的問題
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId>
<!-- 目前也沒有新版本,roketmq2.2.3沒有適配springboot3-->
<version>2.2.3</version>
</dependency>
// 定義個配置類,引入也可以:
@Configuration
@Import({RocketMQAutoConfiguration.class})
public class RocketMQConfig {
}
9、springboot3默認依賴 elasticsearch從7.x升級到8.x
- 配置文件地址修改:
spring.elasticsearch.rest.uris
==>spring.elasticsearch.uris
- springboot3要求elasticsearch必須升級到8.x,不能使用7.x,否則報錯:
缺失響應(yīng)頭X-Elastic-Product(Elasticsearch)
- 廢棄:high client客戶端
- 要求ElasticSearch必須8.x
10、springboot 3.2 openFeign加載失敗暫未解決 (3.1沒問題)
啟動失敗 feign導(dǎo)致 not annotated with HTTP method type (ex. GET, POST)
- https://blog.csdn.net/nailsoul/article/details/105223740
- https://www.jianshu.com/p/11b4cbc8951a
- https://juejin.cn/post/7112414513550491656
11、hutool5.8–>hutool6.0升級要要點
說明常見類,如果找不到,請看源碼注釋,注釋上面寫的有,或者全局搜索即可,無需多說。
-
ServletUtil
–>JakartaServletUtil
- hutool類變化說明: 【6.0.0】升級到6.0不兼容情況匯總,大家一起來統(tǒng)計維護呀、
- 國密相關(guān)SmUtil,使用6.0x問題,參加:hutool 5.x 和 hutool 6.x crypto 國密SMUtil 模塊放在一個工程中會產(chǎn)生沖突
12、Centos7使用jdk21報錯
java: /lib64/libc.so.6: version `GLIBC_2.14’ not found (required by /usr/local/java/jdk-21.0.2+13/bin/…/lib/libjli.so)
原因,Centos6缺失GLIBC_2.14,報錯缺失2.14、2.15解決辦法相同。
只需要安裝最高版本,自動安裝低版本,比如:安裝2.17.自動安裝2.14
參考1:version `GLIBC_2.14’ not found 問題解決
參考2:解決 /lib64/libc.so.6: version `GLIBC_2.15’ not found 問題
最新已經(jīng)是2.38
了,可以直接安裝:wget --no-check-certificate https://ftp.gnu.org/gnu/glibc/glibc-2.38.tar.gz
,安裝前確保安裝的有gcc
。
- Centos6 推薦2.17
13、@Async注解報錯Invalid return type for async method (only Future and void
分析原因,springboot2.7一切正常,升級到springboot3.x@Async標(biāo)記的方法,只能返還void或者Future(@Async+Future+AsyncResult返回值)文章來源:http://www.zghlxwxcb.cn/news/detail-837559.html
參考1:spring 升級到JDK 17和Sping Boot 3后,異步僅接受Future和void作為返回類型
參考2:Async only accepted Future and void as return type after upgrade to JDK 17 and Spring Boot 3文章來源地址http://www.zghlxwxcb.cn/news/detail-837559.html
到了這里,關(guān)于SpringBoot2.7升級項目到Springboot3.1踩坑指南(jdk17/jdk21)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!