java platform_Java Platform Module系统中的可选依赖项

java platform

Java平台模块系统(JPMS)对依赖项有很强的见解:默认情况下,需要它们(可以访问),然后在编译时和运行时都将它们存在。 但是,这不适用于可选的依赖项,因为代码是针对运行时不一定存在的工件编写的。 幸运的是,JPMS有一个require静态子句,可以在这些确切情况下使用。

我将向您展示几个示例,其中默认行为的严格性会导致问题,然后将模块系统的解决方案介绍给可选的依赖项:需要静态。 但是,对它们进行编码并非易事,因此我们也将对此进行仔细研究。

总览

一些示例建立在名为Service Monitor 的小型演示应用程序 的optional-dependencies分支上。

不需要的依赖之谜

为了确定常规的require子句的严格性会导致问题的原因,我想从两个示例开始。 尽管在某些方面相似,但是稍后在我们讨论如何针对可能缺少的依赖项进行编码时,差异变得很重要。

实用程序库

让我们从我们正在维护的虚构库uber.lib开始,该库与少数其他库集成。 它的API提供了基于它们的功能,从而公开了它们的类型。 我们将通过com.google.guava的示例进行演示 ,在我们的假设场景中,该示例已经变成了uber.lib想要针对其进行编码的Java模块。

作为uber.lib的维护者,我们假设没有使用Guava的人永远不会调用我们库的Guava部分。 在某些情况下,这很有意义:如果没有这样的图,为什么还要在uber.lib中调用为com.google.common.graph.Graph实例创建漂亮报告的方法?

对于uber.lib ,这意味着它无需com.google.guava即可完美运行:如果Guava将其放入模块图中 ,则客户端可能会调用uber.lib API的该部分。 如果没有,他们也不会,图书馆也会很好。 我们可以说uber.lib从不需要它自己的依赖。

具有常规依赖性,无法实现可选关系。

但是,使用常规的require子句无法实现这种可选关系。 根据可读性和可访问性规则, uber.lib必须要求com.google.guava对其类型进行编译,但这会强制所有客户端在启动其应用程序时始终在模块路径上使用Guava。

如果与图书馆屈指可数uber.lib集成,它将使客户依赖于所有的人,即使他们可能永远不会使用超过一个。
这不是我们的好举动。

花式统计图书馆

第二个示例来自演示应用程序 ,该应用程序包含一个模块monitor.statistics 。 假设有一些高级统计信息库,其中包含monitor.statistics要使用的模块stats.fancy ,但是对于应用程序的每次部署,该信息都不会出现在模块路径中。 (这样做的原因无关紧要,但让我们一起使用一个许可证,该许可证可以防止将花哨的代码“用于邪恶”,但是,由于我们是邪恶的策划者,我们有时只是想这样做。)

我们想在monitor.statistics中编写代码,该代码使用fancy模块中的类型,但是要使其正常工作,我们需要使用require子句来依赖它。 但是,如果执行此操作,则在不存在stats.fancy的情况下,模块系统将不会启动应用程序。

僵局。 再次。

带有“需要静态”的可选依赖项

当一个模块需要针对另一个模块的类型进行编译,但又不想在运行时依赖它时,可以使用require静态子句。 如果foo需要静态bar,则模块系统在编译和运行时的行为会有所不同:

  • 在编译时,必须存在bar ,否则会出现错误。 在编译过程中的酒吧FOO可读。
  • 在运行时,可能不存在bar ,这将不会导致错误或警告。 如果存在,则foo可以读取。

我们可以立即将其付诸实践,并创建一个可选的依赖项,从monitor.statisticsstats.fancy

module monitor.statistics {requires monitor.observer;requires static stats.fancy;exports monitor.statistics;
}

如果在编译过程中缺少stats.fancy,则在编译模块声明时会出现错误:

monitor.statistics/src/main/java/module-info.java:3:error: module not found: stats.fancyrequires static stats.fancy;^
1 error

但是,在启动时 ,模块系统不在乎stats.fancy是否存在。

同样, uber.lib的模块描述符将所有依赖声明为可选:

module uber.lib {requires static com.google.guava;requires static org.apache.commons.lang;requires static org.apache.commons.io;requires static io.javaslang;requires static com.aol.cyclops;
}

现在我们知道了如何声明可选的依赖项,还有两个问题需要回答:

  • 在什么情况下会出现?
  • 我们如何针对可选依赖项进行编码?

