Java 9迁移指南:七个最常见的挑战

我确定您已经听说过更新到Java 9并不是一件容易的事,甚至可能是不兼容的更新,而且对于大型代码库而言,迁移毫无意义。 这样做之后,我迁移了一个相当大的旧代码库,我可以告诉你,这还不错。 比碰到Java 8确实要花更多的时间,但是要花很多时间。 迁移最重要的是,发现了一些小问题,甚至是一些很小的问题,无论迁移本身如何,都需要解决,我们借此机会做到了。

我在java9.wtf上收集了一些令人惊讶的细节,但是将这七个最大的问题浓缩到了Java 9迁移指南中。 它既是帖子,又是可以返回的资源,因此请将其放在快速拨号上,并在遇到具体问题时进行搜索。 还要注意,尽管您需要了解一些有关模块系统的信息 (这是一个动手指南 ),但这并不是关于模块化应用程序的问题–仅仅是关于使其能够在Java 9上编译和运行。

非法访问内部API

模块系统的最大卖点之一是坚固的封装。 这样可以确保从模块外部无法访问非公共类以及非导出包中的类。 首先,这当然适用于JDK附带的平台模块,其中仅完全支持java。*和javax。*软件包。 另一方面,大多数com.sun。*和sun。*软件包是内部的,因此默认情况下无法访问。

尽管Java 9编译器的行为完全符合您的期望并防止了非法访问,但在运行时却并非如此。 为了提供少量的向后兼容性,它通过允许对内部类的访问,简化了迁移并提高了在Java 8上构建的应用程序在Java 9上运行的机会。 如果使用反射进行访问,则会发出警告。

病征

在针对Java 9进行编译期间,您会看到类似于以下内容的编译错误:

error: package com.sun.java.swing.plaf.nimbus is not visible
import com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel;^(package com.sun.java.swing.plaf.nimbus is declaredin module java.desktop, which does not export it)
1 error

为反射发出的警告如下所示:

Static access to [Nimbus Look and Feel]
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by j9ms.internal.Nimbus(file:...) to constructor NimbusLookAndFeel()
WARNING: Please consider reporting thisto the maintainers of j9ms.internal.Nimbus
WARNING: Use --illegal-access=warn to enable warningsof further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
Reflective access to [Nimbus Look and Feel]

修正

依赖内部API的最明显且可持续的解决方案是摆脱它们。 用维护的API替换它们,您还清了一些高风险的技术债务。

如果由于某种原因无法做到这一点,那么下一个最好的办法就是确认依赖关系,并告知模块系统您需要访问它。 为此,您可以使用两个命令行选项:

  • 选项–add-exports $ module / $ package = $ readingmodule可用于将$ module的 $ package导出到$ readingmodule 。 因此, $ readingmodule中的代码可以访问$ package中的所有公共类型,而其他模块则不能。 将$ readingmodule设置 ALL-UNNAMED时,模块图中的所有模块和类路径中的代码都可以访问该包。 在迁移到Java 9的过程中,您将始终使用该占位符。 该选项可用于java和javac命令。
  • 这涵盖了对公共类型的公共成员的访问,但是反射可以做得更多:通过大量使用setAccessible(true),它可以与非公共类,字段,构造函数和方法(有时称为Deep Reflection )进行交互,甚至在导出的包中仍然被封装。 java选项–add-opens使用与–add-exports相同的语法,并将包打开以进行深层反射,这意味着所有类型及其成员均可访问,而无论其可见性修饰符如何。

您显然需要–add-exports来安抚编译器,但为运行时收集–add-exports和–add-opens也具有一些优点:

  1. 运行时的允许行为将在将来的Java版本中更改,因此无论如何您都必须在某些时候进行
  2. –add-opens使非法反射访问的警告消失
  3. 正如我将在稍后展示的那样,您可以通过使运行时实际执行强封装来确保没有新的依赖项出现

走得更远

根据Java 9进行编译有助于在项目代码库中查找对内部API的依赖性。 但是您的项目使用的库和框架很可能会造成麻烦。

JDeps是要找到在您的项目依赖JDK-内部API编译依赖的完美工具。 如果您不熟悉它,我已经写了入门入门。 这是将其用于手头任务的方法:

jdeps --jdk-internals -R --class-path '$libs/*' $project

在这里,$ libs是一个包含所有依赖项的文件夹,$ project是您项目的JAR。 分析输出超出了本文的范围,但并不难–您将进行管理。

