SpringBoot2.0 迁移 SpringBoot3.0

最近Springboot3.0正式版发布了,Spring Framework也升级到了6.0,JavaEE也由旧的Oracle发布的JavaEE升级为Eclipse发布的JakartaEE。

Springboot3.0开始仅支持OpenJDK17及更高版本的OpenJDK,OpenJDK17是一个LTS版本。

考虑到Jdk8已经是一个发布快十年的Jdk,且OpenJDK17发布了很多提升效率的新特性,因此我再看到Springboot3.0正式版发布后,也将我的开源项目springboot-project-seed的依赖整体进行了升级。

依赖对比

IDEA: IntelliJ IDEA 2022.3.2 (Community Edition)

软件

旧版本

新版本

JDK

OpenJDK8

OpenJDK17

springboot

2.3.7.RELEASE

3.0.2

spring-cloud bom

null

2022.0.1

spring-data bom

null

2022.0.1

mybatis-mapper bom

null

2.1.0

knife4j-dependencies bom

null

4.0.0

问题与解决方案

sun.misc.Cleaner被转移到jdk.internal.ref.Cleaner

旧的伪代码:

java.nio.MappedByteBuffer buffer = null;
sun.misc.Cleaner cleaner = ((sun.nio.ch.DirectBuffer) buffer).cleaner();
cleaner.clear();

新的伪代码:

java.nio.MappedByteBuffer buffer = null;
jdk.internal.ref.Cleaner cleaner = ((sun.nio.ch.DirectBuffer) buffer).cleaner();
cleaner.clear();

JavaEE to JakartaEE

程序中的包依赖涉及javax.*需修改为jakarta.*。

常见需要修改的类:



备注

javax.validation.Valid

jakarta.validation.Valid

校验框架validation下的类均需要修改

javax.validation.constraints.NotBlank

jakarta.validation.constraints.NotBlank

校验框架validation下的类均需要修改

javax.servlet.ServletRequest

jakarta.servlet.ServletRequest

tomcat下的类均需要修改

javax.servlet.http.HttpServletRequest

jakarta.servlet.http.HttpServletRequest

tomcat下的类均需要修改

javax.servlet.http.Cookie

jakarta.servlet.http.Cookie

tomcat下的类均需要修改

javax.annotation.Resource

jakarta.annotation.Resource

annotation-api下的类均需要修改

验证存在的Deprecated



备注

org.springframework.web.servlet.handler.HandlerInterceptorAdapter

org.springframework.web.servlet.HandlerInterceptor

OpenJDK1.8的interface支持默认方法后spring的很多抽象适配器类被标记为@Deprecated

com.fasterxml.jackson.databind.PropertyNamingStrategy.SNAKE_CASE

com.fasterxml.jackson.databind.PropertyNamingStrategies.SNAKE_CASE

org.springframework.scheduling.annotation.AsyncConfigurerSupport

org.springframework.scheduling.annotation.AsyncConfigurer

OpenJDK1.8的interface支持默认方法后spring的很多抽象配置类被标记为@Deprecated

org.springframework.http.client.ClientHttpResponse.getStatusCode().getReasonPhrase()

org.springframework.http.client.ClientHttpResponse.getStatusText()

旧的方法被删除

org.springframework.web.socket.messaging.WebSocketStompClient.connect

org.springframework.web.socket.messaging.WebSocketStompClient.connectAsync

tk-mybatis升级为mybatis-mapper

tk-mybatis已经停止维护,要使用mybatis增强组件需迁移到mybatis-mapper 2.1.0。

旧的依赖:

<dependency>
    <groupId>tk.mybatis</groupId>
    <artifactId>mapper-spring-boot-starter</artifactId>
    <version>2.1.5</version>
</dependency>

新的依赖:

<dependency>
    <groupId>io.mybatis</groupId>
    <artifactId>mybatis-service</artifactId>
    <version>2.1.0</version>
</dependency>

mybatis-mapper为tk-mybatis作者基于mybatis-3.5.9开发的全新的mybatis增强组件,支持lambda表达式,优化了查询api,更多信息

tk.mybatis.spring.annotation.MapperScan注解被删除,新的mybatis-service中使用org.mybatis.spring.annotation.MapperScan注解。

CORS配置

allowCredentials设置为true,必须使用allowedOriginPatterns。

http

旧的配置:

@Override
public void addCorsMappings(CorsRegistry registry) {

    registry.addMapping("/**")
        .allowedOrigins("*")
        .allowCredentials(true)
        .allowedHeaders("*")
        .allowedMethods("GET", "POST", "DELETE", "PUT", "PATCH", "OPTIONS")
        .maxAge(3600L);
}

新的配置:

