【问题标题】:Package conflicts with automatic modules in Java 9包与 Java 9 中的自动模块冲突
【发布时间】:2017-07-10 12:43:56
【问题描述】:

随着 Java 9 的临近,我认为将我的一些项目移植到 Java 9 将是一个很好的学习练习。在我的一个项目中,我依赖于 rxjavarxjavafx

dependencies {
    compile 'io.reactivex:rxjava:1.2.6'
    compile 'io.reactivex:rxjavafx:1.0.0'
    ...
}

我想将此项目创建为命名模块。为此,我需要创建一个module-info.java 文件,并且需要在此处指定rxjavarxjavafx 的要求。但是,这些库还没有任何模块信息。

为了解决这个问题,我阅读了I need to create Automatic Modules。据我了解,我需要重命名 rxjavarxjavafx 罐子以具有简单的名称,然后在 --module-path 参数中列出罐子。然后我在我的module-info.java 中添加一个requires 指令和jar 名称。

module com.foo.bar {
    requires rxjavafx;
    requires rxjava;
}

我编写了一个 gradle 任务来为我编辑 jar 名称,它似乎在大多数情况下都有效。它需要所有需要编译的 jars 并将它们重命名为不包含版本信息或斜杠。然后将文件连接成: 分隔字符串:

tasks.withType(JavaCompile) {
    delete { delete '/tmp/gradle' }
    copy {
        from configurations.compile + configurations.testCompile
        into '/tmp/gradle'
        rename '(.*)-[0-9]+\\..*.jar', '$1.jar'
        rename { String fileName -> fileName.replace("-", "") }
    }
    options.compilerArgs += ['--module-path', fileTree(dir: '/tmp/gradle', include: '*.jar').getFiles().join(':')]
}

rx 库自然会共享它们的一些包名...但这会导致编译器返回错误,例如:

error: module  reads package rx.subscriptions from both rxjava and rxjavafx
error: module  reads package rx.schedulers from both rxjava and rxjavafx
error: module  reads package rx.observables from both rxjava and rxjavafx
error: module rxjava reads package rx.subscriptions from both rxjavafx and rxjava
error: module rxjava reads package rx.schedulers from both rxjavafx and rxjava
error: module rxjava reads package rx.observables from both rxjavafx and rxjava
error: module rxjavafx reads package rx.subscriptions from both rxjava and rxjavafx
error: module rxjavafx reads package rx.schedulers from both rxjava and rxjavafx
error: module rxjavafx reads package rx.observables from both rxjava and rxjavafx

似乎解决此问题的唯一方法是将rxjavarxjavafx 的内容重新打包到一个jar 中并将其添加为单个模块。不过,这似乎不是一个好的解决方案...

所以我的问题是:

  • 我是否正确使用了新的模块系统?
  • 我该如何处理这个错误?和
  • 这些依赖项会阻止我更新,还是应该等待 rx 更新它们的库?

注意:我已经尝试使用标准 java/javac 运行它,它们会导致相同的问题。这也是我的java版本:

java version "9-ea"
Java(TM) SE Runtime Environment (build 9-ea+140)
Java HotSpot(TM) 64-Bit Server VM (build 9-ea+140, mixed mode)

【问题讨论】:

  • 小改进(可能):当从 JAR 名称中提取模块名称时,模块系统会抛出看起来像版本的子字符串 ~> 尝试不重命名它们。模块路径也接受目录 ~> 如果你有一个只包含依赖项的目录,直接把它放在模块路径上。
  • 与您当前的问题无关,我建议升级到更新版本的 JDK 9。Build 148(我认为)通过反射对私有类型和成员的可访问性进行了一些重大更改。当前版本为 157。

标签: java compiler-errors java-9 java-module java-platform-module-system


【解决方案1】:

我是否正确使用了新的模块系统?

是的。您看到的是预期行为,这是因为 JPMS 模块不允许拆分包。

如果您不熟悉“拆分包”一词,它本质上是指同一包的两个成员来自两个不同的模块。

