Java Platform Module系统中的可选依赖项

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

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

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

相关文章

sqoop导出solr数据_Apache Atlas - 强大的元数据管理工具

构建和安装Apache Atlas构建Apache Atlas下载 Apache Atlas 1.0.0 发行版源码, apache-atlas-1.0.0-sources.tar.gz, 从 downloads 下载. 然后按照以下说明构建Apache Atlas。tar xvfz apache-atlas-1.0.0-sources.tar.gz cd apache-atlas-sources-1.0.0/ export MAVEN_OPTS&qu…

Mac入门--如何使用brew安装多个PHP版本

一 安装7.1 1. 安装PHP7.1 brew install php7.12. 修改配置 php-fpm.conf,一般在/usr/local/etc/php下(如果php-fpm.conf中不存在&#xff0c;则查找php-fpm.d目录) vim php-fpm.conflisten 127.0.0.1:90003. 启动PHP7.1 brew services start php7.14. 这时php-fpm会监听9000端…

python单例模式继承_python单例模式

单例模式是常见的一种设计模式&#xff0c;它是针对类的一种描述&#xff0c;因此&#xff0c;我们可以使用python的decorator来实现通用的单例模式。一.基本的单例模式首先建立我们的decorator。我们需要为classType建立_instance和_lock成员&#xff1a;Pythondef singleton(…

[MEGA DEAL]完整的Java编程训练营(94%)

成为Java Master的10门课程&#xff08;83.5小时&#xff09;&#xff1a;使用JavaFX的设计UI&#xff0c;利用设计模式&#xff0c;Master Multithreading等 嘿&#xff0c;怪胎&#xff0c; 本周&#xff0c;在我们的JCG Deals商店中 &#xff0c;我们提供了一个极端的报价…

【洛谷 1879】玉米田

题目描述农场主John新买了一块长方形的新牧场&#xff0c;这块牧场被划分成M行N列(1 ≤ M ≤ 12; 1 ≤ N ≤ 12)&#xff0c;每一格都是一块正方形的土地。John打算在牧场上的某几格里种上美味的草&#xff0c;供他的奶牛们享用。遗憾的是&#xff0c;有些土地相当贫瘠&#xf…

echarts的tree怎么控制位置_自动化考研保研面试—线性系统控制器设计

这个问题是我导师&#xff08;面试组长&#xff09;多次在保研考研面试的时候问过的&#xff0c;而且每年都会问&#xff01;问题其实不难&#xff0c;涉及的知识点也就是自控原理经典控制理论的部分&#xff0c;但是基本上很少有人能够完整地回答出来&#xff0c;不服的话请看…

PCF上的Spring Cloud合同和Spring Cloud Services

最近&#xff0c;我们有一个客户&#xff0c;对于使用Spring Cloud Contract &#xff08;SCC&#xff09;来防止微服务团队之间的API“漂移”&#xff0c;微型开发团队需要由单个开发团队来照顾构成企业应用程序一部分的单个API的想法非常感兴趣。 Spring Cloud Contract是Sp…

MS Lync2010客户端开发体会

和前身OCS2007 相比较&#xff0c;Lync2010原生客户端的扩展性大大缩小了&#xff0c;但是提供了很好的客户端SDK&#xff0c;整个系统架构也有较大变化。由于Lync2010原生客户端实在太简单&#xff0c;用惯了QQ的国人&#xff0c;实在无法接受以下功能的缺失&#xff1a; 组织…

date javascript 时区_第23节 Datejs 日期库-Web前端开发之Javascript-零点程序员-王唯

Datejs 是一个开源的JavaScript库&#xff0c;用来解析、格式化和处理日期数据&#xff0c;支持多种语言的日期格式处理&#xff1b;官网&#xff1a;www.datejs.com/Moment.js 是一个简单易用的轻量级JavaScript日期处理类库&#xff0c;提供了日期格式化、日期解析等功能。它…

制杖题

题目描述 求不大于 m 的、 质因数集与给定质数集有交集的自然数之和。 输入格式 第一行二个整数 n&#xff0c;m。 第二行 n 个整数&#xff0c;表示质数集内的元素 p[i]。 输出格式 一个整数&#xff0c;表示答案&#xff0c;对 376544743 取模。 输入输出样例 输入 #1复制 2 …

煤矿安全规程专家解读2016电子版_【学习】煤矿安全规程专家解读(165)

点击蓝字关注我们第二编 井工部分第三百三十三条爆破前&#xff0c;必须加强对机器、液压支架和电缆等的保护或将其移出工作面。爆破前&#xff0c;班组长必须亲自布置专人在警戒线和可能进入爆破地点的所有通路上担任警戒工作。警戒人员必须在安全地点警戒。警戒线处应设置警…

DC / OS中具有Java和数据库应用程序的服务发现

该博客将展示一个简单的Java应用程序如何使用DC / OS中的服务发现与数据库进行对话。 为什么要进行服务发现&#xff1f; 应用程序通常由多个组件组成&#xff0c;例如应用程序服务器&#xff0c;数据库&#xff0c;Web服务器&#xff0c;缓存和消息传递服务器。 通常&#xf…

RAC环境下创建本地数据文件的解决方法

引用收藏&#xff1a;http://blog.itpub.net/501889/viewspace-1083311/ 同事不小心&#xff0c;在RAC环境下创建了本地数据文件&#xff0c;这个肯定会出问题的&#xff0c;节点2不能访问此数据文件。其实发现做错了&#xff0c;立马删掉应该没有问题。数据文件还没有数据。下…

诺基亚n1平板电脑刷机教程_【个人记事本】闲鱼购买平板的经历

今年由于疫情原因&#xff0c;国内开展了全体学生在家学习的模式&#xff0c;这就避免不了老师发一些课件&#xff0c;还有一些录课的视频等电子学习资料等。考虑到开学复习的便利性&#xff08;平板比笔记本电脑更具有便携性&#xff0c;更方便&#xff09;。所以&#xff0c;…

找出一个字符串中出现次数最多的字_487,重构字符串

想了解更多数据结构以及算法题&#xff0c;可以关注微信公众号“数据结构和算法”&#xff0c;每天一题为你精彩解答。问题描述给定一个字符串S&#xff0c;检查是否能重新排布其中的字母&#xff0c;使得两相邻的字符不同。若可行&#xff0c;输出任意可行的结果。若不可行&am…

一、数据设计规范

一、数据设计规范 1、表的前缀 1、表名称不应该取得太长&#xff08;一般不超过三个英文单词。不推荐使用中文拼音&#xff0c;总的长度不要超过30个字符&#xff09; 格式:Tbl_Wms_log 表示 表_Wms系统_log 好处:执行查询方式辨别SQL类别(T_表-Table、V_视图-View、S_存储过…

http缓存管理器_小心缓存管理器

http缓存管理器如果使用spring和JPA&#xff0c;则很有可能利用ehcache&#xff08;或其他缓存提供程序&#xff09;。 您可以在两种不同的情况下进行此操作&#xff1a;JPA 2级缓存和spring方法缓存。 配置应用程序时&#xff0c;通常会设置JPA提供程序的二级缓存提供程序&am…

cad线加粗怎么设置_AutoCAD2019怎么加粗线条 将不同线段加粗方法

AutoCAD2019是一款非常专业的制图软件&#xff0c;那有很多用户表示自己不知道这款软件怎么加粗线条&#xff0c;下面就通过这篇文章给大家介绍一下&#xff0c;一起往下看吧&#xff01;如图所示&#xff0c;我用L命令绘制一根线段&#xff1a;&#xff0c;这根线段的宽度为默…

时间管理——你不可不知的3种时间管理方法

时间管理——你不可不知的3种时间管理方法 时间管理 英文名:Time Management   请问&#xff0c;如果每天都有86400元进入你的银行户头&#xff0c;而你必须当天用光&#xff0c;你会如何运用这笔钱&#xff1f;   天下真有这样的好事吗&#xff1f;   是的&#xff0c;…

python tkinter布局混用_[宜配屋]听图阁

这篇文章主要介绍了python tkinter控件布局项目实例,文中通过示例代码介绍的非常详细&#xff0c;对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下代码部分&#xff1a;from tkinter import *import tkinter.messagebox as messageboxclass Tkdemo():def __i…