从 Spring Boot 2 升级到 Spring Boot 3

1、概览

在本教程中,我们将学习如何将 Spring Boot 应用迁移到 3.0 版本。当前所使用的 Spring Boot 版本是 2.7,并且 Java 版本是 17。

2、核心的变化

Spring Boot 3.0 是该框架的一个重要里程碑,对其核心组件进行了多项重要修改。

2.1、配置属性

部分属性的修改:

  • spring.redis 已移至 spring.data.redis
  • spring.data.cassandra 已移至 spring.cassandra
  • 移除了spring.jpa.hibernate.use-new-id-generator
  • server.max.http.header.size 已移至 server.max-http-request-header-size
  • 移除了对 spring.security.saml2.relyingparty.registration.{id}.identity-provider 的支持。

要识别这些属性,我们可以在 pom.xml 中添加 spring-boot-properties-migrator

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-properties-migrator</artifactId>
    <scope>runtime</scope>
</dependency>

最新版本的 spring-boot-properties-migrator 可从 Maven Central 获取。

该依赖会在启动时生成并打印一份报告,列出已废弃的属性名称,并在运行时临时迁移这些属性。

2.2、Jakarta EE 10

新版 Jakarta EE 10 对 Spring Boot 3 的相关依赖进行了更新:

  • Servlet 规范更新至 6.0。
  • JPA 规范更新至 3.1。

因此,如果我们未通过 spring-boot-starter 来自动管理这些依赖的版本,我们就应该手动更新它们。

首先更新 JPA 依赖:

<dependency>
    <groupId>jakarta.persistence</groupId>
    <artifactId>jakarta.persistence-api</artifactId>
    <version>3.1.0</version>
</dependency>

最新版本的 jakarta.persistence-api 可从 Maven Central 获取。

接下来更新 Servlet 依赖:

<dependency>
    <groupId>jakarta.servlet</groupId>
    <artifactId>jakarta.servlet-api</artifactId>
    <version>6.0.0</version>
</dependency>

最新版本的 jakarta.servlet-api 可从 Maven Central 获取。

除了依赖坐标的变化外,Jakarta EE 现在使用 jakarta 包而不是 javax 包。因此,更新依赖后,我们可能需要更新 import 语句。

2.3、Hibernate

如果我们未通过 spring-boot-starter 来管理 Hibernate 的依赖版本,那么一定要手动更新其版本:

<dependency>
    <groupId>org.hibernate.orm</groupId>
    <artifactId>hibernate-core</artifactId>
    <version>6.1.4.Final</version>
</dependency>

最新版本的 hibernate-core 可从 Maven Central 获取。

2.4、其他变化

此外,该版本还包括一些其他重大的核心变化:

  • 移除图像 banner 支持:要自定义 banner,只有 banner.txt 文件才被视为有效文件。
  • 日志中的日期格式:Logback 和 Log4J2 的新默认日期格式为 yyyy-MM-dd'T'HH:mm:ss.SSSXXX 如果要恢复旧的默认格式,我们需要将 application.yaml 中的属性 logging.pattern.dateformat 的值设置为旧值。
  • @ConstructorBinding 只在构造函数上使用:对于 @ConfigurationProperties 类,不再需要在类上使用 @ConstructorBinding,并且应该将其移除。然而,如果一个 class 或 record 有多个构造函数,则必须在所需的构造函数上使用 @ConstructorBinding 来指定哪个构造函数将用于属性绑定。

3、Web 应用的变化

如果我们的应用是一个 Web 应用,我们可能要进行一些更改。

3.1、尾斜线匹配

新发布的 Spring Boot 废弃了配置“尾斜线匹配”的选项,并将其默认值设置为 false

例如,定义一个简单的 GET 端点:

@RestController
@RequestMapping("/api/v1/todos")
@RequiredArgsConstructor
public class TodosController {
    @GetMapping("/name")
    public List<String> findAllName(){
        return List.of("Hello","World");
    }
}

现在,GET /api/v1/todos/name/ 默认情况下不再匹配,会导致 HTTP 404 错误。

我们可以通过定义一个实现 WebMvcConfigurerWebFluxConfigurer(响应式)的新配置类来启用所有端点的尾斜线匹配:

public class WebConfiguration implements WebMvcConfigurer {

    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        // 启用尾斜线匹配
        configurer.setUseTrailingSlashMatch(true);
    }

}

3.2、响应头大小限制

如前所述,server.max.http.header.size 属性已被弃用,取而代之的是 server.max-http-request-header-size,它只检查请求头的大小。为了给响应头也定义一个限制,让我们定义一个新的 Bean:

@Configuration
public class ServerConfiguration implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {

    @Override
    public void customize(TomcatServletWebServerFactory factory) {
        factory.addConnectorCustomizers(new TomcatConnectorCustomizer() {
            @Override
            public void customize(Connector connector) {
                connector.setProperty("maxHttpResponseHeaderSize", "100000");
            }
        });
    }
}

如果我们使用的是 Jetty 而不是 Tomcat,则应将 TomcatServletWebServerFactory 更改为 JettyServletWebServerFactory。此外,其他嵌入式 Web 容器并不支持这一功能。

3.3、其他变化

此外,该版本在 web 应用层面也有其他重大变化:

  • 优雅停机:优雅停机的 SmartLifecycle 实现更新了。现在,Spring 会在 SmartLifecycle.DEFAULT_PHASE - 2048 阶段启动优雅停机,并在 SmartLifecycle.DEFAULT_PHASE - 1024 阶段停止 Web 服务器。
  • RestTemplateHttpClientRestTemplate 更新了其 Apache HttpClient5 的版本。

4、Actuator 的变化

Actuator 模块中引入了一些重大变化。

4.1、Actuator 端点脱敏

在以前的版本中,Spring 会自动屏蔽端点 /env/configprops 上的敏感 key 值,这些端点会显示配置属性和环境变量等敏感信息。在此版本中,Spring 更改了方法,默认情况下更加安全。

现在,它不再只屏蔽某些 key 值,而是默认屏蔽所有 key 值。我们可以通过设置 management.endpoint.env.show-values(适用于 /env 端点)或 management.endpoint.configprops.show-values(适用于 /configprops 端点)的属性值来更改这一配置。可选值如下:

  • NEVER:不显示值
  • ALWAYS:显示所有值。
  • WHEN_AUTHORIZED:如果用户已获授权,则会显示所有值。对于 JMX,所有用户都已授权。对于 HTTP,只有特定角色可以访问数据。

4.2、其他变化

Spring Actuator 模块上也有其他相关更新:

  • Jmx 端点暴露:JMX 只处理 health 端点。通过配置 management.endpoints.jmx.exposure.includemanagement.endpoints.jmx.exposure.exclude 属性,我们可以对其进行自定义。
  • httptrace 端点重命名:此版本将 /httptrace 端点重命名为 /httpexchanges
  • 该版本现在隔离了负责序列化 actuator 端点响应的 ObjectMapper 实例。我们可以通过将 management.endpoints.jackson.isolated-object-mapper 属性设置为 false 来更改此功能。

5、Spring Security

Spring Boot 3 仅与 Spring Security 6 兼容。

在升级到 Spring Boot 3.0 之前,我们应该先 将 Spring Boot 2.7 应用升级到Spring Security 5.8。然后,我们可以将 Spring Security 升级到版本 6,并升级到 Spring Boot 3.0。

该版本中引入了一些重要更改:

  • ReactiveUserDetailsService 未自动配置:在存在 AuthenticationManagerResolver 的情况下,ReactiveUserDetailsService 不再自动配置。
  • SAML2 依赖方配置:我们之前提到,新版 Spring boot 不再支持 spring.security.saml2.relyingparty.registration.{id}.identity-provider 下的属性。相反,我们应该使用 spring.security.saml2.relyingparty.registration.{id}.asserting-party 下的新属性。

6、Spring Batch

让我们看看 Spring Batch 模块中引入的一些重大变化。

6.1、不建议使用 @EnableBatchProcessing

以前,我们可以使用 @EnableBatchProcessing 对配置类进行注解,从而启用 Spring Batch 的自动配置功能。如果我们想使用自动配置,Spring Boot 的新版本不鼓励使用该注解。

实际上,使用这个注解(或定义一个实现 DefaultBatchConfiguration 的bean)会告诉 Spring 停止自动配置。

6.2、运行多个作业

以前,使用 Spring Batch 可以同时运行多个批处理作业。但现在情况已不再如此。如果自动配置检测到单个作业,它将在应用程序启动时自动执行。

因此,如果 context 中有多个作业,我们就需要使用 spring.batch.job.name 属性提供作业的名称,从而指定启动时应执行哪个作业。因此,这意味着如果我们要运行多个作业,就必须为每个作业创建一个单独的应用程序。

或者,我们也可以使用 Quartz、Spring Scheduler 或其他调度程序来调度作业。

7、总结

在本文中,我们学习了 Spring Boot 3 中核心组件的变化,以及如何将 Spring Boot 2.7 应用迁移到 Spring Boot 3。

还有一些其他模块的变化,如 Spring Session、Micrometer、依赖管理等,可以参考 官方 Wiki


参考:https://www.baeldung.com/spring-boot-3-migration