Skip to content

Java 8 To 17 迁移指南

项目配置更新

Maven 配置

问题位置: pom.xml 及各子模块的 pom.xml

xml
<properties>
    <java.version>1.8</java.version>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
</properties>

修改建议: 更新 Maven 配置以支持 Java 17:

xml
<properties>
    <java.version>17</java.version>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
</properties>

Maven 插件更新

问题位置: pom.xml

xml
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.8.1</version>
    <configuration>
        <source>1.8</source>
        <target>1.8</target>
        <encoding>UTF-8</encoding>
    </configuration>
</plugin>

修改建议: 更新 Maven 编译插件配置:

xml
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.10.1</version>
    <configuration>
        <source>17</source>
        <target>17</target>
        <encoding>UTF-8</encoding>
        <compilerArgs>
            <arg>--add-opens=java.base/java.lang=ALL-UNNAMED</arg>
            <arg>--add-opens=java.base/java.io=ALL-UNNAMED</arg>
            <arg>--add-opens=java.base/java.util=ALL-UNNAMED</arg>
            <arg>--add-opens=java.base/java.util.concurrent=ALL-UNNAMED</arg>
        </compilerArgs>
    </configuration>
</plugin>

Spring Boot 版本更新

问题位置: pom.xml

xml
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.7.18</version>
    <relativePath/>
</parent>

修改建议: 更新 Spring Boot 版本至少为 2.7.x 或更高版本(推荐 3.x),以确保与 Java 17 完全兼容:

xml
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.1.5</version>
    <relativePath/>
</parent>

注意: 如果升级到 Spring Boot 3.x,需要同时处理 Jakarta EE 相关的包名变更(javax.* 变为 jakarta.*)。

移除的 Java EE 模块

Java 9 及以上版本移除了许多 Java EE 模块,这些模块需要作为单独的依赖项添加。

JAXB (Java Architecture for XML Binding)

问题: Java 9+ 移除了 javax.xml.bind 包

修改建议: 添加以下依赖到 pom.xml:

xml
<dependency>
    <groupId>jakarta.xml.bind</groupId>
    <artifactId>jakarta.xml.bind-api</artifactId>
    <version>4.0.0</version>
</dependency>
<dependency>
    <groupId>org.glassfish.jaxb</groupId>
    <artifactId>jaxb-runtime</artifactId>
    <version>4.0.2</version>
</dependency>

JavaBeans Activation Framework (JAF)

问题: Java 9+ 移除了 javax.activation 包

修改建议: 添加以下依赖到 pom.xml:

xml
<dependency>
    <groupId>jakarta.activation</groupId>
    <artifactId>jakarta.activation-api</artifactId>
    <version>2.1.1</version>
</dependency>
<dependency>
    <groupId>org.eclipse.angus</groupId>
    <artifactId>angus-activation</artifactId>
    <version>2.0.0</version>
</dependency>

Java Annotation API

问题位置: 多个文件中使用了 javax.annotation.* 包

java
import javax.annotation.Resource;

修改建议: 添加以下依赖到 pom.xml:

xml
<dependency>
    <groupId>jakarta.annotation</groupId>
    <artifactId>jakarta.annotation-api</artifactId>
    <version>2.1.1</version>
</dependency>

并更新导入语句:

java
import jakarta.annotation.Resource;

Java Mail API

问题位置: /core/alarm/impl/EmailJobAlarm.java

修改建议: 添加以下依赖:

xml
<dependency>
    <groupId>jakarta.mail</groupId>
    <artifactId>jakarta.mail-api</artifactId>
    <version>2.1.1</version>
</dependency>
<dependency>
    <groupId>org.eclipse.angus</groupId>
    <artifactId>jakarta.mail</artifactId>
    <version>2.0.1</version>
</dependency>

Java Servlet API

问题位置: 多个文件中使用了 javax.servlet.* 包

java
import javax.servlet.http.HttpServletRequest;

修改建议: pom.xml

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

并更新导入语句:

java
import jakarta.servlet.http.HttpServletRequest;

反射 API 变更

反射访问非公共成员

问题位置:

xxx/common/util/ReflectHelper.java

xxx/config/mybatis/MybatisInterceptor.java

xxx/calc/utils/AccessDBUtils.java

问题描述: Java 9+ 对反射 API 进行了更严格的封装,特别是对非公共成员的访问限制更严格。

修改建议: 确保在访问非公共成员时显式设置可访问性,并在使用后恢复:

java
try {
    field.setAccessible(true);
    Object value = field.get(object);
    // 使用 value
    return value;
} finally {
    field.setAccessible(false);
}

如果在运行时遇到 InaccessibleObjectException,需要添加 JVM 参数:

csharp 体验AI代码助手 代码解读复制代码--add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED

反射获取方法和字段

问题位置:

xxx/service/impl/HeatingDeviceServiceImpl.java

修改建议:

  • 使用 Class.getDeclaredMethod() 代替 Class.getMethod() 来获取非公共方法

  • 确保正确处理泛型类型

  • 添加适当的异常处理

java
try {
    Method method = clazz.getDeclaredMethod(methodName, parameterTypes);
    method.setAccessible(true);
    return method.invoke(object, args);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
    throw new RuntimeException("反射调用方法失败", e);
} finally {
    // 可选:恢复访问控制
}

内部 API 使用

sun.* 包使用

问题描述: Java 9+ 严格限制了对 sun.* 包的访问。

修改建议:

  • 检查项目中是否使用了 sun.* 包下的类

  • 如果有,寻找标准 API 替代方案

  • 如果必须使用,考虑使用 --add-exports 或 --add-opens 命令行参数

语言特性优化

Switch 表达式

问题位置:

xxx/common/util/text/Convert.java

xxx/common/system/query/QueryGenerator.java

xxx/biz/service/impl/NodeRelationConvertServiceImpl.java

修改建议: 使用 Java 12+ 的 Switch 表达式语法,更简洁、更安全:

java
// 旧代码
double result;
switch (operator) {
    case "+":
        result = num1 + num2;
        break;
    case "-":
        result = num1 - num2;
        break;
    // ...
}
return result;

// 新代码
double result = switch (operator) {
    case "+" -> num1 + num2;
    case "-" -> num1 - num2;
    case "*" -> num1 * num2;
    case "/" -> {
        if (num2 == 0) {
            log.warn("除数不能为零");
            yield 0.0;
        }
        yield num1 / num2;
    }
    default -> {
        log.warn("不支持的操作符: {}", operator);
        yield 0.0;
    }
};
return result;

文本块

问题位置: 项目中有多处使用长字符串拼接的代码

修改建议: 使用 Java 15+ 的文本块特性,提高代码可读性:

java
// 旧代码
String sql = "SELECT * " +
             "FROM users " +
             "WHERE status = 'active' " +
             "AND created_date > ?";

// 新代码
String sql = """
             SELECT *
             FROM users
             WHERE status = 'active'
             AND created_date > ?
             """;

instanceof 模式匹配

问题位置:

xxx/common/aspect/TypeTransAspect.java

修改建议: 使用 Java 16+ 的 instanceof 模式匹配,简化类型检查和转换:

java
// 旧代码
if (result instanceof Result) {
    if (((Result) result).getData() instanceof IPage) {
        // 使用 ((Result) result).getData()
    }
}

// 新代码
if (result instanceof Result res && res.getData() instanceof IPage page) {
    // 直接使用 page
}

Record 类型

修改建议: 对于简单的数据传输对象,考虑使用 Java 16+ 的 Record 类型:

java
// 旧代码
public class UserDTO {
    private final String name;
    private final String email;
    
    public UserDTO(String name, String email) {
        this.name = name;
        this.email = email;
    }
    
    // getters, equals, hashCode, toString
}

// 新代码
public record UserDTO(String name, String email) {}

第三方库兼容性

Spring Boot 和 Spring Framework

