文章目录
- 概要
- 国内顶级开源项目升级情况
- 适配SpringBoot3指南
- SpringBoot3升级要点
- 1、jdk17变动(如javax)
- 2、redis修改spring.redis.host ===> spring.data.redis.host
- 3、SpringCloudApplication注解被删除
- 4、不兼容升级import java.servlet====>import jakarta.servlet
- 5、swagger集成 弃用springfox--->springdoc不兼容升级
- 6、动态数据源baomidou的dynamic-datasource依赖变动
- 7、Spring Framework 6.0 中删除了对 Apache HttpClient 支持(RestTemplate受影响)
- 8、SpringBoot3.0整合RocketMQ时出现未能加载bean文件
概要
由于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 |
适配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.host
spring:data:redis:host: 10.16.58.180port: 6379password: Admin123database: 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注解说明
注解作用 | 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 = "$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 = "根据儿童证件号码查询儿童档案")
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){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 = "获取预约码", 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示例
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.
参考:SpringBoot3.0整合RocketMQ时出现未能加载bean文件
<dependency><groupId>org.apache.rocketmq</groupId><artifactId>rocketmq-spring-boot-starter</artifactId><!-- 目前也没有新版本,roketmq2.2.3没有适配springboot3--><version>2.2.3</version></dependency>
``
### 9、org.elasticsearch:elasticsearch:jar:unknown was not found
> 从springboot2升级到springboot3 elasticsearch依赖出现问题
### 10、springboot 3.2 openFeign加载失败暂未解决 (3.1没问题)
> 启动失败 feign导致 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`-