找到反射性访问要困难一些。 运行时的默认行为是对首次非法访问软件包的警告一次,这是不够的。 幸运的是,有–illegal-access = $ value选项,其中$ value可以是:

  • 允许:允许访问所有JDK内部API,以在类路径上进行编码。 对于反射式访问,将为首次访问每个包装发出单个警告。 (Java 9中的默认值。)
  • 警告:行为类似许可证,但每次反射访问都会发出警告。
  • 调试:行为像警告,但每个警告中都包含堆栈跟踪。
  • 拒绝:对于那些相信强封装的人的选择:
    默认情况下,禁止所有非法访问。

特别否认对于寻找反射访问非常有帮助。 一旦收集了所有必需的–add-exports和–add-opens选项,它也是一个很好的默认值。 这样,如果您不注意,就不会出现新的依赖项。

帖子中只有这么多事实-幸运的是,有一本书包含了更多的事实:
Java 9模块系统

  • 模块系统的深入介绍:
    • 基本概念和高级主题
  • 曼宁(Manning)发布:
    • 自2017年赛事开始提供抢先体验
  • 订阅我的时事通讯以保持关注。
    (甚至可以偷看。)

使用代码fccparlog可获得 37%的折扣

对Java EE模块的依赖

Java SE中有许多实际上与Java EE相关的代码。 它最终分为以下六个模块:

  • 带有javax.activation包的java.activation
  • java.corba javax.activity中有,javax.rmi中,javax.rmi.CORBA中,并org.omg。*包
  • 带有javax.transaction包的java.transaction
  • java.xml.bind和所有javax.xml.bind。*软件包
  • 带有javax.jws,javax.jws.soap,javax.xml.soap和所有javax.xml.ws。*包的java.xml.ws
  • 带有javax.annotation包的java.xml.ws.annotation

由于各种兼容性原因(其中一个是拆分包,我们将在下面讨论),默认情况下,类路径上的代码看不到这些模块,这会导致编译或运行时错误。

病征

这是java.xml.bind模块中使用JAXBException的类的编译错误:

error: package javax.xml.bind is not visible
import javax.xml.bind.JAXBException;^(package javax.xml.bind is declared in module java.xml.bind,which is not in the module graph)
1 error

如果您无法通过编译器,却忘记了运行时间,则会收到NoClassDefFoundError:

Exception in thread "main" java.lang.NoClassDefFoundError: javax/xml/bind/JAXBExceptionat monitor.Main.main(Main.java:27)
Caused by: java.lang.ClassNotFoundException: javax.xml.bind.JAXBExceptionat java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:582)at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:185)at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:496)... 1 more

修正

模块化代码后,可以在模块的声明中声明常规依赖项。 在此之前,–add-modules $ module可以为您解决,确保$ module可用,并且可以将其添加到java和javac中。 如果添加java.se.ee ,则可以访问所有Java EE模块。

分包

这有点棘手…为了增强一致性,不允许一个模块从两个不同的模块读取同一包。 但是,实际的实现更为严格,甚至不允许两个模块包含相同的程序包(已导出或未导出)。 模块系统在该假设下运行,并且每当需要加载一个类时,它都会查找哪个模块包含该程序包,然后在其中查找该类(这将提高类的加载性能)。

为了保护这一假设,模块系统检查是否有两个命名模块拆分了一个程序包和barfs(如果找到)。 但是,在迁移期间,您并没有完全处于这种情况。 您的代码来自类路径,该类路径将其放入所谓的未命名模块中。 为了最大程度地提高兼容性,不对其进行检查,并且不对模块进行任何检查。

现在,在拆分包的情况下,这意味着未发现命名模块(例如,在JDK中)和未命名模块之间的拆分。 如果混用了类加载行为,这听起来可能很幸运:相反:如果在模块和类路径之间拆分包,则对于来自该包的类,类加载将始终且仅查看模块。 这意味着包的类路径部分中的类实际上是不可见的。

病征

症状是,即使绝对存在,也无法加载来自类路径的类,从而导致如下编译错误:

error: cannot find symbolsymbol:   class Nonnulllocation: package javax.annotation

或者,在运行时,出现上述NoClassDefFoundErrors。

发生这种情况的一个示例是各种JSR-305实现。 例如,使用注释javax.annotation.Generated(来自java.xml.ws.annotation )和java.annotation.Nonnull(来自com.google.code.findbugs:jsr305 )的项目在编译时会遇到麻烦。 它要么缺少Java EE批注,要么在如上所述添加模块时会遇到拆分包,而看不到JSR 305模块。

修正

