28

背景

在 Maven 中,工件可以声明一个依赖项

<optional>true</optional>

这意味着依赖项不是必需的,但如果存在则可以使用。

模块系统的状态似乎指定一个模块只能读取它需要的模块。

问题

  • Java 9 模块系统真的不支持可选依赖吗?
  • 为什么不?
  • Java 9 模块系统提供了哪些可选依赖项的替代方案?

用例

我有一个框架,它集成了应用程序可能使用或不使用的各种库。目前,这个框架是一个单一的 JAR,它反映了类路径以跳过缺少库的集成代码。

我想我们可以为每个配置将它拆分为一个单独的模块,但这会导致 JAR 数量的组合爆炸,因为我们不仅需要为每个可选依赖项提供一个单独的 JAR,而且还需要一个单独的 JAR 用于大多数对可选依赖项...

4

1 回答 1

24

是的,支持可选依赖项。引用原始提案

扩展模块声明的语言以允许在指令static上使用修饰符requires,具有以下含义:

  • 在编译时,requires static M表示强制依赖。如果在可观察模块中找不到合适的模块并解决了,这是一个错误。

  • 在编译时间之后的阶段中,requires static M表示可选的依赖关系。模块系统在解析过程中不会在可观察模块中搜索合适的模块,但如果生成的模块图包含合适的模块,那么它将在进行通常的解析后完整性检查之前添加适当的可读性边。[...]

因此,形式的假设模块声明

module joda.beans {
    requires static joda.collect;
    ...
}

将确保该joda.collect模块在编译时可用,以便可以毫不费力地编译joda.beans引用的模块中的代码。joda.collect但是,它不能保证joda.collect在链接时或运行时可用。

(与此同时,为该功能创建了官方文档。)

我为此写了一个演示。有趣的花絮是module-info.java声明可选依赖项的模块...

module org.codefx.demo.advent {
    // list the required modules
    requires org.codefx.demo.advent.calendar;
    // with 'static' the factories are only required at compile time;
    // to be present at run time either other modules most require them
    // or they must be added with the '--add-modules' command line option
    requires static org.codefx.demo.advent.factory.chocolate;
    requires static org.codefx.demo.advent.factory.quote;
}

...以及同一模块中想要从其可选依赖项中访问类型的代码。它必须这样写,以便在类型ChocolateFactory和/或QuoteFactory不存在时优雅地失败:

private static List<SurpriseFactory> createSurpriseFactories() {
    return Stream.of(
            createChocolateFactoryIfAccessible(),
            createQuoteFactoryIfAccessible())
            .flatMap(Optional::stream)
            .collect(toList());
}

private static Optional<SurpriseFactory> createChocolateFactoryIfAccessible() {
    try {
        return Optional.of(new ChocolateFactory());
    } catch (NoClassDefFoundError er) {
        return Optional.empty();
    }
}

private static Optional<SurpriseFactory> createQuoteFactoryIfAccessible() {
    try {
        return Optional.of(new QuoteFactory());
    } catch (NoClassDefFoundError er) {
        return Optional.empty();
    }
}

最后,命令行可用于定义应用程序使用哪些模块启动:

$java \
    --add-modules org.codefx.demo.advent.factory.chocolate,org.codefx.demo.advent.factory.quote \
    -p mods -m org.codefx.demo.advent

当然,其他模块也可能非可选地需要它们,这会迫使 JVM 将它们包含到模块图中。

于 2016-10-06T16:26:00.477 回答