例如:
com.foo.A(来自 moduleA.jar)
com.foo.B(来自 moduleB.jar)

我该怎么办?

你有两个选择:

  1. (更难)“取消拆分”包依赖项。但是,如果您不熟悉库的内部运作,这可能会很困难或不可能
  2. (更容易)将两个 jar 组合成一个 jar(因此是一个自动模块),如上所述。我同意这不是一个“好”的解决方案,但首先拆分包通常也不是一个好主意。

这些依赖项会阻止我更新,还是应该等待 rx 更新它们的库?

希望 rx 最终会在将来的某个时候更新他们的库以不拆分包。在那之前,我的建议是将两个罐子一起粉碎成一个罐子(选项#2)。

【讨论】:

  • 是的,这很痛苦,但是 afaik 不允许拆分包有安全性+性能方面的好处。希望 lib 所有者能够快速提供组合 jar。或者我可以看到 maven central 提供自动组合的 jars。
  • 阅读 java 9 迁移说明很有趣,jdk9 不太可能破坏您的代码。但是这个限制打破了大量的图书馆。对于通过多个 jar 分发的库,拆分包是一种非常常见的做法。
  • @eugcomax 重要的是要注意 JDK 9 迁移有 2 个不同的阶段——1)“容忍”java 9(即将应用程序作为未命名的模块运行),然后 2)“采用”java 9(即转换应用程序以使用模块信息,因此它们被命名为模块)。我相信第 1 阶段不太可能破坏人们的代码,但第 2 阶段无疑会让人头疼。
  • 您可能使用的是非常旧的 JDK 版本。 jsr305 注释已经在他们自己的模块中一段时间​​了,它不包含在 java.se 聚合模块中。所以这个模块默认不加到JDK中。
  • 您可以将两个 jar 解压缩到同一个目录中,然后将它们重新压缩到一个存档中(并将文件扩展名从 .zip 重命名为 .jar)
【解决方案2】:

我有类似的问题:

error: module flyway.core reads package javax.transaction.xa from both jboss.transaction.api.1.2.spec and java.sql
error: module slf4j.api reads package javax.transaction.xa from both jboss.transaction.api.1.2.spec and java.sql
error: module hibernate.core reads package javax.transaction.xa from both jboss.transaction.api.1.2.spec and java.sql
.../src/main/java/module-info.java:1: error: module eu.com.x reads package javax.transaction.xa from both java.sql and jboss.transaction.api.1.2.spec

我可以通过检查我的项目传递依赖项(“gradle dependencies”或“mvn dependency:tree”可能会有所帮助)并通过类似于以下代码的排除来摆脱拆分包编译问题:

configurations.all {
    exclude group: 'org.jboss.spec.javax.transaction', module: 'jboss-transaction-api_1.2_spec'
}

<dependencies>
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-core</artifactId>
      <version>5.2.10.Final</version>
      <exclusions>
        <exclusion>
          <groupId>org.jboss.spec.javax.transaction</groupId>
          <artifactId>jboss-transaction-api_1.2_spec</artifactId>
        </exclusion>
      </exclusions> 
    </dependency>
  </dependencies>

我的问题不需要重新包装 jar。 #JDK8 上没有出现此问题。可能排除依赖项对每个项目都没有帮助。

【讨论】:

  • 我遇到了同样的问题,但在这种情况下,排除 boss-transaction-api_1.2_spec 依赖项无法正常工作。它在java.transaction.xa 包中包含其他类,这些类不在java.sql 模块导出的同一包中。因此,我得到了 java.lang.ClassNotFoundException 运行时异常。在解决此问题之前,我可能必须“取消模块化”我的项目。
【解决方案3】:

我在从 javaee 和 java.transaction.xa 读取包时遇到了同样的问题。 我通过将此行添加到我的 modul-info.java 来修复它

 opens javax.transaction.xa;

它运行良好,但出现提示说包 javax.transaction.xa 为空或不存在。但是源代码编译正确。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-10-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-13
    相关资源
    最近更新 更多