@Override
public void addCorsMappings(CorsRegistry registry) {

    registry.addMapping("/**")
        .allowedOriginPatterns("*")
        .allowCredentials(true)
        .allowedHeaders("*")
        .allowedMethods("GET", "POST", "DELETE", "PUT", "PATCH", "OPTIONS")
        .maxAge(3600L);
}
websocket

旧的配置:

@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
    registry.addEndpoint(endpoint)
        .setAllowedOrigins("*") // 解决跨域问题
        .withSockJS()
        .setInterceptors(new HttpSessionIdHandshakeInterceptor());
}

新的配置:

@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
    registry.addEndpoint(endpoint)
        .setAllowedOriginPatterns("*") // 解决跨域问题
        .withSockJS()
        .setInterceptors(new HttpSessionIdHandshakeInterceptor());
}

websocket headers

header的容器由LinkedList修改为了ArrayList。

旧的伪代码:

@Override
public void postSend(Message<?> message, MessageChannel channel, boolean sent) {
    Map<?, ?> nativeHeadersMap = (Map<?, ?>) message.getHeaders().get(NATIVE_HEADERS);

    LinkedList<?> idList = (LinkedList<?>) nativeHeadersMap.get("id");
    if (idList != null) {
        String idString = idList.get(0).toString();
    }
}

新的伪代码:

@Override
public void postSend(Message<?> message, MessageChannel channel, boolean sent) {
    Map<?, ?> nativeHeadersMap = (Map<?, ?>) message.getHeaders().get(NATIVE_HEADERS);

    List<?> idList = (List<?>) nativeHeadersMap.get("id");
    if (idList != null) {
        String idString = idList.get(0).toString();
    }
}

路径匹配

Spring Framework 6.0的Spring MVC和Spring Webflux路径匹配规则发生了变化,默认情况下头部和尾部斜杠/的匹配机制将和以前不同。

6.0以前/foo/bar等同于/foo/bar/,6.0以后/foo/bar不同于/foo/bar/

假设
spring.application.name=sps
api-prefix=/api/kyyee/v2/${spring.application.name}

旧的伪代码:

@RequestMapping("${api-prefix}/employees)

新的伪代码:

@RequestMapping("/${api-prefix}/employees)

如路径匹配涉及以上类似写法,必须在头部添加斜杠/,否则在访问相关uri时,程序将响应404。

spring.data

spring.data前缀已被保留给Spring Data相关的项目,如果使用了Spring Data相关项目,application.yml中相关的配置需要修改。例如 spring.redis 现在需要修改为 spring.data.redis。
旧的伪代码:

spring:
    redis:
        url: redis://${REDIS_USERNAME}:${REDIS_PASSWORD}@${REDIS_URL}
        timeout: 1000ms
        jedis:
            pool:
                max-active: 100
                max-idle: 10
                max-wait: -1ms
                min-idle: 1

新的伪代码:

spring:
    data:
        redis:
            url: redis://${REDIS_USERNAME}:${REDIS_PASSWORD}@${REDIS_URL}
            timeout: 1000ms
            jedis:
                pool:
                    max-active: 100
                    max-idle: 10
                    max-wait: -1ms
                    min-idle: 1

springfox to springdoc

swagger的spring增强组件springfox已停止维护,且不再支持springboot3.0,因此采用全新推出的springdoc作为swagger的spring增强组件,该组件提供了对OpenApi3.0的支持。

<dependency>
    <groupId>com.github.xiaoymin</groupId>
    <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
    <version>4.0.0</version>
</dependency>

knife4j是一款更适合Chinese Coder的swagger-ui组件,引入上述依赖将自动引入springdoc相关的依赖。springboot3需要的knife4j-openapi3-jakarta-spring-boot-starter最低版本为4.0.0,springdoc最低版本为2.0.0。

OpenApi3.0对比Swagger2.0的注解由较大的变更,参考下表:



备注

@Api

@Tag

@ApiIgnore

@Parameter(hidden=true) or @Operation(hidden=true) or @Hidden

@ApiImplicitParam

@Parameter

@ApiImplicitParams

@Parameters

@ApiModel

@Schema

@ApiModelProperty(hidden=true)

@Schema(accessMode=READ_ONLY)

@ApiModelProperty

@Schema

@ApiOperation(value=“foo”, notes=“bar”)

@Operation(summary=“foo”, description=“bar”)

@ApiParam

@Parameter

@ApiResponse(code=404, message=“foo”)

@ApiResponse(responseCode=“404”, description=“foo”)


Github

开源项目springboot-project-seed已完成 springboot 2.3.7.RELEASE 到 springboot 3.0.2 升级,欢迎 issue、star。