为社么以及如何升级至Java 16或17

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在2021年4月27日的InfoQ直播中,我探讨了为什么应该考虑"},{"type":"link","attrs":{"href":"https:\/\/www.infoq.com\/presentations\/upgrade-java16-java17\/","title":"","type":null},"content":[{"type":"text","text":"升级到Java 16或Java 17"}]},{"type":"text","text":"(一旦发布),并就如何完成升级提供了一些实用的建议。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"直播的内容基于我个人的GitHub库"},{"type":"link","attrs":{"href":"https:\/\/github.com\/johanjanssen\/JavaUpgrades","title":"","type":null},"content":[{"type":"text","text":"JavaUpgrades"}]},{"type":"text","text":",其中有文档和示例介绍了升级到Java 16或Java 17时常见的难题和异常。其中也有具体的解决方案,你可以用在自己的应用程序中。示例要用Docker运行,是用Maven构建的,但是你当然也可以设置自己的Gradle构建。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本文以及那次直播都是为了让用户可以轻松升级到Java 16或Java 17。大部分常见的升级任务都讨论到了,所以你可以更容易地解决它们,并专注于克服应用程序所特有的挑战。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"为什么要升级?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Java的每个新版本,尤其是大版本,都会解决安全漏洞,提升性能,增加新特性。保持Java版本最新有助于保持应用程序的健康,也有助于组织留住现有的开发人员,并有可能吸引来新员工,因为开发人员一般更希望使用比较新的技术。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"升级有时会被视为一项挑战"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"人们认为,升级到Java的新版本需要很大的工作量。这是因为代码库需要变更,还需要在所有构建和运行应用程序的服务器中安装Java的最新版本。幸运的是,有些公司使用了Docker,团队可以让它们自己升级这些内容。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"许多人将Java 9模块系统(即Jigsaw)视为一项重大的挑战。然而,Java 9并不需要你显式地使用模块系统。事实上,大多数运行在Java 9以及更高版本上的应用程序并没有在代码库中配置Java模块。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"评估任何升级所需的工作量都是一项挑战。那取决于多种因素,如依赖项数量及其现状。举例来说,如果你使用的是Spring Boot,那么升级Spring Boot可能已经解决大部分升级问题。遗憾的是,由于存在不确定性,大部分开发人员会将升级工作量评估为许多天、周甚或是月。如此一来,考虑成本、时间或其他优先事项,组织或管理层就会推迟升级。我以前见过人们对将Java 8应用程序升级到Java 11的工作量评估从数周到数月不等。不过,我曾在几天内完成了一次类似的升级。这一部分是因为我之前的经验,不过,这也得益于我没有多想就开始了升级过程。周五下午升级Java就很理想,看看会发生什么。我最近将一个Java 11应用程序升级到了Java 16,我唯一需要完成的任务就是升级一个"},{"type":"link","attrs":{"href":"https:\/\/projectlombok.org\/","title":"","type":null},"content":[{"type":"text","text":"Lombok"}]},{"type":"text","text":"依赖项。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"升级可能很困难,评估所需的时间似乎是不可能的,但通常,实际的升级过程不会花那么多时间。在许多应用程序升级中,我都见过同样的问题。我希望帮助团队快速解决重复出现的问题,让他们可以集中精力克服应用程序独有的挑战。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Java的发版节奏"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"过去,"},{"type":"link","attrs":{"href":"https:\/\/en.wikipedia.org\/wiki\/Java_version_history","title":"","type":null},"content":[{"type":"text","text":"Java每两年发布一个新版本"}]},{"type":"text","text":"。然而,从Java 9发布之后,新版本发布变成了每6个月一次,长期支持版本(LTS)每3年一次。大多数非长期支持版本都通过小版本升级提供大约6个月的支持,直到下一个版本发布。另一方面,LTS版本几年内都会收到小版本升级,至少到下个LTS版本发布。实际提供支持的时间可能会更长,这取决于OpenJDK的供应商(Adoptium、Azul、Corretto等)。举例来说,Azul对于非LTS版本提供的支持时间就比较长。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/imgopt.infoq.com\/fit-in\/625x1000\/filters:quality(80)\/filters:no_upscale()\/articles\/why-how-upgrade-java17\/en\/resources\/1Release%20cadence-1630173649116.jpg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你可能会问自己,“我应该总是升级到最新版本,还是应该停留在一个LTS版本上?”保证应用程序使用的是LTS版本意味着你可以利用小版本升级带来的各种改进,尤其是与安全相关的那些。另一方面,在使用最新的非LTS版本时,你应该每隔6个月就升级到一个新的非LTS版本,否则就无法利用小版本升级了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然而,每6个月升一次级是一项不小的挑战,因为在升级应用程序之前,你可能不得不等待你所使用的框架完成升级。但是,你应该也不会等待太长时间,因为非LTS版本的小版本很快就会不再发布了。在我们公司,我们目前决定停留在LTS版本上,因为我们觉得自己没有时间每6个月升级一次,这样一个时间窗口太小。不过也不绝对,如果团队真得需要,或者一个非LTS版本带来了有趣的Java新特性,那么我们也可能改变决定。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"升级到什么版本?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一般来说,应用程序由依赖项和你自己的代码(打包后在JDK上运行)构成。如果JDK中有什么修改,那么依赖项或\/和你自己的代码就需要修改。在大多数情况下,这是由JDK移除了某项特性导致的。如果你的依赖项使用了一项已经移除的JDK特性,那么请保持耐心,等待该依赖项的新版本发布。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"多JDK版本"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"当升级应用程序时,你可能希望使用JDK的不同版本,如最新版本用于实际的升级,老版本用于保持应用程序的运行。用于应用程序开发的当前JDK版本可以通过环境变量"},{"type":"codeinline","content":[{"type":"text","text":"JAVA_HOME"}]},{"type":"text","text":"指定,也可以借助包管理工具"},{"type":"link","attrs":{"href":"https:\/\/sdkman.io\/","title":"","type":null},"content":[{"type":"text","text":"SDKMAN!"}]},{"type":"text","text":"或"},{"type":"link","attrs":{"href":"https:\/\/www.infoq.com\/news\/2021\/08\/jdkmon-java\/","title":"","type":null},"content":[{"type":"text","text":"JDKMon"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"对于我"},{"type":"link","attrs":{"href":"https:\/\/github.com\/johanjanssen\/JavaUpgrades","title":"","type":null},"content":[{"type":"text","text":"GitHub库"}]},{"type":"text","text":"中的示例,我使用Docker和不同的JDK版本来说明特定的特性如何工作或造成破坏。你可以试一下相关特性,而不必安装多个JDK版本。遗憾的是,使用Docker容器的反馈回路有点长。需要首先构建并运行镜像。所以一般来说,我建议你尽可能从IDE内升级。但是,在一个干净的、没有个性化设置的Docker容器环境中试验一些东西或构建应用程序或许是一个不错的注意。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"为了说明这一点,我们创建了一个标准的"},{"type":"codeinline","content":[{"type":"text","text":"Dockerfile"}]},{"type":"text","text":" 文件,其中包含下面的内容。该示例使用了Maven JDK 17镜像,并将你的应用程序代码复制到里面。"},{"type":"codeinline","content":[{"type":"text","text":"RUN"}]},{"type":"text","text":" 命令会运行所有测试,出错了也不会失败。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"FROM maven:3.8.1-openjdk-17-slim\n\nADD . \/yourproject\nWORKDIR \/yourproject\n\nRUN mvn test --fail-at-end\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"要想构建上述镜像,则运行"},{"type":"codeinline","content":[{"type":"text","text":"docker build"}]},{"type":"text","text":" 命令,并通过"},{"type":"codeinline","content":[{"type":"text","text":"-t"}]},{"type":"text","text":" 指定标签(或名称),通过"},{"type":"codeinline","content":[{"type":"text","text":"."}]},{"type":"text","text":" 配置上下文,在本例中是当前目录。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"docker build -t javaupgrade .\n"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"准备工作"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"大多数开发人员都是从升级本地环境开始,然后是构建服务器,最后是各部署环境。不过,我有时候会直接在构建服务器上使用新版本的Java进行构建,而不是针对这个特定的项目做好所有配置,然后看看会出什么问题。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一次性从Java 8升级到17也是可以的。不过,如果你遇到任何问题,可能会很难确定这两个Java版本间的哪个新特性导致了问题。小步升级,比如从Java 8升级到Java 11,定位问题会比较容易。而且,在你搜索问题原因时,加上Java版本也是有帮助的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我建议在旧版本的Java上升级依赖项。那样你可以专注于让依赖项可以正常工作,而不必同时升级Java。遗憾的是,有时候没法这样做,因为有些依赖项需要更新的Java版本。如果是这样,你就别无选择,只能同时升级Java和依赖项了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Maven和Gradle提供了一些插件,可以显示依赖项的新版本。"},{"type":"codeinline","content":[{"type":"text","text":"mvn versions:display-dependency-updates"}]},{"type":"text","text":" 命令会调用"},{"type":"link","attrs":{"href":"https:\/\/www.mojohaus.org\/versions-maven-plugin\/","title":"","type":null},"content":[{"type":"text","text":"Maven版本插件"}]},{"type":"text","text":"。该插件会列出有新版本可用的依赖项:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"[INFO] --- versions-maven-plugin:2.8.1:display-dependency-updates (default-cli) @ mockito_broken ---\n[INFO] The following dependencies in Dependencies have newer versions:\n[INFO] org.junit.jupiter:junit-jupiter .................... 5.7.2 -> 5.8.0-M1\n[INFO] org.mockito:mockito-junit-jupiter ................... 3.11.0 -> 3.11.2\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在"},{"type":"codeinline","content":[{"type":"text","text":"build.gradle"}]},{"type":"text","text":" 文件中配置好插件后,"},{"type":"codeinline","content":[{"type":"text","text":"gradle dependencyUpdates -Drevision=release"}]},{"type":"text","text":" 命令会调用"},{"type":"link","attrs":{"href":"https:\/\/github.com\/ben-manes\/gradle-versions-plugin","title":"","type":null},"content":[{"type":"text","text":"Gradle版本插件"}]},{"type":"text","text":":"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"plugins {\n id \"com.github.ben-manes.versions\" version \"$version\"\n }\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"升级完依赖项后,就可以升级Java了。要想把代码改到在新版本的Java上运行,最好是在IDE中进行,以确保它支持Java的最新版本。最后,将构建工具升级到最新版本,并配置Java版本:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"\n org.apache.maven.plugins\n maven-compiler-plugin\n 3.8.1\n \n 17\n \n\nplugins {\n java {\n toolchain {\n languageVersion = JavaLanguageVersion.of(16)\n }\n }\n}\ncompile 'org.apache.maven.plugins:maven-compiler-plugin:3.8.1'\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不要忘了把Maven和Gradle插件升级到最新版本。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"JDK中移除的特性"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"JDK中总是有些元素可能被移除,包括方法、证书、垃圾收集算法、JVM选项,甚至是整个工具。不过,在大多数情况下,这些被移除的部分在删除之前已经被标记为“已废弃”或“将移除”。举例来说,JAXB在Java 9中已废弃,但最终移除是在"},{"type":"link","attrs":{"href":"https:\/\/openjdk.java.net\/projects\/jdk\/11\/","title":"","type":null},"content":[{"type":"text","text":"Java 11"}]},{"type":"text","text":"中。如果你已经解决了与已废弃的特性相关的问题,那么在特性真正被移除时也就不用担心了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以参考"},{"type":"link","attrs":{"href":"https:\/\/javaalmanac.io\/","title":"","type":null},"content":[{"type":"text","text":"Java Version Almanac"}]},{"type":"text","text":"和"},{"type":"link","attrs":{"href":"https:\/\/foojay.io\/almanac\/jdk-16\/","title":"","type":null},"content":[{"type":"text","text":"Foojay Almanac"}]},{"type":"text","text":"对Java不同版本的比较,看看增加了哪些项,废弃了哪些项,或者是移除了哪些项。以"},{"type":"link","attrs":{"href":"https:\/\/openjdk.java.net\/jeps\/0","title":"","type":null},"content":[{"type":"text","text":"Java增强提案(JEP)"}]},{"type":"text","text":"这种形式所做的高级变更可以在"},{"type":"link","attrs":{"href":"https:\/\/openjdk.java.net\/jeps\/0","title":"","type":null},"content":[{"type":"text","text":"OpenJDK"}]},{"type":"text","text":"网站上查看。关于每个Java版本的详细信息,可以查阅Oracle公布的"},{"type":"link","attrs":{"href":"https:\/\/www.oracle.com\/java\/technologies\/javase\/16-relnote-issues.html","title":"","type":null},"content":[{"type":"text","text":"发布说明"}]},{"type":"text","text":"。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Java 11"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https:\/\/openjdk.java.net\/projects\/jdk\/11\/","title":"","type":null},"content":[{"type":"text","text":"Java 11"}]},{"type":"text","text":"移除了多个特性。首先是JavaFX,它已经不在规范中,也不再捆绑在OpenJDK中。不过,有的供应商提供的JDK构建包含的内容比规范里的多。例如,"},{"type":"link","attrs":{"href":"https:\/\/github.com\/ojdkbuild\/ojdkbuild","title":"","type":null},"content":[{"type":"text","text":"ojdkbuild"}]},{"type":"text","text":"和"},{"type":"link","attrs":{"href":"https:\/\/bell-sw.com\/pages\/downloads\/","title":"","type":null},"content":[{"type":"text","text":"Liberica JDK"}]},{"type":"text","text":"的完整JDK都包含了"},{"type":"link","attrs":{"href":"https:\/\/openjfx.io\/","title":"","type":null},"content":[{"type":"text","text":"OpenJFX"}]},{"type":"text","text":"。此外,你也可以使用"},{"type":"link","attrs":{"href":"https:\/\/gluonhq.com\/","title":"","type":null},"content":[{"type":"text","text":"Gluon"}]},{"type":"text","text":"提供的"},{"type":"link","attrs":{"href":"https:\/\/gluonhq.com\/products\/javafx\/","title":"","type":null},"content":[{"type":"text","text":"JavaFX"}]},{"type":"text","text":"构建,或者向应用程序添加OpenJFX依赖。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在JDK 11之前,有些字体是包含在JDK中的。例如,"},{"type":"link","attrs":{"href":"https:\/\/poi.apache.org\/","title":"","type":null},"content":[{"type":"text","text":"Apache POI"}]},{"type":"text","text":"可以把这些字体用于Word和Excel文档。然而,在JDK 11开始,就不再提供那些字体了。如果操作系统也没有提供,那么你可能就会遇到一些奇怪的错误。解决方案是在操作系统上安装字体。根据你在应用程序中使用的字体,你可能需要安装更多的包:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"apt install fontconfig\nOptional: libfreetype6 fontconfig fonts-dejavu\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https:\/\/www.oracle.com\/java\/technologies\/jdk-mission-control.html","title":"","type":null},"content":[{"type":"text","text":"Java Mission Control"}]},{"type":"text","text":"(JMC)是一个监控和性能分析应用程序,它开销很小,可以在包括生产环境在内的任何环境中对应用程序做性能分析。如果你没用过,我强烈建议你用一下。它不再是JDK的一部分,但"},{"type":"link","attrs":{"href":"https:\/\/adoptopenjdk.net\/jmc.html","title":"","type":null},"content":[{"type":"text","text":"AdoptOpenJDK"}]},{"type":"text","text":"和"},{"type":"link","attrs":{"href":"https:\/\/www.oracle.com\/java\/technologies\/jdk-mission-control.html","title":"","type":null},"content":[{"type":"text","text":"Oracle"}]},{"type":"text","text":"给它起了一个新名字JDK Mission Control,并提供了单独的下载包。Java 11的最大变化是移除了"},{"type":"link","attrs":{"href":"https:\/\/openjdk.java.net\/jeps\/320","title":"","type":null},"content":[{"type":"text","text":"Java EE和CORBA"}]},{"type":"text","text":"模块,如4个Web服务API——"},{"type":"link","attrs":{"href":"https:\/\/jcp.org\/en\/jsr\/detail?id=224","title":"","type":null},"content":[{"type":"text","text":"JAX-WS"}]},{"type":"text","text":"、"},{"type":"link","attrs":{"href":"https:\/\/jcp.org\/en\/jsr\/detail?id=222","title":"","type":null},"content":[{"type":"text","text":"JAXB"}]},{"type":"text","text":"、"},{"type":"link","attrs":{"href":"https:\/\/jcp.org\/en\/jsr\/detail?id=925","title":"","type":null},"content":[{"type":"text","text":"JAF"}]},{"type":"text","text":"和"},{"type":"link","attrs":{"href":"https:\/\/jcp.org\/en\/jsr\/detail?id=250","title":"","type":null},"content":[{"type":"text","text":"Common Annotations"}]},{"type":"text","text":"——因为已经包含在Java EE中,所以被认为是多余的。在2017年发布后不久,Oracle就将Java EE 8贡献给了Eclipse基金会,旨在使Java EE开源。考虑到Oracle的品牌策略,有必要"},{"type":"link","attrs":{"href":"https:\/\/www.infoq.com\/news\/2018\/03\/java-ee-becomes-jakarta-ee\/","title":"","type":null},"content":[{"type":"text","text":"将Java EE重命名为Jakarta EE"}]},{"type":"text","text":",并"},{"type":"link","attrs":{"href":"https:\/\/www.infoq.com\/news\/2020\/10\/the-road-to-jakartaee-9\/","title":"","type":null},"content":[{"type":"text","text":"将命名空间从"}]},{"type":"codeinline","content":[{"type":"text","text":"javax"}]},{"type":"link","attrs":{"href":"https:\/\/www.infoq.com\/news\/2020\/10\/the-road-to-jakartaee-9\/","title":"","type":null},"content":[{"type":"text","text":" 迁移到"}]},{"type":"codeinline","content":[{"type":"text","text":"jakarta"}]},{"type":"text","text":"。因此,在使用像JAXB这样的依赖项时,确保自己使用了比较新的Jakarta EE工件。例如,JAXB工件的Java EE 8版本名为"},{"type":"codeinline","content":[{"type":"text","text":"javax.xml.bind:jaxb-api"}]},{"type":"text","text":" ,后续开发于2018年停止。JAXB的Jakarta EE版本在新工件"},{"type":"codeinline","content":[{"type":"text","text":"jakarta.xml.bind:jakarta.xml.bind-api"}]},{"type":"text","text":" 下继续开发。务必确保应用程序中所有的导入都已经改为了新命名空间"},{"type":"codeinline","content":[{"type":"text","text":"jakarta"}]},{"type":"text","text":" 。例如,对于JAXB,将"},{"type":"codeinline","content":[{"type":"text","text":"javax.xml.bind.*"}]},{"type":"text","text":" 改为"},{"type":"codeinline","content":[{"type":"text","text":"jakarta.xml.bind.*"}]},{"type":"text","text":" ,并添加相关依赖项。下图中左边的列是受这项变更影响的模块。右边两列显示了可以用作依赖项的"},{"type":"codeinline","content":[{"type":"text","text":"groupId"}]},{"type":"text","text":" 和"},{"type":"codeinline","content":[{"type":"text","text":"artifactId"}]},{"type":"text","text":" 。请注意,JAXB和JAX-WS都需要两个依赖项:一个用于API,一个用于实现。官方没有提供CORBA的替代方案,但Glassfish还是提供了一个可用的工件。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/imgopt.infoq.com\/fit-in\/625x1000\/filters:quality(80)\/filters:no_upscale()\/articles\/why-how-upgrade-java17\/en\/resources\/1Jakarta-1630173649116.jpg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Java 15"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https:\/\/openjdk.java.net\/projects\/jdk\/15\/","title":"","type":null},"content":[{"type":"text","text":"Java 15"}]},{"type":"text","text":"移除了JavaScript引擎Nashorn,不过,你仍然可以通过添加以下依赖项来使用:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"\n org.openjdk.nashorn\n nashorn-core\n 15.2\n\n"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Java 16"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在这个"},{"type":"link","attrs":{"href":"https:\/\/openjdk.java.net\/projects\/jdk\/16\/","title":"","type":null},"content":[{"type":"text","text":"版本"}]},{"type":"text","text":"中,JDK开发者封装了一些JDK内部构件。他们不希望应用程序再使用JDK的底层API。这主要影响了Lombok这样的工具。所幸,Lombok几个周内就发布了一个新版本,解决了这个问题。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果你有任何代码或依赖项仍然使用JDK内部构件,那么可以尝试使用JDK的高级API来解决这个问题。如果不行的话,Maven还提供了一种变通方法:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"\n org.apache.maven.plugins\n maven-compiler-plugin\n 3.8.1\n \n true\n \n -J--add-opens=jdk.compiler\/com.sun.tools.javac.comp=ALL-UNNAMED\n -J--add-opens=jdk.compiler\/com.sun.tools.javac.file=ALL-UNNAMED\n -J--add-opens=jdk.compiler\/com.sun.tools.javac.main=ALL-UNNAMED\n -J--add-opens=jdk.compiler\/com.sun.tools.javac.model=ALL-UNNAMED\n -J--add-opens=jdk.compiler\/com.sun.tools.javac.parser=ALL-UNNAMED\n -J--add-opens=jdk.compiler\/com.sun.tools.javac.processing=ALL-UNNAMED\n -J--add-opens=jdk.compiler\/com.sun.tools.javac.tree=ALL-UNNAMED\n -J--add-opens=jdk.compiler\/com.sun.tools.javac.util=ALL-UNNAMED\n -J--add-opens=jdk.compiler\/com.sun.tools.javac.jvm=ALL-UNNAMED\n \n \n\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我曾尝试使用"},{"type":"link","attrs":{"href":"https:\/\/maven.apache.org\/guides\/mini\/guide-using-toolchains.html","title":"","type":null},"content":[{"type":"text","text":"Maven Toolchains"}]},{"type":"text","text":"通过在"},{"type":"codeinline","content":[{"type":"text","text":"pom.xml"}]},{"type":"text","text":" 文件中指定JDK版本来实现JDK切换。很遗憾,当使用Lombok的旧版本在Java 16上运行应用程序时报错了:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project broken: Compilation failure -> [Help 1]\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上面就是全部报错信息。我不知道你怎么看,但在我看来,这没什么用,所以我提交了这个"},{"type":"link","attrs":{"href":"https:\/\/issues.apache.org\/jira\/browse\/MCOMPILER-460","title":"","type":null},"content":[{"type":"text","text":"问题"}]},{"type":"text","text":"。如果这个问题修复了,那么使用Maven Toolchains切换版本是一种不错的方法。后来,我直接在Java 16上运行代码,得到了一个更具描述性的错误,其中提到了我之前展示的部分变通方案:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"… class lombok.javac.apt.LombokProcessor (in unnamed module @0x21bd20ee) cannot access class com.sun.tools.javac.processing.JavacProcessingEnvironment \n(in module jdk.compiler) because module jdk.compiler does not export com.sun.tools.javac.processing \nto unnamed module …\n"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Java 17"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"JDK维护人员已经就"},{"type":"link","attrs":{"href":"https:\/\/openjdk.java.net\/projects\/jdk\/17\/","title":"","type":null},"content":[{"type":"text","text":"9月份要发布的内容"}]},{"type":"text","text":"达成了一致。"},{"type":"link","attrs":{"href":"https:\/\/openjdk.java.net\/jeps\/398","title":"","type":null},"content":[{"type":"text","text":"Applet API将被废弃"}]},{"type":"text","text":",因为浏览器停止支持Applet已经很长时间了。实验性的AOT和JIT编译器也将被移除。作为实验性编译器的替代方案,你可以使用"},{"type":"link","attrs":{"href":"https:\/\/www.graalvm.org\/","title":"","type":null},"content":[{"type":"text","text":"GraalVM"}]},{"type":"text","text":"。最大的变化是JEP-403:"},{"type":"link","attrs":{"href":"https:\/\/www.infoq.com\/news\/2021\/06\/internals-encapsulated-jdk17\/","title":"","type":null},"content":[{"type":"text","text":"强封装的JDK内部构件"}]},{"type":"text","text":"。Java选项"},{"type":"codeinline","content":[{"type":"text","text":"--illegal-access"}]},{"type":"text","text":" 已经无效,如果你仍然试图访问一个内部API,则会抛出如下异常:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"java.lang.reflect.InaccessibleObjectException: \n Unable to make field private final {type} accessible:\n module java.base does not \"opens {module}\" to unnamed module {module}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"大多数时候,这可以通过升级依赖项或使用高级API来解决。如果不行的话,你可以使用"},{"type":"codeinline","content":[{"type":"text","text":"--add-opens"}]},{"type":"text","text":" 参数来获得对内部API的访问。不过,除非不得已不要这样做。注意,有些工具在Java 17上还无法运行。例如,Gradle就无法构建项目,而Kotlin不能使用"},{"type":"codeinline","content":[{"type":"text","text":"jvmTarget = \"17\""}]},{"type":"text","text":" 。有些框架,如"},{"type":"link","attrs":{"href":"https:\/\/site.mockito.org\/","title":"","type":null},"content":[{"type":"text","text":"Mockito"}]},{"type":"text","text":",在Java 17上也有些小问题。"},{"type":"codeinline","content":[{"type":"text","text":"enum"}]},{"type":"text","text":" 字段中的方法会导致这个特定的"},{"type":"link","attrs":{"href":"https:\/\/github.com\/mockito\/mockito\/issues\/2315","title":"","type":null},"content":[{"type":"text","text":"问题"}]},{"type":"text","text":"。不过,我估计大部分问题都会在Java 17发布之前或发布之后短期内得到解决。对于任何插件或依赖项,你可能会在构建应用程序时看到这条消息“"},{"type":"text","marks":[{"type":"italic"}],"text":"不支持的类文件主版本61"},{"type":"text","text":"”。类文件主版本61用于Java 17,60用于Java 16。这基本上是说该插件或依赖项不能用于那个Java版本。大多数时候,升级到最新版本就可以解决问题。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"完工"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在解决了所有挑战之后,你终于可以在Java 17上运行应用程序了。经过努力,你现在可以使用令人兴奋的Java新特性了,如"},{"type":"link","attrs":{"href":"https:\/\/www.infoq.com\/news\/2020\/08\/java16-records-instanceof\/","title":"","type":null},"content":[{"type":"text","text":"记录和模式匹配"}]},{"type":"text","text":"。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"小结"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"升级Java是一项挑战,不过这也要看你的Java版本和依赖项有多老,你的环境配置有多复杂。本文旨在帮助你解决Java升级时最常见的挑战。一般来说,很难评估实际的升级工作要花费多长时间。我觉得,大多数时候,从Java 11升级到Java 17要比从Java 8升级到Java 11简单。对于大多数应用程序,从一个LTS版本升级到下一个LTS版本需要几个小时到几天的时间。大部分时间都花在了构建应用程序上。重要的是先开始,然后逐步更改。这样可以激励自己、团队和管理层继续努力。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你开始升级应用程序了吗?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"作者简介:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"Johan Janssen"},{"type":"text","text":"是Sanoma Learning教育部门的一名软件架构师。他特别喜欢分享Java相关的知识。他在Devoxx、Oracle Code One、Devnexus等会议上做过演讲。他通过参与计划委员会来协助大会组织,发起并组织了JVMCON。他得过的奖项有JavaOne Rock Star和Oracle Code One Star。他在数字和印刷媒体上撰写了各种文章。他是Chocolatey各种Java JDK\/JRE包的维护者,每月有大约10万次下载。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"原文链接:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https:\/\/www.infoq.com\/articles\/why-how-upgrade-java17\/","title":"","type":null},"content":[{"type":"text","text":"Why and How to Upgrade to Java 16 or 17"}]}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章