现在是时候将应用程序迁移到Java 17了?

原文作者:Johan Janssen,原文链接:It’s time to move your applications to Java 17. Here’s why—and how.

关于从以前的长期支持版本(Java 11和Java 8)迁移代码,您需要知道的是什么。

Java语言和运行时平台的下一个长期支持(LTS)版本Java 17于9月14日正式发布。不幸的是,许多应用程序仍然运行在旧版本的Java上,例如以前的LTS版本:Java 11和Java 8。本文解释了为什么应该升级应用程序,并帮助您实际升级到Java 17。

但首先,很多人可能会问:“为什么要升级?”

Java 的每个新版本,尤其是大版本,都会解决安全漏洞,提升性能,增加新特性。保持 Java 版本最新有助于保持应用程序的健康,也有助于组织留住现有的开发人员,并有可能吸引来新员工,因为开发人员一般更希望使用比较新的技术。

为什么会有人想要升级到最新的Java版本呢?这是有道理的,特别是当您的应用程序在Java 8、Java 11、Java 14或您正在使用的任何版本上运行得很好时。升级到Java 17需要付出努力,特别是当目标是真正利用JVM中的新语言特性和功能时。

是的,根据环境和应用程序,升级可能需要一些努力。开发人员和其他团队成员需要更新他们的本地环境。然后,构建环境和运行时环境(例如生产环境)也需要升级。

幸运的是,许多项目和组织使用Docker,这在这方面帮助很大。在我的组织中,团队定义了他们自己的持续集成/持续部署(CI/CD)管道,他们在Docker映像中运行一切。团队只需在他们的Docker映像中指定该版本,就可以升级到最新的Java版本——这不会影响使用旧Java版本的其他团队,因为这些团队可以使用旧的Docker映像。

在Kubernetes上运行的测试和生产环境也是如此。当团队想要升级到一个新的Java版本时,他们可以自己更改Docker映像,然后部署所有内容。(当然,如果您仍然拥有共享的构建环境,或者其他管理您的环境的团队,那么这个过程可能会更具挑战性。)

应用程序也可能需要一些更改。我注意到,团队发现要估计这么多工作是很有挑战性的,结果估计将一个应用程序从Java 8升级到Java 11需要几周甚至几个月的时间。这些高估值通常会导致公司因为其他优先事项而推迟升级。

我成功地升级了一个应用程序,估计只花了几天时间,主要是由于等待构建完成。这部分是由于多年的升级经验,但这也是一个刚刚开始的问题,并试图修复过程中的问题。周五下午干这活还不错;看看你走了多远,还有哪些挑战还没有完成,这样就更容易估计剩下的工作。

然而,即使有了多年的经验,在没有深入了解项目的情况下,我也无法估计一次升级需要多长时间。很大程度上取决于您的应用程序有多少依赖项。通常,将依赖项升级到最新版本可以解决Java升级期间可能出现的许多问题。

LTS版本

本文一直将Java 8、Java 11和Java 17称为LTS发行版。这是什么意思?以下是来自Oracle Java SE支持路线图的引用:

对于Java SE 8之后的产品版本,Oracle将每三年指定一个版本作为长期支持(LTS)版本。Java SE 11是一个LTS发行版。出于Oracle Premier Support的目的,非LTS版本被认为是最新LTS版本的累积实现增强集。一旦一个新特性发布可用,任何以前的非lts发布将被认为取代。例如,Java SE 9是一个非lts发行版,并立即被Java SE 10(也是非lts)取代,Java SE 10又立即被Java SE 11取代。然而,Java SE 11是一个LTS版本,因此Oracle客户将获得Oracle高级支持和定期更新版本,即使Java SE 12已经发布。

在Java升级期间需要更改什么?

