本文将帮助你如何将应用程序从Java 8迁移到java11(纯干货分享)

883 阅读7分钟

所以想迁移到Java 11,但是你的Maven项目仍然停留在Java 8上吗?你不太关心新的模块系统在Java 9中引入的,只想让你的应用程序在最新的JDK版本上运行?那么这个向导是给你的。它包含了我在将我们的产品迁移到Java 11时学到的所有知识。

从2019年起,Oracle Java 8将不再接收免费的安全更新。所以现在是时候迁移到JDK 11.

清理你的pom.xml档案

在考虑升级Java版本之前,应该做的第一件事是清理pom.xml档案。如果你的项目是一个多模块maven项目,那么它有助于建立父POM并进行维护。dependencyManagement朗德pluginManagement在这个文件里。这样,所有的插件和依赖项都定义在一个文件中,而不是分散在多个POM文件中,这使得管理版本更加容易。

为了将项目迁移到最新的JavaVersion 11,强烈建议将尽可能多的插件和依赖项更新到最新的稳定版本。如果使用旧版本,许多插件(如编译器插件、尽到火插件或故障安全插件)与Java 9不兼容。而且,许多库不兼容,不需要迁移到最新版本。

确保在主POM中配置了版本插件:

<plugin> 
<groupId>org.codehaus.mojo</groupId> 
<artifactId>versions-maven-plugin</artifactId> 
<version>2.5</version> 
<configuration> 
<generateBackupPoms>false</generateBackupPoms> 
</configuration>
</plugin>

此插件有助于查找模块的最新插件或依赖版本。打开终端并执行以下命令,查找必须更新的插件版本:

mvn versions:display-plugin-updates

将看到一个插件列表在项目中使用的更新版本可用。更新所有这些插件到最新的稳定版本。在更新插件版本之后,请确保项目仍然正确编译和运行。

你可以用mvn -N ...从您的项目根目录,只检查您的父POM,以防止多模块项目。

为Java 11配置插件

Java 11中最重要的插件是编译器插件、确定性插件(用于单元测试)和故障安全插件(用于集成测试)。

为了编译Java 11的项目,添加release配置到编译器插件,这是一个新的编译器参数,用于替换source和target版本参数:

<plugin> 
 <groupId>org.apache.maven.plugins</groupId> 
 <artifactId>maven-compiler-plugin</artifactId> 
 <version>3.8.0</version> 
 <configuration> 
 <release>11</release> 
 </configuration>
</plugin>

另外,不要忘记将IDE项目SDK设置为相同的JDK版本。在IntelliJIDEA中,转到模块设置->Project->SDK。

为了确保火和故障安全插件,我们添加了一个额外的参数。--illegal-access=permit若要允许第三方库的所有反射访问,请执行以下操作:

<plugin>
 <groupId>org.apache.maven.plugins</groupId> 
 <artifactId>maven-surefire-plugin</artifactId> 
 <version>2.22.0</version> 
 <configuration> 
 <argLine> 
 --illegal-access=permit 
 </argLine> 
 </configuration>
</plugin>
<plugin> 
 <groupId>org.apache.maven.plugins</groupId> 
 <artifactId>maven-failsafe-plugin</artifactId> 
 <version>2.22.0</version>
 <configuration> 
 <argLine> 
 --illegal-access=permit 
 </argLine> 
 </configuration>
</plugin>

只有当依赖项大量使用反射时,才需要这样做。如果您不确定是否需要这个,可以添加argLine稍后如果你的测试遇到麻烦。

当库试图通过setAccessible(true):

WARNING: Please consider reporting this to the maintainers of 
org.codehaus.groovy.reflection.CachedClass
WARNING: Use --illegal-access=warn to enable warnings of further illegal 
reflective access operations
WARNING: All illegal access 
operations will be denied in a future 
release

请记住,稍后您可能还必须通过--illegal-access=permit启动应用程序时使用。

更新依赖关系

如前所述,最好的方法是将所有依赖项迁移到最新的稳定版本,以确保Java 11一切正常。javassist, cglib, asm或byte-buddy...这些库通常是作为传递依赖项出现的,所以至少要确保这些库是最新的。

<dependency> 
 <groupId>org.javassist</groupId>
 <artifactId>javassist</artifactId>
 <version>3.23.1-GA</version>
</dependency>
<dependency>
 <groupId>cglib</groupId>
 <artifactId>cglib-nodep</artifactId>
 <version>3.2.7</version>
