JDK 8 → JDK 17 升级说明书(面向 Spring Boot / Spring Cloud / Spring )

自从 JDK 8 发布以来,Java 语言在持续进化,带来了许多新的功能和性能改进。而 JDK 17 作为一个长期支持版本(LTS),在许多方面超越了 JDK 8,不仅提升了语言本身的能力,还进一步提高了性能、可维护性和开发者体验。

1 背景与功能增强

公司核心系统当前基于:

  • JDK 1.8
  • Spring Boot 2.x / Spring Cloud 2021.x
    官方已于 2022 年宣布:

Spring Framework 6、Spring Boot 3、Spring Cloud 2022+ 最低要求 JDK 17

继续留在 JDK 8 将永久锁定在 2.x 维护分支,无法获得任何新特性与安全补丁。

1.Spring 官方强制基线

  • Spring Framework 6.x:仅支持 JDK 17+
  • Spring Boot 3.x:最低 JDK 17,默认内置 Native/CDS 优化
  • Spring Cloud 2022+:基于 Boot 3,提供 Gateway 4、K8s Config 2.0 等新组件

停留在 JDK 8 = 永久错过新功能与社区长期维护。

2.模块化系统(JDK 9 引入,JDK 17 强化)

在 JDK 9 中引入了 Java 平台的模块化系统,它通过 module 关键字帮助开发者将代码组织成不同的模块,从而提升代码的可维护性、可扩展性和安全性。JDK 17 在模块系统方面进行了更多优化,使得模块化的使用更加高效。

  • JDK 8:没有模块化系统,所有代码都在一个大的类库中。
  • JDK 17:模块化系统已经成熟,Java 平台本身被拆分为多个模块,可以灵活选择需要的模块,大大减少了 JDK 的体积,提高了应用的启动性能。
示例代码:
复制代码
module com.myapp {
    requires java.base;
    exports com.myapp.service;
}

3.Lambda 表达式与 Stream API(JDK 8 引入)

JDK 8 引入了 Lambda 表达式和 Stream API,极大简化了函数式编程和集合操作的语法。JDK 17 在此基础上优化了性能和流操作的稳定性,尤其在处理大量数据时,提供了更好的支持。

  • JDK 8:Lambda 表达式和 Stream API 支持函数式编程,简化了集合的操作。
  • JDK 17:增强了 Stream API 和 Lambda 表达式的性能,特别是在并行流操作和垃圾回收方面。

4.记录类(JDK 14 引入)

JDK 14 引入了记录类(record),这是一个简化 Java Bean 类的语法。它使得不需要编写大量的模板代码(如 getter、setter、equals、hashCode 等方法)。在 JDK 17 中,记录类的支持变得更加完善,尤其是对继承和序列化的支持。

  • JDK 8:没有记录类,需要手动编写 getter、setter、equals 和 hashCode 方法。
  • JDK 17 :使用 record 关键字定义不可变对象,自动生成常用方法,减少样板代码,提高可读性。
示例代码:
复制代码
public record Person(String name, int age) {}

5.增强的 switch 表达式(JDK 12 引入)

JDK 12 引入了增强的 switch 表达式,这使得 switch 更加灵活,支持返回值、块语句和 yield 语句。JDK 17 中进一步优化了 switch 表达式的性能和功能,提升了代码的可维护性。

  • JDK 8switch 只能用作控制结构,无法返回值或执行多条语句。
  • JDK 17 :增强的 switch 表达式允许返回值、使用块语句,并支持 yield 语句来返回结果
示例代码:
复制代码
int day = 3;
String result = switch (day) {
    case 1 -> "Sunday";
    case 2 -> "Monday";
    default -> throw new IllegalStateException("Invalid day: " + day);
};

6、var 关键字(JDK 10 引入)

JDK 10 引入了 var 关键字,允许在声明变量时让编译器根据初始化值推断类型。这大大减少了样板代码,增强了代码的简洁性和可读性。在 JDK 17 中,var 关键字支持了更多的上下文,进一步简化了代码。

  • JDK 8 :没有 var 关键字,所有变量都必须显式声明类型。
  • JDK 17var 关键字被广泛应用于局部变量声明和增强的 lambda 表达式中,减少了冗余的类型声明。

示例代码:

复制代码
var message = "Hello, Java!";
var list = new ArrayList<String>();

7.JVM 性能与垃圾回收改进

JDK 8 和 JDK 17 在 JVM 性能和垃圾回收(GC)方面有显著差异。JDK 17 默认启用了更先进的垃圾回收器(例如 G1 GC 和 ZGC),这些改进使得内存管理更加高效,并减少了 GC 停顿时间。

  • JDK 8 :默认使用 Parallel GC,适用于大部分应用场景,但在响应时间要求高的场景下可能表现不佳。
  • JDK 17 :引入了 ZGC(Z Garbage Collector),在低延迟应用中表现优异,能够处理非常大的堆内存。

8.、文本块(JDK 13 引入)

JDK 13 引入了文本块(text block)功能,这使得多行字符串文本的编写变得更加简洁和可读。文本块支持原始文本格式,使得嵌套 JSON 或 SQL 查询变得更加直观。

  • JDK 8 :多行字符串需要使用字符串连接符 +,使得代码不易阅读。
  • JDK 17 :支持多行字符串文本(text block),大大提升了代码的可读性和简洁性。

示例代码:

复制代码
String json = """
              {
                  "name": "John",
                  "age": 30,
                  "city": "New York"
              }
              """;

9.增强的 PatternMatcher API(JDK 16 引入)

JDK 16 引入了对正则表达式的增强支持,尤其是 PatternMatcher 类的增强,使得在处理复杂的正则表达式时更加灵活和高效。

  • JDK 8:正则表达式库功能较为基础。
  • JDK 17:增强了正则表达式的语法支持,包括非捕获分组和更复杂的匹配模式。

10.sealed 类(JDK 15 引入)

sealed 类(密封类)是在 JDK 15 中引入的一个特性,它允许类设计者限制哪些类可以继承它们。这个特性为 Java 提供了更强的类型系统,能够确保类层次结构的封闭性。

  • JDK 8 :没有 sealed 类,所有类可以自由继承。
  • JDK 17 :支持 sealed 类,类设计者可以限制子类的继承,从而增强代码的可控性和安全性。

示例代码:

复制代码
public sealed class Shape permits Circle, Square {}
public final class Circle extends Shape {}
public final class Square extends Shape {}

11.JEP 389:外部内存访问 API(JDK 16 引入)

JDK 16 引入了外部内存访问 API,它为 Java 提供了一种更高效的方式来访问外部内存(即不在 Java 堆中的内存),这个特性对于与本地代码或操作系统交互时的性能优化非常重要。

  • JDK 8:不提供对外部内存的直接访问。
  • JDK 17 :通过 MemorySegmentMemoryAddress 等 API,Java 现在可以更高效地与外部内存交互。

2 升级计划

整个升级过程 计划分为3步。

  • step1:先将项目升级到jdk17,使用正常
  • step2:生成代码的模板类升级到Jdk17对应的版本
  • step3:功能陆续验证

原项目版本:Jdk8 + springboot 2.x.x.x

升级后版本:Jdk17 + springboot 3.x.x

2.1 基础依赖升级
1. 首先把项目环境切换到Jdk17上
2. 父级pom文件中spring-boot-starter-parent版本升级到3.x.x

<properties>

<spring.boot.version>3.1.0</spring.boot.version>

</properties>

3. 项目配置中的java.version由8升级到17

<properties>

<java.version>17</java.version>

</properties>

4. maven编译相关maven.compiler.sourcemaven.compiler.target由8升到17

<properties>

<maven.compiler.source>17</maven.compiler.source>

<maven.compiler.target>17</maven.compiler.target>

</properties>

5. 由于在jdk17中移除了javax的部分包,所以很多javax.xxx都需要修改jakarta.xxx

新版 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 语句。

6. 如果我们未通过 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 获取。

7. lombok版本由1.18.20 升级 1.18.30
2.2 相关组件升级
部分属性的修改:

部分属性的修改:

  • 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 获取。

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

其他变化

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

  • 移除图像 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 来指定哪个构造函数将用于属性绑定。
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 下的新属性。

3 版本对应

JDK版本与Spring Boot版本对应

Maven版本与Spring Boot版本对应

SpringCloud与SpringBoot版本对应

项目大致依赖

springcloud相关组件