上海做网站培训班,潮州 网站建设,黑帽seo软件,济南网络营销外包服务java platformJava平台模块系统#xff08;JPMS#xff09;对依赖项有很强的见解#xff1a;默认情况下#xff0c;需要它们#xff08;可以访问#xff09;#xff0c;然后在编译时和运行时都将它们存在。 但是#xff0c;这不适用于可选的依赖项#xff0c;因为代码… 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.statistics到stats.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.htmljava platform