您的应用程序包含您和您的团队编写的代码,它可能还包含依赖项。如果从JDK中删除某些内容,可能会破坏代码、依赖关系或两者都破坏。确保这些依赖关系是最新的,以解决这些问题通常会有所帮助。有时,在开始升级过程之前,您可能不得不等到框架发布了与最新Java版本兼容的新版本。这意味着作为升级前评估过程的一部分,您对依赖项有很好的了解。

JDK中的大多数功能并没有立即全部删除。首先,功能被标记为弃用。例如,用于XML绑定的Java体系结构(JAXB)在Java 9中被标记为弃用,在Java 11中被删除。如果您不断地更新,那么您将看到弃用,并且您可以在功能被删除之前解决这些特性的任何使用问题。但是,如果您直接从Java 8跳到Java 17,那么这个特性的删除会让您一下子都受到打击。

要查看API的变化,例如,查看在特定Java版本中哪些方法被删除或添加到String API中,请查看Marc Hoffmann和Cay Horstmann编写的《Java版本年鉴》

多版本JAR功能

如果您的应用程序被仍然使用旧JDK的客户使用,并且他们站点的升级超出了您的控制范围,该怎么办?在Java 9和JEP 238中引入的多版本JAR功能可能很有用,因为它允许您将多个Java版本(包括比Java 9早的版本)的代码打包到一个JAR文件中。

例如,创建一个Application类(清单1)和一个Student类(清单2),并将它们放在文件夹src/main/java/com/example中。Student类是运行在Java 8上的类。

清单1:Application类

public class Application {

   public static void main(String[] args) {
       Student student = new Student("James ");
       System.out.println("Implementation " + student.implementation());
       System.out.println("Student name James contains a blank: " + student.isBlankName());
   }
}

清单2:Student类使用Java 8编写的

public class Student {
   final private String firstName;

   public Student(String firstName) {
       this.firstName = firstName;
   }

   boolean isBlankName() {
       return firstName == null || firstName.trim().isEmpty();
   }

   static String implementation() { return "class"; }
}

接下来创建一个Student record(如清单3),该代码中不仅使用records(Java 14引入),还是用String.isBlank()方法(该方法在Java 11中引入)。然后再把该文件放在src/main/java17/com/example目录中

清单3:使用新的Java特性写的Student record

public record Student(String firstName) {
   boolean isBlankName() {
       return firstName.isBlank();
   }

   static String implementation() { return "record"; }
}

根据您使用的构建工具,需要进行一些配置。一个Maven的例子可以在我的GitHub存储库中找到。该示例构建在Java 17上并创建JAR文件。当在JDK 17或更新版本上执行JAR文件时,将使用Student record。当在旧版本上执行JAR文件时,将使用Student类。

这个特性非常有用,例如,如果新的api提供更好的性能,因为您可以为拥有最新Java版本的客户使用这些api。同样的JAR文件可以用于使用较旧JDK的客户,而没有性能改进。

请注意,所有的实现,在这种情况下,Student,应该有相同的公共API,以防止运行时问题。不幸的是,构建工具不验证公共api,但有些ide会。另外,在JDK 17中,您可以使用jar -validate命令来验证jar文件。

需要注意的是某些JDK版本中提供的预览功能。一些更大的特性首先作为预览版本发布,可能会在下一个jdk中形成最终的特性。这些预览特性在Java的LTS和非LTS版本中都有。这些特性是通过启用预览标志启用的,默认情况下是关闭的。如果在生产代码中使用这些预览特性,请注意它们可能在不同JDK版本之间发生变化,这可能导致需要进行一些调试或重构。

更多关于Java弃用和特性移除的信息

在升级JDK之前,确保您的IDE、构建工具和依赖项是最新的。Maven Versions Plugin和Gradle Versions Plugin会显示你有哪些依赖项,并列出最新的可用版本。

请注意,这些工具只显示您使用的工件的新版本—但是有时工件名称会更改、生成分叉或代码移动。例如,JAXB首先通过javax.xml提供。Bind:jaxb-api,但更改为jakarta.xml。将bind-api转换到Eclipse Foundation之后。要找到这样的改变,你可以使用Jonathan Lermitage的Old GroupIds Alerter Maven插件或者他的Gradle插件。