</dependency>

此命令有助于从模块中查找过时的依赖版本:

mvn versions:display-dependency-updates

更新尽可能多的libary到最新的稳定版本。如果项目中存在一些由于兼容性问题而无法更新的依赖项,那么请保持原样。很可能它在Java 11中运行得很好。

现在是第一次用JDK 11编译项目的好时机:

mvn clean test-compile compile

提示:您可以使用并行构建来加速多模块Maven项目。mvn -T 4 compile在4个CPU核上并行编译所有模块。

最终将面临不同的编译器错误,例如ClassNotFoundException...每个项目都是不同的,所以我不能为你将要面对的每一个问题提供解决方案。本文的其余部分描述了为了使用JDK 11运行应用程序而必须解决的各种问题的解决方案。

添加缺失模块

随着Java 9中Java模块系统的引入,Java标准库被划分为不同的模块。虽然大多数类仍然可用,没有任何更改,但有些没有。必须明确定义你的应用程序需要访问哪些其他模块,或者只需从Maven中央存储库中添加这些模块即可。

命令java --list-modules列出所有可用模块。

在将web项目迁移到Java 11时,我们必须添加jaxb和javax.annotations防止ClassNotFoundException...我们将下面的libary作为附加的Maven依赖项添加到我们的Poms中:

<dependency> 
 <groupId>javax.annotation</groupId>
 <artifactId>javax.annotation-api</artifactId>
 <version>1.3.2</version>
</dependency>
<dependency>
 <groupId>javax.xml.bind</groupId>
 <artifactId>jaxb-api</artifactId>
 <version>2.4.0-b180725.0427</version>
</dependency>
<dependency>
 <groupId>org.glassfish.jaxb</groupId>
 <artifactId>jaxb-runtime</artifactId>
 <version>2.4.0-b180725.0644</version>
</dependency>

而不是通过Maven添加那些libary,我们可以利用–add-modules参数来向项目中添加额外的JDK模块。

固定sun.*和com.sun.*进口品

虽然有些类已被移动到其他Java模块,但其他类已不能再在用户代码中使用,即sun.*包和一些类com.sun.*...如果由于代码链接到这些包中的类而导致编译器错误,则必须从代码中删除这些导入。

下面是我们在项目中必须解决的一些问题:

  • sun.misc.BASE64Encoder*这可以简单地改为java.util.Base64.getEncoder()它可以从Java 8中获得。
  • sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl这个类意外地被使用在我们的代码库中,可以简单地用接口类型来替换。java.lang.reflect.ParameterizedType.
  • sun.reflect.annotation.AnnotationParser:我们使用这个类以编程方式创建注释实例。类不再可访问,但可以由AnnotationFactory从Hibernate Validator那里。
  • com.sun.org.apache.xml.internal.utils.DefaultErrorHandler:我们已经用接口的自定义实现替换了这个类。

货币格式

我们遇到了一个奇怪的例子,其中包含了区域设置的数字格式,例如Locale.GERMANY这让我们的测试失败了,出现了一个相当奇怪的断言错误:

java.lang.AssertionError:
Expected: is "9,80 €"
 but: was "9,80 €"

底层代码使用NumberFormat.getCurrencyInstance(Locale.GERMANY)将数字格式化为德国货币格式。这到底是怎么回事?

javas数字格式已被修改为使用非破缺空间而不是数字和货币符号之间的正常空格。这一更改非常有意义,因为它防止了数字和货币符号之间的不同表示格式之间的换行符。将测试中的字符串更改为使用非中断空间(在MacOSX键盘上使用选项空间)解决了这个问题。

Servlet容器

在使用ApacheTomcat运行Web应用程序时,至少需要ApacheTomcat 7.0.85或更高版本。否则,Tomcat将不会在Java 9和更高版本上启动,您将看到以下错误:

/path/to/apache-tomcat-7.0.64/bin/catalina.sh run
-Djava.endorsed.dirs=/path/to/apache-tomcat-7.0.64/endorsed is not
supported. Endorsed standards and standalone APIs
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will ex
it.in modular form will be supported via the concept of upgradeable 
modules.
Disconnected from server

另外,不要忘记最终添加附加的启动参数--illegal-access=permit到servlet容器。

就这样

我希望这些技巧对你有所帮助,并帮助你将应用程序从Java 8迁移到11。

欢迎大家点赞关注,转发,此外给大家整理了一些详细的java资料,私信我“资料”即可获取