由于SpringBoot3.x全面拥抱JDK17,兼容jdk21,jdk17乃是大势所趋。这里是从SpringBoot2.7-->SpringBoot3.1踩坑指南。
提前阅读:jdk8升级JDK17避坑指南(适用于SpringBoot2.3—SpringBoot2.7升级)
国内顶级开源项目升级到springBoot3情况,可以作为升级SpringBoot3的风向标。仅对比国内规模使用,落地过万企业的开源项目
参考:国内顶级开源项目:芋道、ruoyi、JeecgBoot、pig、SpringBlade功能对比
评价项/项目名 | yudao-cloud | Ruoyi-Cloud | RuoYi-Cloud-Plus | Dante Cloud | pig | bladex | JeecgBoot |
---|---|---|---|---|---|---|---|
官网 | 芋道yudao-cloud 开发指南 | 若依 | plus-doc.dromara.org | Dante Cloud | pig4cloud | bladex.cn、看云-SpringBlade开发手册 | JeecgBoot |
源码收费 | 免费 | 免费 | 免费 | 免费 | 免费 + 收费(3999) | 免费 + 收费(5000) | 免费 + 收费(100000) |
文档收费 | 文档收费 | 免费、视频收费 | 文档免费、视频收费 | 免费 | 免费、授权收费 | 文档收费 | 文档免费、授权收费 |
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 |
前提说明,建议先完成springboot2.x—>springBoot2.7.x+jdk17的适配,这里升级难度会小很多。参考:文章最前面的文章。
详见: jdk8升级JDK17避坑指南(适用于SpringBoot2.3—SpringBoot2.7升级)
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
使用@SpringBootApplication替换
servlet捐献给社区,为了避免版权问题,修改了包名,导致不兼容.
jakarta.servlet jakarta.servlet-api 6.0.0
springfox不维护了,springboot3使用springdoc,并启用openapi3.0,相关注解进行了变化。
参考: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 = "新增儿童档案绑定监护人", 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正则替换01 import io.swagger.annotations.Api; import io.swagger.v3.oas.annotations.tags.Tag; @Api\(tags = "([^\"]+)", hidden = ([^\"]+), description = "([^\"]+)"\) @Tag(name = "", description = "") @Api\(value = "([^\"]+)", tags = "([^\"]+)"\) @Tag(name = "", description = "") @Api\(tags = "([^\"]+)", description = "([^\"]+)"\) @Tag(name = "", description = "") @Api\(tags = "([^\"]+)"\) @Tag(name = "") // idea正则替换02 import io.swagger.annotations.ApiOperation; import io.swagger.v3.oas.annotations.Operation; @ApiOperation\(value = "([^\"]+)", notes = "([^\"]+)", position = ([^\"]+)\) @Operation(summary = "", description= "") @ApiOperation\(value = "([^\"]+)", httpMethod = "([^\"]+)", produces = "([^\"]+)"\) @Operation(summary = "", method = "") @ApiOperation\(value = "([^\"]+)", notes = "([^\"]+)"\) @Operation(summary = "", description = "") @ApiOperation\(value = "([^\"]+)", httpMethod = "([^\"]+)"\) @Operation(summary = "", method = "") @ApiOperation\(value = "([^\"]+)"\) @ApiOperation\("([^\"]+)"\) @Operation(summary = "") // idea正则替换03 import io.swagger.annotations.ApiModel; import io.swagger.v3.oas.annotations.media.Schema; @ApiModel\(value = "([^\"]+)"\) @Schema(description = "") @ApiModel\(description = "([^\"]+)"\) @Schema(description = "") // idea正则替换04 import io.swagger.annotations.ApiModelProperty; ''; @ApiModelProperty\(value = "([^\"]+)", example = "([^\"]+)", required = ([^\"]+)\) @Schema(description = "", example = "", required= ) @ApiModelProperty\(value = "([^\"]+)", example = "([^\"]+)", hidden = ([^\"]+)\) @Schema(description = "", example = "", hidden = ) @ApiModelProperty\(value = "([^\"]+)", example = "([^\"]+)"\) @Schema(description = "", example = "") @ApiModelProperty\(value = "([^\"]+)", hidden = ([^\"]+)\) @Schema(description = "", hidden = ) @ApiModelProperty\(value = "([^\"]+)"\) @Schema(description = "") @ApiModelProperty\(hidden = ([^\"]+)\) @Schema(hidden = ) @ApiModelProperty\("([^\"]+)"\) @Schema(description = "") @ApiModelProperty\(value="([^\"]+)"\) @Schema(description = "") // idea正则替换05 import io.swagger.annotations.ApiParam; import io.swagger.v3.oas.annotations.Parameter; @ApiParam\(value = "([^\"]+)", example = "([^\"]+)", defaultValue = "([^\"]+)"\) @Parameter(name = "", example = "", description="") @ApiParam\(value = "([^\"]+)", defaultValue = "([^\"]+)"\) @ApiParam\(value = "([^\"]+)", example = "([^\"]+)"\) @Parameter(name = "", example = "") @ApiParam\(value = "([^\"]+)"\) @ApiParam\("([^\"]+)"\) @Parameter(name = "") // 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 = "", description = "", required = , example = "", in = ParameterIn.QUERY) @ApiImplicitParam\(name = "([^\"]+)", value = "([^\"]+)", required = ([^\"]+), paramType = "query"\) @Parameter(name = "", description = "", required = true, in = ParameterIn.QUERY) @ApiImplicitParam\(name = "([^\"]+)", value = "([^\"]+)", dataType = "([^\"]+)", required = ([^\"]+)\) @Parameter(name = "", description = "", schema = @Schema(type = ""), required = ) @ApiImplicitParam\(name = "([^\"]+)", value = "([^\"]+)", dataType = "([^\"]+)"\) @Parameter(name = "", description = "", schema = @Schema(type = "")) @ApiImplicitParam\(name = "([^\"]+)", value = "([^\"]+)", paramType = "form"\) @Parameter(name = "", description = "", in = ParameterIn.QUERY) @ApiImplicitParam\(name = "([^\"]+)", value = "([^\"]+)", dataType = "__file", paramType = "form"\) @Parameter(name = "", description = "", example = "__file", in = ParameterIn.QUERY) @ApiImplicitParam\(value = "([^\"]+)"\) @Parameter(description = "")
swaggr2实例
// Swagger2 实体类 @Getter @Setter @NoArgsConstructor @ApiModel(value = "根据儿童证件号码查询儿童档案") 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 = "根据监护人ID分页查询被监护人", httpMethod = "GET", produces = "application/json") @GetMapping("/getByPersonId") @ApiImplicitParam(value = "预约id") public AjaxResult getByPersonId(@RequestParam(value = "pageNum",required = false,defaultValue = "1") Integer pageNum, @ApiParam("预约id,字段名:personApptId,默认无") @RequestParam(value = "personApptId") Long personApptIdpersonId, @ApiIgnore HttpServletRequest request ){ IPagepage = new Page<>(pageNum,pageSize); IPage 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 = "获取预约码", 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"), }) public String getCode(Long personId, Long apptId, Long collectLocationId) { return vficPersonService.generateApptQrCode(apptId, personId, collectLocationId); } }
swagger3示例
经过测试,该条也可以不升级
参考1:baomidou dynamic-datasource、
参考2:kancloud tracy5546 dynamic-datasource
com.baomidou dynamic-datasource-spring-boot-starter ${version} com.baomidou dynamic-datasource-spring-boot3-starter ${version}
升级到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、
org.apache.httpcomponents httpclient 4.5.14 org.apache.httpcomponents.client5 httpclient5 5.3
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及不兼容的问题
org.apache.rocketmq rocketmq-spring-boot-starter 2.2.3
// 定义个配置类,引入也可以: @Configuration @Import({RocketMQAutoConfiguration.class}) public class RocketMQConfig { }
启动失败 feign导致 not annotated with HTTP method type (ex. GET, POST)
说明常见类,如果找不到,请看源码注释,注释上面写的有,或者全局搜索即可,无需多说。
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。
分析原因,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