0


SpringBoot2.7升级项目到Springboot3.1踩坑指南(jdk17/jdk21)

文章目录

概要

由于SpringBoot3.x全面拥抱JDK17,兼容jdk21,jdk17乃是大势所趋。这里是从SpringBoot2.7-->SpringBoot3.1踩坑指南。

提前阅读:jdk8升级JDK17避坑指南(适用于SpringBoot2.3—SpringBoot2.7升级)

国内顶级开源项目升级情况

 国内顶级开源项目升级到springBoot3情况,可以作为升级SpringBoot3的风向标。仅对比国内规模使用,落地过万企业的开源项目

参考:国内顶级开源项目:芋道、ruoyi、JeecgBoot、pig、SpringBlade功能对比
评价项/项目名yudao-cloudRuoyi-CloudRuoYi-Cloud-PlusDante CloudpigbladexJeecgBoot官网芋道yudao-cloud 开发指南若依plus-doc.dromara.orgDante Cloudpig4cloudbladex.cn、看云-SpringBlade开发手册JeecgBoot源码收费免费免费免费免费免费 + 收费(3999)免费 + 收费(5000)免费 + 收费(100000)文档收费文档收费免费、视频收费文档免费、视频收费免费免费、授权收费文档收费文档免费、授权收费githubyudao-cloudRuoYiRuoYi-Vue-PlusDante CloudpigSpringBladejeecg-bootgiteeyudao-cloudRuoYiRuoYi-Vue-PlusDante Cloud暂无SpringBladejeecg-bootjdk17分支master-jdk21RuoYi-Cloud-Plus 2.Xdante-cloud 3.1.Xpig jdk17jeecg-boot/springboot3

适配SpringBoot3指南

  • 参考1-微信公众号-这可能是最全的SpringBoot3新版本变化了!、
  • 参考2-SpringBoot官网-Spring Boot 3.0 Release Notes、
  • 参考3-微信公众号-Swagger升级指南:Swagger2与Swagger3注解差异揭秘、
  • 参考4-微信公众号-Dante Cloud 3.2.0.0 发布,首个适配 Spring Boot 3.2版本及经验分享
  • 参考5-JeecgBoot 文档中心-升级SpringBoot3

SpringBoot3升级要点

前提说明,建议先完成springboot2.x—>springBoot2.7.x+jdk17的适配,这里升级难度会小很多。参考:文章最前面的文章。

1、jdk17变动(如javax)

详见: jdk8升级JDK17避坑指南(适用于SpringBoot2.3—SpringBoot2.7升级)

  • 模块化对反射的影响==>对系统类的反射增加了限制,需要打开限制增加jvm启动参数add-opens,自己写的类,可以正常使用反射。
  • 删除sun.misc 下的包,如sun.misc.BASE64Encoder==>java.util.Base64替换
  • 删除JAXB、soup相关==>maven仓库上面有新的maven坐标,引入新依赖即可
  • 删除javax.annotation==>maven仓库上面有新的maven坐标,引入依赖即可

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.hostspring:data:redis:host: 10.16.58.180
      port:6379password: Admin123
      database:6

3、SpringCloudApplication注解被删除

使用@SpringBootApplication替换

4、不兼容升级import java.servlet====>import jakarta.servlet

servlet捐献给社区,为了避免版权问题,修改了包名,导致不兼容.

  • 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,相关注解进行了变化。

参考:spring boot 3 整合 swagger3、
参考:Swagger升级指南:Swagger2与Swagger3注解差异揭秘、
参考:OpenApi3.0注解说明
注解作用swagger2swagger2示例swagger3-openApi3.0swagger示例替换用于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 = "新增儿童档案绑定监护人", httpMethod = "POST", produces = "application/json")

@Operation

 @Operation(summary = "新增儿童档案绑定监护人", method = "POST")
@ApiOperation(value = "根据儿童证件号码查询儿童档案", httpMethod = "POST", produces = "application/json")

–>

@Operation(summary = "根据儿童证件号码查询儿童档案", method = "POST")

用于Controller接口参数注解@ApiParam或@ApiImplicitParam

@ApiParam("预约id,字段名:personApptId,默认无")

@Parameter

@Parameter(description = "预约id,字段名:personApptId,默认无", required = true) 
@ApiParam(value = "追溯码 形如:81900920216939751445,max=32", defaultValue = "81900920216939751445")

—>

@Parameter(description = "追溯码 形如:81900920216939751445,max=32", example = "81900920216939751445")