迁移路径将有所不同,具体取决于拆分JDK软件包的工件。 在某些情况下,进入随机JDK包的可能不只是某些类,而是整个JDK模块的替代品,例如,因为它覆盖了认可的标准 。 在这种情况下,您正在寻找–upgrade-module-path $ dir选项– $ dir中的模块用于在运行时替换可升级模块。

如果确实只有几个拆分包的类,则长期解决方案是删除拆分。 如果短期内无法实现,则可以使用类路径中的内容修补命名模块。 选项–patch-module $ module = $ artifact会将$ artifact中的所有类合并到$ module中,将split包的所有部分放入同一模块中,从而删除了split。

不过,还有一些注意事项。 首先,打补丁的模块必须实际上将其放入模块图中,为此可能需要使用–add-modules。 然后,它必须有权访问成功运行所需的所有依赖项。 由于命名模块无法从类路径访问代码,因此可能有必要开始创建一些自动模块 ,这超出了本文的范围。

走得更远

通过尝试和错误查找拆分包非常令人不安。 幸运的是, JDeps报告了它们,因此,如果您分析您的项目及其依赖性,输出的第一行将报告已拆分的软件包。 您可以使用与上面相同的命令:

jdeps --jdk-internals -R --class-path '$libs/*' $project

投射到URL类加载器

我刚刚描述的类加载策略是用一种新类型实现的,而在Java 9中,应用程序类加载器就是这种类型。 这意味着它不再是URLClassLoader,因此偶尔的(URLClassLoader)getClass()。getClassLoader()序列将不再执行。 这是另一个典型的示例,其中Java 9在严格意义上是向后兼容的(因为从未指定过它是URLCassLoader),但是仍然可能导致移植挑战。

病征

这是非常明显的。 您将收到ClassCastException抱怨新的AppClassLoader不是URLClassLoader:

Exception in thread "main" java.lang.ClassCastException:java.base/jdk.internal.loader.ClassLoaders$AppClassLoadercannot be cast to java.base/java.net.URLClassLoaderat monitor.Main.logClassPathContent(Main.java:46)at monitor.Main.main(Main.java:28)

修正

类加载器可能已强制转换为特定于URLClassLoader的访问方法。 如果是这样,您只需很小的改动就可以进行迁移。 新的AppClassLoader唯一受支持(因此可访问)的超类型是SecureClassLoader和ClassLoader ,在9中仅添加了一些方法。不过,请看一下,它们可能会满足您的需求。

在运行时图像中拖影

随着JDK的模块化,从根本上改变了运行时映像的布局。 rt.jar,tools.jar和dt.jar等文件不见了; 现在,JDK类已捆绑到jmod文件(每个模块一个)中,jmod文件是一种有目的的未指定文件格式,允许将来进行优化而无需考虑向后兼容性。 而且,JRE和JDK之间的区别消失了。

所有这些都未指定,但这并不意味着根据这些详细信息,这里没有代码。 特别是像IDE这样的工具(尽管它们大多数已经被更新了),这些更改将具有兼容性问题,并且除非更新,否则它们将以无法预测的方式停止工作。

这些更改的结果是,您从系统资源(例如从ClasLoader :: getSystemResource)获取的URL发生了更改。 它过去的格式如下:jar:file:$ javahome / lib / rt.jar!$ path,其中$ path类似于java / lang / String.class。 现在看起来像jrt:/ $ module / $ path。 当然,所有创建或使用此类URL的API均已更新,但是非手工制作这些URL的非JDK代码必须针对Java 9进行更新。

此外,Class :: getResource *和ClassLoader :: getResource *方法不再读取JDK内部资源。 而是使用Module :: getResourceAsStream来访问模块内部资源或创建JRT文件系统,如下所示:

FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
fs.getPath("java.base", "java/lang/String.class"));

引导类路径

我在这里很迷茫,因为我从未使用过-Xbootclasspath选项,该选项已被删除。 显然,它的功能已被各种新的命令行选项所取代(此处是JEP 220的解释):

  • javac选项–system可用于指定系统模块的备用源
  • javac选项–release可用于指定备用平台版本
  • 上面提到的java选项–patch-module选项可用于将内容注入到初始模块图中的模块中

新版本字符串

经过20多年的努力,Java终于并正式接受它不再在1.x版中使用。 万岁! 因此,从Java 9开始,系统属性java.version及其兄弟姐妹不再以1.x开头,而是以x开头,即Java 9中的9。

病征