接下来,我们将回答两个问题。

喜欢我的帖子? 然后拿我的书! Java 9模块系统

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

直到4月6日:使用代码mlparlog可获得 50%的折扣!

解决可选依赖项

模块解析是这样的过程:给定初始模块和可观察模块的范围,该模块通过解析require子句构建模块图。 解析模块时,必须在可观察模块的范围中找到它需要的所有模块。 如果是,则将它们添加到模块图;否则,将它们添加到模块图。 否则会发生错误。 重要的是要注意,在解析期间未放入模块图中的模块在以后的编译或执行期间也不可用。

在编译时,模块解析会像常规依赖项一样处理可选的依赖项。 但是,在运行时,要求静态子句通常被忽略。 当模块系统遇到一个模块系统时,它不会尝试实现它,这意味着它甚至不检查命名模块是否存在于可观察模块的范围中。

仅是可选依赖项的模块在运行时将不可用。

结果,即使模块存在于模块路径上(或与此相关的JDK中),也不会仅仅由于可选的依赖关系而将其添加到模块图中。 仅当它也是正在解析的某个其他模块的常规依赖项,或者因为它是使用命令行标志–add-modules显式添加的,它才会进入图表。

也许您偶然发现了“ 大部分都忽略了可选依赖项”这一短语。 为什么大多数? 嗯,模块系统要做的一件事是,如果一个可选的依赖关系使其成为一个图形,则会添加一个可读性边缘。 这样可以确保如果存在可选模块,则可以立即访问其类型。

针对可选依赖项进行编码

可选的依赖项在针对它们编写代码时需要多加考虑,因为这是在monitor.statistics使用stats.fancy中的类型但运行时不存在该模块时发生的:

Exception in thread "main" java.lang.NoClassDefFoundError:stats/fancy/FancyStatsat monitor.statistics/monitor.statistics.Statistician.<init>(Statistician.java:15)at monitor/monitor.Main.createMonitor(Main.java:42)at monitor/monitor.Main.main(Main.java:22)
Caused by: java.lang.ClassNotFoundException: stats.fancy.FancyStats... many more

哎呀。 我们通常不希望我们的代码这样做。

一般而言,当当前正在执行的代码引用类型时,Java虚拟机会检查它是否已加载。 如果不是,它将告诉类加载器执行此操作,如果失败,则结果为NoClassDefFoundError,该错误通常使应用程序崩溃或至少从正在执行的逻辑块中失败。

对于可选的依赖项,我们选择退出使模块系统安全的检查。

这是JAR hell著名的东西,模块系统希望通过在启动应用程序时检查声明的依赖项来克服 。 但是,由于需要static,因此我们选择退出该检查,这意味着我们最终可能会遇到NoClassDefFoundError。 我们该怎么做呢?

建立的依存关系

但是,在研究解决方案之前,我们需要查看我们是否确实有问题。 对于uber.lib,我们希望仅在调用库的代码已使用它们的情况下才使用来自可选依赖项的类型,这意味着类加载已成功。

换句话说,调用uber.lib时,必须存在所有必需的依赖项,否则将无法进行调用。 因此,我们毕竟没有问题,也不需要做任何事情。

内部依赖

不过,一般情况有所不同。 带有可选依赖项的模块很可能会首先尝试从中加载类,因此NoClassDefFoundError的风险非常高。

一种解决方案是确保在访问依赖项之前,必须对具有可选依赖项的模块进行所有可能的调用。 该检查点必须评估该依赖项是否存在,如果不存在,则将到达它的所有代码发送到不同的执行路径。

模块系统提供了一种检查模块是否存在的方法。 我在时事通讯中解释了如何到达那里以及为什么使用新的stack-walking API ,所以当我说这是可行的方式时,在这里您只需要信任我:

public class ModuleUtils {public static boolean isModulePresent(String moduleName) {return StackWalker.getInstance(RETAIN_CLASS_REFERENCE).walk(frames -> frames.map(StackFrame::getDeclaringClass).filter(declaringClass ->declaringClass != ModuleUtils.class).findFirst().orElse((Class) ModuleUtils.class));.getModule();.getLayer().findModule(moduleName).isPresent();// chain all the methods!}}

(在实际的应用程序中,缓存值可能并不总是重复相同的检查。)

用“ stats.fancy”之类的参数调用此方法将返回该模块是否存在。 如果使用常规依赖项的名称(简单的require子句)进行调用,则结果将始终为true,因为否则模块系统将无法启动应用程序。 如果使用可选依赖项的名称(需要static子句)进行调用,则结果将为true或false。

如果存在可选依赖项,则模块系统将建立可读性,因此沿着使用模块中类型的执行路径进行操作是安全的。 如果不存在,选择这样的路径将导致NoClassDefFoundError,因此必须找到其他路径。

摘要

有时您想针对运行时并不总是存在的依赖关系编写代码。 为了使依赖项的类型在编译时可用,但在启动时不强制其存在,模块系统提供了require静态子句。 但是请注意,如果仅以这种方式引用模块,则在解析过程中不会拾取该模块,并且需要特别注意确保在运行时不存在可选依赖项时代码不会崩溃。

要了解有关模块系统的更多信息,请查看JPMS标签或获取我的书《 Java 9模块系统 (带Manning)》。 如果您对历史观点感兴趣,请查看Project Jigsaw标签 。

翻译自: https://www.javacodegeeks.com/2017/04/optional-dependencies-java-platform-module-system.html

java platform

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

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

相关文章

hibernate删除记录_Hibernate记录:常见问题的提示和解决方案

hibernate删除记录如何通过适当的日志记录解决一些最常见的Hibernate问题&#xff1f; Hibernate的日志记录配置是一个重要但很少讨论的主题。 正确的配置可以帮助您在开发过程中发现潜在的问题&#xff0c;而错误的配置则可以在生产中导致严重的性能问题。 这就是我在新书《 …

java velocity详解_[velocity] velocity详解

(1)为什么要使用模版语言&#xff1f;在服务器端可以使用 Velocity处理模板和生成的动态内容(HTML、XML等)。这和JSP技术的目标非常接近。但是&#xff0c;JSP模型可以毫无阻碍地访问底层的Servlet API和Java编程语言。它基本上是一种在很大程度上开放的访问模型。而作为一种完…

pc端游戏修改器_原神:不要吐槽手机内存了,想要获得最佳游戏体验,PC端最合适...

想必大家这段时间都被一个米哈游公司的游戏给刷屏了吧&#xff0c;它就是号称用时三年&#xff0c;斥资一亿美元打造的《原神》&#xff0c;正是这种情况下&#xff0c;原神的关注远远要高于其他游戏&#xff0c;并且玩家的期望值也是和关注度成正比。但是&#xff0c;一经发布…

java调用命令行命令_Java命令行界面(第4部分):命令行

java调用命令行命令我的Java命令行解析系列的第四部分介绍了Commandline &#xff0c;它被描述为 “用于解析命令行参数的Java库”&#xff0c;它“基于使用注释从命令行参数到对象的映射”。 像以前介绍的args4j和jbock一样&#xff0c; Commandline使用注释来提供潜在命令行…

JVM 内存示意图(内存结构图/内存解析图)

图 1&#xff1a; 图 2&#xff1a; 图 3&#xff1a; 图 4&#xff1a;

java登录界面命令_Java命令行界面(第8部分):Argparse4j

java登录界面命令Argparse4j是“ Java命令行参数解析器库”&#xff0c;其主页将其描述为“基于Python的argparse模块的Java命令行参数解析器库”。 在本文中&#xff0c;我将简要介绍如何使用Argparse4j 0.7.0处理命令行参数&#xff0c;该参数类似于本系列中的前七篇文章中有…

协议管理模块设计

文章目录协议和合同的区别协议表说明协议签署日志协议和合同的区别 如果协议的内容写得比较明确、具体、详细、齐全&#xff0c;并涉及到违约责任&#xff0c;即使其名称写的是协议&#xff0c;也是合同&#xff1b;如果协议的内容写得比较概括、原则、很不具体&#xff0c;也…

java登录界面命令_Java命令行界面(第7部分):JCommander

java登录界面命令这是我系列的第七篇文章&#xff0c;简要介绍了用于处理Java命令行参数的各种库。 这篇文章回到了基于注释的库的覆盖范围&#xff0c;该库似乎是在Java中可用于处理命令行参数的众多可用库中知名度最高&#xff0c;最受欢迎的库之一&#xff1a; JCommander 。…

java体系技术框架_java框架之Spring 核心框架体系结构

很多人都在用spring开发java项目&#xff0c;但是配置maven依赖的时候并不能明确要配置哪些spring的jar&#xff0c;经常是胡乱添加一堆&#xff0c;编译或运行报错就继续配置jar依赖&#xff0c;导致spring依赖混乱&#xff0c;甚至下一次创建相同类型的工程时也不知道要配置哪…

IntelliJ IDEA中打开项目时用 Import Project(导入项目) 和 Open(打开项目) 的区别

文章目录创建测试目录使用 Open 方式打开用 Import Project 方式打开用 Open 方式打开文件夹下的多个项目用 Import Project 的方式打开多个项目总结对于 IDEA 中如下图所示的 Import Project 和 Open Project 的区别&#xff0c;一开始不明白怎么回事&#xff0c;网上看了很多…

java命令模式返回值_JAVA 设计模式 命令模式

目录用途命令模式 (Command)将一个请求封装为一个对象&#xff0c;从而使你可以用不同的请求对客户进行参数化&#xff1b;对请求排队或请求日志&#xff0c;以及支持可撤销的操作。命令模式是一种行为型模式。结构图-命令模式结构图Command: 用来声明执行操作的接口。abstract…

IntelliJ IDEA for Mac 如何创建普通的Java项目_创建项目_新建项目

文章目录创建 Java 新项目的流程普通 Java 项目结构Java 项目结构特例讲解创建 Java 新项目的流程 Create New Project Empty Project &#xff08;1&#xff09;选择【Empty Project】&#xff0c;则项目不会自动创建模块&#xff0c;项目创建成功之后&#xff0c;会自动弹出…

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

java 集成开发工具通过从您的应用程序学习企业APM产品&#xff0c;发现更快&#xff0c;更有效的性能监控。 参加AppDynamics APM导览&#xff01; 无论您是刚刚起步还是已经从事了一段时间&#xff0c;使用正确的工具进行编程都可以对项目的成功产生巨大的影响。 适当的工具使…

aws高额账单_取消堆放以提高延迟并减少AWS账单

aws高额账单大多数性能问题可以通过几种不同的方式解决。 多数人都容易理解和应用许多适用的解决方案。 一些解决方案&#xff08;例如从JVM管理的堆中删除某些数据结构&#xff09;更加复杂。 因此&#xff0c;如果您不熟悉此概念&#xff0c;我建议您继续学习我们最近如何减少…

Iphone 手机如何导入/导出通讯录

打开苹果云官网&#xff0c;选择「通讯录」&#xff0c;点击左下角的「设置」&#xff0c;可以看到导入/导出菜单 点击进入 icloud 官网

intellij远程调试_IntelliJ中的远程调试Wildfly应用程序

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

微信小程序注册/登录接口开发

文章目录后端有关说明前端有关说明接口设计小程序注册/登录接口APP 注册/登录接口PC Web 端的注册/登录接口小程序注册/登录序列图校验 token后端有关说明 登录和注册的逻辑要独立抽取写成2个接口&#xff1a;注册接口、登录接口 小程序、APP、PC端的登录接口和注册接口要分开…

couchbase_使用Portworx和Couchbase的有状态容器

couchbase容器本应是短暂的&#xff0c;因此可以很好地扩展以用于无状态应用程序。 有状态的容器&#xff08;例如Couchbase&#xff09;需要区别对待。 管理Docker容器的持久性概述了如何管理有状态容器的持久性。 该博客将说明如何使用Docker Volume Plugins和Portworx创建有…

在 MyEclipse 中配置 tomcat

在 IDE 配置 tomcat 实则就是让 IDE 来管理 tomcat 配置 步骤 1 点击工具栏上的“Run/Stop/Restart MyEclipse Servers”图标旁边的下拉箭头 步骤 2 选择“Configure Server” 步骤 3 在弹出的对话框“Preferences”中展开“MyEclipse” – “Servers” – “Tomcat”–“To…

java 中怎么加汉字,jquery怎么向body里面增加文字

jquery如何向里面增加文字jsp代码是这样的&#xff1a;我想在页面加载时动态的将一行文字增加到里面&#xff0c;不知道为什么总是加不进去&#xff0c;js代码如下&#xff1a;$(document).ready(function(){if($("#type").val()"MESSAGE") {$(".ke-t…