参数隐藏@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 = "根据儿童证件号码查询儿童档案")")
@ApiModel(value

—>

@Schema(description

@ApiModel(description 

—>

@Schema(description
// idea正则替换01importio.swagger.annotations.Api;importio.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正则替换02importio.swagger.annotations.ApiOperation;importio.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正则替换03importio.swagger.annotations.ApiModel;importio.swagger.v3.oas.annotations.media.Schema;@ApiModel\(value ="([^\"]+)"\)@Schema(description ="$1")@ApiModel\(description ="([^\"]+)"\)@Schema(description ="$1")// idea正则替换04importio.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正则替换05importio.swagger.annotations.ApiParam;importio.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正则替换06importspringfox.documentation.annotations.ApiIgnore;importio.swagger.v3.oas.annotations.Hidden;@ApiIgnore@Hidden@ApiIgnoreHttpServletResponse response
@ApiIgnoreHttpServletRequest 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 ="根据儿童证件号码查询儿童档案")publicclassChildGetByChildNoVoimplementsSerializable{@ApiModelProperty(value ="身份证")privateString childNo;privateLong personId;}// Swagger2 Controller@AllArgsConstructor@RestController@RequestMapping("/app/child/v2")@Api(value ="/app/child/v2", tags ="儿童档案")publicclassChildControllerextendsBaseController@ApiOperation(value ="根据监护人ID分页查询被监护人", httpMethod ="GET", produces ="application/json")@GetMapping("/getByPersonId")@ApiImplicitParam(value ="预约id")publicAjaxResultgetByPersonId(@RequestParam(value ="pageNum",required =false,defaultValue ="1")Integer pageNum,@ApiParam("预约id,字段名:personApptId,默认无")@RequestParam(value ="personApptId")Long personApptIdpersonId,@ApiIgnoreHttpServletRequest request
                                   ){IPage<ApptChildRelationship> page =newPage<>(pageNum,pageSize);IPage<ChildApiVo> pageData = childService.getByPersonId(page,personId);TableDataInfo rspData =newTableDataInfo();
        rspData.setCode(200);
        rspData.setRows(pageData.getRecords());
        rspData.setMsg("查询成功");
        rspData.setTotal(pageData.getTotal());returnAjaxResult.success(rspData);}@PostMapping("/getCode")@Operation(summary ="获取预约码", method ="POST")@ApiImplicitParam(value ="预约id")@ApiImplicitParams({@ApiImplicitParam(name ="personId", value ="用户id", paramType ="form"),@ApiImplicitParam(name ="apptId", value ="预约id", paramType ="form"),@ApiImplicitParam(name ="collectLocationId", value ="采样点id", paramType ="form"),})publicStringgetCode(Long personId,Long apptId,Long collectLocationId){return vficPersonService.generateApptQrCode(apptId, personId, collectLocationId);}}

swagger3示例

6、动态数据源baomidou的dynamic-datasource依赖变动

经过测试,该条也可以不升级
参考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发现依赖提示缺少: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时出现未能加载bean文件

springboot2.x使用rocketmq没有问题,springboot3出现,required a bean of type ‘org.apache.rocketmq.spring.core.RocketMQTemplate’ that could not be found.

参考1:SpringBoot3.0整合RocketMQ时出现未能加载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})publicclassRocketMQConfig{}

9、springboot3默认依赖 elasticsearch从7.x升级到8.x

  • 配置文件地址修改:spring.elasticsearch.rest.uris==>spring.elasticsearch.uris
  • springboot3要求elasticsearch必须升级到8.x,不能使用7.x,否则报错:缺失响应头X-Elastic-Product(Elasticsearch)
  • 废弃:high client客户端
  • 要求ElasticSearch必须8.x

10、springboot 3.2 openFeign加载失败暂未解决 (3.1没问题)

启动失败 feign导致 not annotated with HTTP method type (ex. GET, POST)

11、hutool5.8–>hutool6.0升级要要点

说明常见类,如果找不到,请看源码注释,注释上面写的有,或者全局搜索即可,无需多说。

  • ServletUtil–>JakartaServletUtil
  • hutool类变化说明: 【6.0.0】升级到6.0不兼容情况汇总,大家一起来统计维护呀、
  • 国密相关SmUtil,使用6.0x问题,参加:hutool 5.x 和 hutool 6.x crypto 国密SMUtil 模块放在一个工程中会产生冲突

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 问题

最新已经是

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标记的方法,只能返还void或者Future(@Async+Future+AsyncResult返回值)

参考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

标签: jdk17 springboot

本文转载自: https://blog.csdn.net/ab601026460/article/details/135441258
版权归原作者 Dazer007 所有, 如有侵权,请联系我们删除。

“SpringBoot2.7升级项目到Springboot3.1踩坑指南(jdk17/jdk21)”的评论:

还没有评论