没有明确的症状-如果某些实用程序功能确定错误的版本,几乎一切都可能出错。 不过,找到它并不难。 对以下字符串的全文搜索应导致所有特定于版本字符串的代码:java.version,java.runtime.version,java.vm.version,java.specification.version,java.vm.specification.version。

修正

如果您愿意将项目的要求提高到Java 9,则可以避开整个系统属性的探测和解析,而可以使用新的Runtime.Version类型 ,这使所有这些操作变得更加容易。 如果您想保持与Java 9之前版本的兼容性,您仍然可以通过创建多发行版JAR来使用新的API。 如果这还是不可能的话,看来您实际上必须编写一些代码(很多!)并根据主要版本进行分支。

摘要

现在,您知道如何使用内部API(–add-export和–add-opens),如何确保存在Java EE模块(–add-modules)以及如何处理拆分包(–patch-module)。 这些是您在迁移过程中最可能遇到的问题。 URLClassLoader转换为较不常见且也较不易于修复而无法访问有问题的代码,这是由于新的运行时映像布局和资源URL,已删除的-Xbootclasspath和新版本字符串导致的问题。

知道如何解决这些问题将为您提供很好的机会来克服所有的迁移难题,并使您的应用程序在Java 9上编译和运行。如果没有,请查看JEP 261的Risks and Assumptions部分 ,其中列出了其他一些潜在的陷阱。

如果您不知所措,请等待我的下一篇文章,其中提供有关如何将这些单独的修补程序纳入全面的迁移策略的一些建议,例如,通过包括构建工具和持续集成。 或索取我的书 ,在那里我将解释所有这些以及更多内容。

翻译自: https://www.javacodegeeks.com/2017/07/java-9-migration-guide-seven-common-challenges.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/349084.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

nuxt sass 全局变量的问题_Sass入门教程

SASS(Syntactically Awesome Stylesheet)是一个CSS预处理器,有助于减少CSS的重复,节省时间。 它是更稳定和强大的CSS扩展语言描述文档的风格结构。sass中文网而且Sass算是CSS的超集,它100%兼容CSS的语法,所有在 CSS 中正常工作的代…

用C语言实现优先级排序和MATLABsort函数的比较

为了实现对两个数组进行优先级排序,用c语言有两种实现方法, 一是需要对两个数组进行排序,然后对排序后的坐标再排序,(求最小值是我自己需要) 二是直接寻找数组排序后的元素坐标,调用qsort函数进行排序,排序后的数组会存放在原数组中,那么就有两种寻找坐标,一是寻找…

造成内存泄漏_如何造成内存泄漏

造成内存泄漏这将是一个相当邪恶的职位-当您确实希望使某人的生活陷入困境时,您将在谷歌上搜索。 在Java开发领域,内存泄漏只是您在这种情况下会引入的错误类型。 为您的受害者保证几天甚至几周的办公室不眠之夜。 我们将在这篇文章中描述两次泄漏。 两…

通过Java,Spring Boot应用程序将Gmail用作SMTP服务器

Gmail用户可以使用Gmail的SMTP服务器smtp.gmail.com从其Spring Boot应用程序发送电子邮件。 为此,让我们在应用程序中进行一些设置: 在application.properties文件中提供SMTP连接属性: spring.mail.hostsmtp.gmail.com spring.mail.username…

在建工地扬尘在线监控系统推荐_配电室为何需要安装蓄电池在线监控系统?保定钰鑫电气...

配电室蓄电池在线监控系统提高了蓄电池运行质量、增强了电力系统的安全运行、保障蓄电池运行环境的可靠,打造无人值守配电室、智能化运维模式,减少蓄电池损耗、浪费,降低了维护成本,为何需要安装一套配电室蓄电池在线监测系统&…

最好的Java开发人员测试和集成工具

通过从应用程序中学习企业APM产品,发现更快,更高效的性能监控。 参加AppDynamics APM导览! 无论您是刚开始还是已经使用了一段时间,使用正确的工具进行编程都可以对项目的成功产生巨大的影响。 适当的工具使您可以编写更好的代码…

最速下降法matlab全局最小值_梯度下降概念

1、梯度概念(1)从几何意义上讲,就是函数变化最快的地方。在单变量的函数中,梯度只是导数,其实就是函数在某个给定点的切线的斜率;在多变量函数中,梯度是一个向量,向量有方向,梯度的方向就指出了…

IntelliJ中的远程调试Wildfly应用程序