问题位置: pom.xml

xml
<springboot.version>2.7.18</springboot.version>
<spring-cloud.version>2021.0.0</spring-cloud.version>
<spring-cloud-alibaba.version>2021.1</spring-cloud-alibaba.version>

修改建议: 更新到支持 Java 17 的版本:

xml
<springboot.version>3.1.5</springboot.version>
<spring-cloud.version>2022.0.4</spring-cloud.version>
<spring-cloud-alibaba.version>2022.0.0.0</spring-cloud-alibaba.version>

MyBatis 和 MyBatis-Plus

问题位置: pom.xml

xml
<mybatis-plus.version>3.5.1</mybatis-plus.version>
<dynamic-datasource-spring-boot-starter.version>4.1.3</dynamic-datasource-spring-boot-starter.version>

修改建议: 更新到支持 Java 17 的版本:
``` xml
<mybatis-plus.version>3.5.4.1</mybatis-plus.version>
<dynamic-datasource-spring-boot-starter.version>4.2.0</dynamic-datasource-spring-boot-starter.version>

Shiro

问题位置: pom.xml

xml
<shiro.version>1.12.0</shiro.version>
<java-jwt.version>3.11.0</java-jwt.version>
<shiro-redis.version>3.2.2</shiro-redis.version>

修改建议: Shiro 1.12.0 已支持 Java 17,但建议更新 JWT 库:

xml
<java-jwt.version>4.4.0</java-jwt.version>

数据库驱动

问题位置: pom.xml

xml
<postgresql.version>42.2.25</postgresql.version>
<ojdbc6.version>11.2.0.3</ojdbc6.version>
<sqljdbc4.version>4.0</sqljdbc4.version>
<mysql-connector-java.version>8.0.27</mysql-connector-java.version>

修改建议: 更新到支持 Java 17 的版本:

xml
<postgresql.version>42.6.0</postgresql.version>
<ojdbc.version>23.3.0.23.09</ojdbc.version> <!-- 替换 ojdbc6 -->
<mssql-jdbc.version>12.4.2.jre11</mssql-jdbc.version> <!-- 替换 sqljdbc4 -->
<mysql-connector-j.version>8.2.0</mysql-connector-j.version> <!-- 替换 mysql-connector-java -->

并更新依赖声明:

xml
<!-- MySQL -->
<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <version>${mysql-connector-j.version}</version>
</dependency>

<!-- Oracle -->
<dependency>
    <groupId>com.oracle.database.jdbc</groupId>
    <artifactId>ojdbc11</artifactId>
    <version>${ojdbc.version}</version>
</dependency>

<!-- SQL Server -->
<dependency>
    <groupId>com.microsoft.sqlserver</groupId>
    <artifactId>mssql-jdbc</artifactId>
    <version>${mssql-jdbc.version}</version>
</dependency>

日志框架

问题位置: pom.xml

xml
<log4j2.version>2.17.0</log4j2.version>
<logback.version>1.2.9</logback.version>

修改建议: 更新到支持 Java 17 的版本:

xml
<log4j2.version>2.22.0</log4j2.version>
<logback.version>1.4.11</logback.version>

JSON 处理库

问题位置: pom.xml

xml
<fastjson.version>1.2.83</fastjson.version>

修改建议: 更新到支持 Java 17 的版本,或考虑切换到 Jackson:

xml
<fastjson2.version>2.0.42</fastjson2.version>

并更新依赖:

xml
<dependency>
    <groupId>com.alibaba.fastjson2</groupId>
    <artifactId>fastjson2</artifactId>
    <version>${fastjson2.version}</version>
</dependency>

其他工具库

问题位置: pom.xml

xml
<hutool.version>5.8.25</hutool.version>
<guava.version>29.0-jre</guava.version>
<commons.version>2.6</commons.version>
<commons-io.version>2.11.0</commons-io.version>
<commons-beanutils.version>1.9.4</commons-beanutils.version>

修改建议: 更新到支持 Java 17 的版本:

xml
<hutool.version>5.8.25</hutool.version> <!-- 已是最新版本 -->
<guava.version>32.1.3-jre</guava.version>
<commons-lang3.version>3.13.0</commons-lang3.version> <!-- 替换 commons.version -->
<commons-io.version>2.15.0</commons-io.version>
<commons-beanutils.version>1.9.4</commons-beanutils.version> <!-- 已是最新版本 -->

模块系统兼容性

非模块化应用的兼容性

问题描述: Java 9+ 引入了模块系统,可能会影响非模块化应用的行为。

修改建议:

  • 添加 --add-modules 参数以访问已移除的模块
  • 添加 --add-opens 参数以允许反射访问
  • 考虑将应用迁移到模块系统

安全相关变更

加密算法支持

问题位置:

xxx/common/util/security/SecurityTools.java

xxx/system/service/util/SecurityUtil.java

问题描述: Java 17 可能移除了一些不安全的加密算法。

修改建议:

  • 检查使用的加密算法是否在 Java 17 中仍然受支持
  • 更新到更安全的算法和更长的密钥长度
  • 使用 Java 17 的新安全特性

TLS 配置

问题位置:

xxx/common/util/HttpUtils.java

问题描述: Java 17 默认禁用了一些不安全的 TLS 版本和密码套件。

修改建议:

  • 更新 SSL/TLS 配置,使用 TLS 1.3 或 TLS 1.2
  • 移除对不安全密码套件的支持
  • 使用更安全的证书验证机制

字符串和文本处理

字符串处理方法

问题描述: Java 11+ 引入了许多新的字符串处理方法。

修改建议: 利用 Java 11+ 新增的字符串处理方法提高代码效率:

  • 使用 String.strip() 代替 String.trim()
  • 使用 String.isBlank() 检查空白字符串
  • 使用 String.lines() 处理多行文本
  • 使用 String.repeat(n) 重复字符串

正则表达式改进

问题描述: Java 11+ 改进了正则表达式 API。

修改建议: 利用 Java 11+ 改进的正则表达式 API:

  • 使用 Pattern.asMatchPredicate() 创建谓词
  • 使用命名捕获组简化正则表达式

网络 API 变更

HTTP 客户端

问题位置:

xxx/calc/utils/SSLClient.java

问题描述: Java 11 引入了新的 HTTP 客户端 API,旧的 HTTP 客户端 API 可能在未来版本中被移除。

修改建议: 考虑使用 Java 11+ 的新 HTTP 客户端 API:

java
HttpClient client = HttpClient.newBuilder()
    .version(HttpClient.Version.HTTP_2)
    .followRedirects(HttpClient.Redirect.NORMAL)
    .connectTimeout(Duration.ofSeconds(20))
    .sslContext(sslContext)
    .build();

HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create(url))
    .header("Content-Type", "application/json")
    .POST(HttpRequest.BodyPublishers.ofString(jsonBody))
    .build();

HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

集合和流 API 增强

集合工厂方法

修改建议: 使用 Java 9+ 的集合工厂方法创建不可变集合:

java
// 旧代码
List<String> list = Collections.unmodifiableList(Arrays.asList("a", "b", "c"));
Map<String, Integer> map = Collections.unmodifiableMap(new HashMap<String, Integer>() {{
    put("one", 1);
    put("two", 2);
}});

// 新代码
List<String> list = List.of("a", "b", "c");
Map<String, Integer> map = Map.of("one", 1, "two", 2);

Stream API 增强

修改建议: 利用 Java 9+ 增强的 Stream API:

  • 使用 Stream.ofNullable() 处理可能为 null 的元素
  • 使用 Stream.iterate() 的新重载方法
  • 使用 takeWhile() 和 dropWhile() 方法
  • 使用 Stream.toList() 代替 collect(Collectors.toList())

TIP

作者:Cosolar

链接:https://juejin.cn/post/7495005738764730431

来源:稀土掘金 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

粤ICP备20009776号