JavaFX。从Java 11开始,平台不再将JavaFX作为规范的一部分,大多数JDK构建都删除了它。您可以使用来自Gluon的独立JavaFX构建,或者将OpenJFX依赖项添加到您的项目中。

字体。以前,JDK包含一些字体,但在Java 11中它们被删除了。例如,如果使用Apache POI(用于与Microsoft office兼容的文档的Java API),则需要字体。操作系统需要提供字体,因为它们不再出现在JDK中。然而,在操作系统如Alpine Linux上,字体必须使用apt install fontconfig命令手动安装。根据您使用的字体,可能需要额外的软件包。

Java任务控制。对于监视和分析应用程序,这是一个非常有用的工具。我强烈建议你去调查一下。Java任务控制曾经包含在JDK中,但现在它可以以新的名称独立下载:JDK任务控制。

表1. Java EE模块及其当前替换

CORBA。Java的CORBA模块在Java 11中被移除,目前还没有正式的替代模块。然而,Oracle GlassFish Server包含了CORBA的实现。

Nashorn。Java 15删除了Nashorn JavaScript引擎。如果你仍然想使用引擎,你可以使用nashorn-core依赖项。

实验的编译器。Java 17删除了对GraalVM实验性的提前(AOT)和即时(JIT)编译器的支持,正如JEP 410的文档中所解释的那样。

查找不支持的主要文件

您可能会看到错误“Unsupported class file major version 61”。我在JaCoCo代码覆盖库和各种其他Maven插件中看到过它。消息的主要版本61部分指的是Java 17。因此,在这种情况下,这意味着您正在使用的框架或工具版本不支持Java 17。因此,您应该将框架或工具升级到新版本。(如果您看到一条包含主要版本60的消息,那么它与Java 16有关。)

请注意,像Kotlin和Gradle这样的一些工具目前还不支持Java 17,至少在我撰写本文时(2021年8月中旬)。有时也可以解决这个问题,例如,通过将Java 16指定为Kotlin的JVM目标。不过,我预计Java 17支持很快就会添加进来。

封装JDK内部api

Java 16和Java 17封装了JDK内部api,这将影响各种框架,比如Lombok。您可能会看到诸如模块JDK. compiler没有将com.sun.tools.javac.processing导出到未命名模块这样的错误,这意味着您的应用程序不再能够访问JDK的那一部分。

一般来说,我建议升级所有使用这些内部组件的依赖项,并确保您自己的代码不再使用它们。

如果这是不可能的,有一个解决方案仍然允许您的应用程序访问内部。例如,如果你需要访问comp模块,使用以下方法:

--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED

但是,只将这种方法作为最后的手段,最好是暂时使用,因为您正在规避Java团队添加的重要保护措施。

在Java 16中的JEP 396和Java 17中的JEP 403了解更多关于此问题的信息。

Java升级资源

我建议您查看我创建的Java Upgrades GitHub存储库,其中包含可以在升级过程中帮助您的示例、常见错误和解决方案。

结论

升级您的依赖项并为已删除的JDK特性添加依赖项可以解决许多Java升级挑战。我建议采用结构化的方法逐步升级:首先,确保代码能够编译,然后运行测试,然后运行应用程序。

如果您可以告诉您自己、您的团队和您的公司,您可以在JDK 17上编译和测试所有内容,而不必告诉他们几乎完成了,或者更糟糕的是,它只完成了80%,那么迁移过程就会好得多。

我的个人经验是,从JDK 11升级到JDK 17要比从JDK 8升级到JDK 11容易得多。然而,在这两个场景中,对于重要的应用程序来说,需要花费数小时到数天的时间,这主要是由于要等待构建完成。

我希望本文能够简化您的升级过程。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值