远程调试Java应用程序意味着使用本地开发环境连接到远程运行的应用程序。 Java开箱即-agentlib:jdwp[options]支持远程调试:目标应用程序必须使用-agentlib:jdwp[options]选项执行, -agentlib:jdwp[options]选项加​​载Java调试线协议(jdwp&…

+h eclipse中ctrl_Eclipse 常用的快捷键都有哪些?

今天,小编大概整理了 几 组 Eclipse 的快捷键,希望对你有帮助。1、打开资源CTRL SHIFT R:打开所有类型文件,不包括 JAR 包; CTRL SHIFT T:打开 Java 类型文件,包括 JAR 包;2、查…

apache.camel_Apache Camel 2.11发布

apache.camel上周Apache Camel 2.11发布了。 这篇博客文章总结了最引人注目的新功能和改进。 有关详细说明,请参见Camel 2.11发行说明 。 1)新组件 与往常一样,每个新版本都包含许多新组件,这些组件是由我们庞大的用户群贡献的。…

c向文件中插入数据_Redis从文件中批量插入数据

简介在redis中,有时候需要批量执行某些命令,但是在redis的redis-cli下,只能一条条的执行指令,实在太麻烦了!想到这,你是不是蓝瘦香菇? 如果能将要执行的指令一行行存储到文件中,然后…

python多级目录import_你真的会用Python模块与工具包吗?

在开发过程中,我们无法把所有代码、资源都放在同一个文件中。因此,模块导入在编码中是很常见的。无论是C、Java,还是Python、Go。可以把不同功能、不同模块进行分离,当使用的时候,可以通过import关键字在一个模块中使用…

八边形点坐标数的lisp_图形学入门第五课:齐次坐标

齐次坐标(Homegeneous Coordinates)在学习齐次坐标之前,我们要先好奇的问一下,为什么要学习齐次坐标。上一节课,我们学习了变换的三种基本形式:旋转,缩放,和切变。但是还有一种特殊的变换:Trans…

对速度的需求,访问现有数据的速度提高了1000倍

了解如何通过使用标准Java 8流和Speedment的In-JVM-Memory加速器将分析数据库应用程序加速1000倍。 Web和移动应用程序有时会很慢,因为后备数据库很慢和/或与数据库的连接施加了延迟。 现代UI和交互式应用程序需要快速后端,并且理想情况下没有可观察到的…

mysqls压力测试怎么用_用 Swagger 测试接口,怎么在请求头中携带 Token?

松哥周末抽空给 Spring Security 系列也录制了一套视频,目录如下:感兴趣的小伙伴戳这里-->Spring BootVue微人事视频教程今天的话题来自一个小伙伴在微信上的提问:看到这个问题,松哥忽然想到我自己之前写过 Spring BootSwagger…

安卓手机背景变黑色怎么改_别着急扔掉旧手机 你的电脑可能需要它

PC玩家中,不少人都会有在玩游戏时观测电脑硬件状态的习惯。比如查看游戏帧数、CPU频率、GPU频率或是温度等。大多数人都是通过第三方软件,在游戏内把监测数据显示到电脑显示屏角落。可就算是在角落,这些数据依旧会阻挡游戏画面,在…

JDeps入门–分析项目的依赖关系

JDeps是Java依赖关系分析工具 ,这是一个命令行工具,它处理Java字节码(意味着.class文件或包含它们的JAR),并分析类之间静态声明的依赖关系。 可以用各种方式过滤结果,并可以将其汇总到包或JAR级别。 JDeps还…

禅道开源版用户手册_Docker搭建开源版禅道以及项目基本流程介绍

对于自学软件测试的同学来说,经常会遇到这样的困惑:测试用例怎么写?有啥好的模板?缺陷提交的模板是什么样的?bug的生命周期是啥?项目的流程是啥?以上这些困惑,在你仔细看完这篇文章后…

f12 卡 谷歌浏览器_抢券第二课:利用浏览器F12获取优惠券请求链接

抢券第二课为什么迟迟不来呢?因为最近京东没有那种神券需要定点抢购的,我也没法测试我的理论。现在京东的券随时可以领取到,我多没法测试的东西不想就这样欺骗你们。所以今天的第二课我们讲一讲神奇的谷歌浏览器F1201 工具准备一、浏览器这里…

Java命令行界面(第5部分):JewelCli

细算在Java命令行处理与Apache的百科全书CLI , args4j , jbock和命令行中先前的文章,我把注意力转向在这个岗位使用JewelCli完成的命令行参数相似的处理Java中。 几个Java命令行处理库使用批注来定义命令行选项。 到目前为止,本系…