Eclipse在将构建路径切换到JDK 10后无法找到与XML相关的类

81
我正在Eclipse上开发一个Maven项目(分支platform-bom_brussels-sr7)。最近,当我尝试将该项目的Java Build Path 切换到JDK 10时,Eclipse构建无法找到诸如javax.xml.xpath.XPathorg.w3c.dom.Documentorg.xml.sax.SAXException这样的类。似乎只有与XML相关的类受到影响,主要来自Maven依赖项xml-apis-1.4.01

在Eclipse中尝试使用Maven构建没有错误。Ctrl-LeftClick其中一个被认为是缺失的类可以找到该类并在Eclipse编辑器中打开它。似乎只有Eclipse构建受到影响。

我尝试了几种方法,但都没有帮助。我尝试过:

  • Project Clean
  • 不同版本的Eclipse:Oxygen和Photon。
  • 使用JDK 8和JDK 10运行Eclipse本身。
  • 更改项目的编译器兼容性级别。它在JDK 8构建路径下的兼容性级别8和10下构建,并在JDK 10构建路径下的两者下均失败。

1
我对Eclipse一无所知,但是你列出的类在java.xml模块中。如果您以前在类路径上部署了xml-apis-1.4.01,那么我假设这些类实际上从该JAR文件中从未加载过,它们是从JDK加载(并且继续加载)。您的项目在Eclipse之外是否可以构建/运行? - Alan Bateman
@AlanBateman,你知道为什么javac会允许接受一个不唯一可见的包吗?(请参见我的答案)。我找不到任何定义命名与未命名模块冲突被豁免规则的内容。我有遗漏吗? - Stephan Herrmann
1
Stephan - 你能把你的问题带到jigsaw-dev吗? - Alan Bateman
@AlanBateman,已完成,请查看http://mail.openjdk.java.net/pipermail/jigsaw-dev/2018-December/014076.html(早些时候我没有看到您的回复,因为您没有“提及”我)。 - Stephan Herrmann
我在2021年的Eclipse 2021-03、OpenJDK 11和Oracle JDK11中遇到了同样的问题。我看到下面的答案,它很棒,但我不知道如何解决,特别是指@StephanHerrmann提供的第三个解决方案。这个答案很好,但我希望有更多的细节,因为我无法按照我的Eclipse版本进行操作。 - pixel
显示剩余3条评论
10个回答

117
我假设从Java 1.8迁移的项目仍然没有module-info.java。这意味着您正在编译“未命名模块”中的代码。
未命名模块中的代码“读取”所有可观察的命名和未命名模块,特别是它从JRE系统库中读取模块“java.xml”。此模块导出类似于java.xml.xpath的包。
此外,您在类路径上有xml-apis.java,它提供了另一组相同名称的软件包(如java.xml.xpath和其他)。这些被认为与未命名模块关联,就像您自己的代码一样。
这种情况违反了JLS§7.4.3(最后一段)中定义的“唯一可见性”的要求。特别是每个限定类型名称Q.Id(JSL§6.5.5.2)都要求其前缀Q是唯一可见的包(为简单起见,我忽略了嵌套类型的情况)。因此:该程序是非法的,必须被编译器拒绝。
这让我们面临一个问题和两个解决方案:
(1) 问题:为什么javac接受该程序?
(2) 解决方案:如果在您的项目中添加module-info.java,则可以通过requires来控制您的项目读取哪个模块,即 requires java.xml;requires xml.apis;(其中“xml.apis”是“xml-apis-1.4.01.jar”的自动模块名称)。
(3) 解决方案:除了将您的项目转换为模块外,您仍然可以通过从可观察模块集中排除java.xml来避免冲突。在命令行上,可以使用--limit-modules来完成此操作。Eclipse中的等效方法是"Modularity Details"对话框,请参见JDT 4.8 New&Noteworthy(查找Contents选项卡)。由于java.xml通过许多其他默认可观察模块隐式要求,因此将右侧(“显式包含的模块”)除java.base之外的所有内容推到左侧(“可用模块”)可能是一个好主意,并有选择地重新添加那些您的项目需要的模块。
附注:Eclipse仍然没有提供理想的错误消息,而不是“无法解析”,它实际上应该说:“javax.xml.xpath包可以从多个模块访问:javax.xml,<unnamed>。

PPS:还有一件奇怪的事情:为什么改变JRE和类路径上的jar的顺序(这种顺序不是javac或JEP 261支持的概念)会改变编译器的行为。

编辑:

  • Alex Buckley 确认这种情况是非法的,尽管javac表示相反。已提出针对javac的错误JDK-8215739。在Java 12发布之前数月就已经承认了这个错误。截至2019-06,已经决定Java 13也将在没有修复的情况下发布。Java 14同样如此。该错误曾暂定为Java 15的计划,但在2020-04-20被取消。
  • Eclipse错误信息已经改进,以说明实际问题。
  • 在Eclipse 2019-06中,用于解决方案(3)的UI已经进行了翻新。最新文档可以在在线帮助中找到。
  • 截至2022-12,如我的其他答案所述,这个问题有另一个角度。这并不否定这里所说的内容,但让事情看起来不同。

1
谢谢,我想我需要排除一些传递依赖项(或更新更多库)。 - marcus
3
好的回答。您是否考虑写一篇后续文章,使用新的用户界面解释遵循的具体程序,针对那些不理解模块化编译精确规则的人?(我想到了我的学生学习Java,我无法一下子教他们所有复杂的Java知识,但仍希望尽快让他们接触有趣的项目,在这些项目中他们可能会遇到这个问题。) - Olivier Cailloux
7
Eclipse bug报告线程中的第51条评论 中,有这些短指令对我有效:"为了排除xml-apis.jar,打开eclipse中的你的POM,进入Dependency Hierarchy选项卡,按xml-api过滤,在任何出现的xml-apis实例上右键单击,并选择Exclude Maven Artifact。" - cb4
3
第三个解决方案:从项目依赖项中排除 xml.apis。相较于排除可观察模块集合中的 java.xml,存在以下缺点:1)引入 xml.apis 作为传递依赖项的依赖关系(在我的情况下是 xerces)可能与特定版本很好地配合使用,并且在 JRE 提供的版本中表现不良(可能更老);2)xml.apis 可能提供 JRE 提供版本中未包含的类。但是,排除 java.xml 模块(其他组件可能依赖此模块)也存在类似的风险。@StephanHerrmann: 有何想法? - Olivier Cailloux
2
另外,xml.apis:xml.apis:1.4.01似乎是java.xml的一个子集(在OpenJDK 11中提供):我发现前者中没有包含在后者中缺失的类,除了org.apache.xmlcommons.Version之外,但相反,许多来自java.xml的类不在xml.apis中。因此,我建议排除xml.apis并保留JRE模块。 - Olivier Cailloux
显示剩余14条评论

13

虽然被接受的答案(由我自己提供)仍然是正确的,但最近有一个更加扭曲的故事引起了我的注意

最初的意图可能是为了支持手头的情况。

请参阅原始设计文档“模块系统的状态”(SotMS)中的这个引用:

如果一个包在命名模块和未命名模块中都被定义,则忽略未命名模块中的包。

该文档的日期为2016/3/8 08:18,并且在那个时候已经标记为“此文档略有过时”。此外,它对于任何实现都没有法律约束力。尽管如此,该文档仍然具有一定的相关性,因为上面引用的内容正是javac似乎实现的内容(并且在JDK-8215739提交多年后仍然实现)。

换句话说,这个冲突并不是一号和二号实现之间的冲突,而似乎是甚至在Oracle内部存在的冲突。支持这种情况的有两票(SotMS 和 javac),而反对的只有一票(JLS)。

由于Eclipse committers不倾向于在Oracle内解决这个冲突,最近的 2022-12版Eclipse具有了一个新的编译器选项:通过向项目的 .settings/org.eclipse.jdt.core.prefs 添加以下行,用户可以选择忽略JLS在这方面的规定:

org.eclipse.jdt.core.compiler.ignoreUnnamedModuleForSplitPackage=enabled

这个选项将决策交给用户:他们想要JLS语义还是SotMS/javac语义(在这个特定问题中)?但我们还没有准备好为此提供UI选项,以避免用户毫无思考地做出选择,没有提供此处提供的背景信息。

就我个人而言,我对这种情况并不是特别满意,因为它加剧了Java不是一个,而是几种语言的事实。


1
在我的情况下,使用小写的“enabled”可以正常工作。 - Mike
谢谢,@Mike,我取了内部常量的拼写而不是它的值 :) - 已更正。 - Stephan Herrmann
@StephanHerrmann,这个选项除了在Eclipse项目首选项文件中设置之外,还有其他的设置方式吗?考虑批处理编译器(ECJ、AspectJ编译器)。我需要为IntelliJ IDEA和AJC进行配置,而不是为Eclipse IDE和ECJ(JDT Core)进行配置。 - kriegaex
@StephanHerrmann:我已经找到了那段代码。但是似乎我的问题更基础:我如何直接将选项传递给编译器?CLI的语法是什么?类似于通过-DmyProperty=abc将系统属性传递给JVM吗?到目前为止,我只能通过配置文件进行传递。它似乎不能做我想要的事情,但至少以这种方式识别了该选项。 - kriegaex
不,我并没有从Java中调用它,而是从IDE(IntelliJ)或Maven中调用。有趣的是,Maven构建成功了,而IntelliJ构建失败了,因此我开始搜索我看到的编译错误,并找到了这个设置。此时,我承认我很愚蠢和丑陋,因为一开始设置未能工作的原因就在电脑前面。AJC将前缀JavaCore.PLUGIN_ID从“org.eclipse.jdt.core”重定位到“org.aspectj.org.eclipse.jdt.core”,这一点我忘记了,因为我在上游源代码中查找了设置,而不是在AspectJ分支中。 - kriegaex
显示剩余2条评论

13

在我的情况下,问题是xercesImpl : 2.10.0是一个(短暂的)依赖项。该jar捆绑了org.w3c.dom.html.HTMLDOMImplementation

据我所知,org.w3c.dom包从两个模块中可用,导致构建失败。如果其中一个依赖项(直接或短暂)具有25个由java.xml模块导出的包中的类,则构建将失败。

在Maven中排除xercesImpl(以及下面列出的罪犯)解决了我的问题:

    <dependency>
        <groupId>xyz</groupId>
        <artifactId>xyz</artifactId>
        <version>1.0</version>
        <exclusions>
            <exclusion>
                <groupId>xerces</groupId>
                <artifactId>xercesImpl</artifactId>
            </exclusion>
            <exclusion>
                <groupId>xml-apis</groupId>
                <artifactId>xml-apis</artifactId>
            </exclusion>
            <exclusion>
                ...
            </exclusion>
        </exclusions>
    </dependency>

感谢Rune Flobakk在此提供提示:https://bugs.eclipse.org/bugs/show_bug.cgi?id=536928#c73

其他的问题:

  • batik-ext : 1.9(捆绑了org.w3c.dom.Window)
  • xom : 1.2.5(捆绑了org.w3c.dom.UserDataHandler)
  • stax-api : 1.0.2(捆绑了javax.xml.stream.EventFilter)
  • xml-apis : 1.4.01(捆绑了org.w3c.dom.Document)
  • xml-beans : 2.3.0(捆绑了org.w3c.dom.TypeInfo)

5
这似乎已被报告为 Eclipse Bug 536928。如果每个人都去投票,可能会促使提高优先级。

很遗憾,使用新的Eclipse版本2019-06后,无法再更改顺序以解决该问题。 - benez
fixed - Mike

5

虽然Stephan Herrmann的回答是正确的,但如果我的解决方法能帮助到其他人,我将发布我的错误和如何解决它的方法。我遇到了错误The package javax.xml.namespace is accessible from more than one module: <unnamed>, java.xml,检查出错的类后发现是导入了javax.xml.namespace.QName。通过“打开类型”对话框,我发现这个问题是由eureka客户端引用stax-api所导致的。这样就解决了我的问题:

<exclusion>
   <groupId>stax</groupId>
   <artifactId>stax-api</artifactId>
</exclusion>

“开放类型”是解决我的问题的关键。谢谢! - lilalinux
在我的情况下,是通过axon-messaging传入的xpp3。排除xpp3可以解决问题。 - lilalinux
1
谢谢这个提示。我一直找不到关于org.w3c.dom.Document引用冲突来自哪里的问题,但在Eclipse 2020-12中很容易找到了:选中Eclipse标记的import语句中的org.w3c.dom.Document,在Type Hierarchy对话框中右键单击顶部的Document并选择Implementors > Workspace,以显示导入org.w3c.dom.Document(或您选择的任何从多个模块访问的类型)的所有项目中的所有JAR。 - MikeOnline

5
这里发生的情况是您有一个通配符导入,如import org.w3c.dom.*,表示您要从包org.w3c.dom导入所有类。现在,如果org.w3c.dom中至少有一个类由第二个源提供,Java就不能启动(正如此处所指出的)。
(顺便说一句,在更近期的Eclipse版本中,“...无法解析”消息被更准确的错误消息“包org.w3c.dom可以从超过一个模块访问:<未命名>,java.xml”替换,参见Stephan Herrmann的此合并变更请求。) 解决这个问题的方法
  1. 打开“打开类型”对话框(Ctrl+Shift+T)。
  2. 输入完整的导入,如 org.w3c.dom.*org.w3c.dom.
  3. 检查整个列表是否有多个来源。这里的所有条目都应该只包含类似于“jdk-11-…”之类的内容。
  4. 收集包含你有多个来源的类的所有 JAR 文件。
  5. pom.xml 中打开“依赖层次结构”选项卡。
  6. 搜索 JAR 文件。
  7. 添加排除项(右键单击或手动编辑 pom.xml)。

示例

我在我的 pom.xml 中有以下的 findbugs 依赖项:

<dependency>
    <groupId>com.google.code.findbugs</groupId>
    <artifactId>findbugs</artifactId>
    <version>${findbugs.version}</version>
</dependency>

Findbugs有两个需要排除的依赖项:

<dependency>
    <groupId>com.google.code.findbugs</groupId>
    <artifactId>findbugs</artifactId>
    <version>${findbugs.version}</version>
    <exclusion>
        <groupId>xml-apis</groupId>
        <artifactId>xml-apis</artifactId>
    </exclusion>
    <exclusion>
        <groupId>jaxen</groupId>
        <artifactId>jaxen</artifactId>
    </exclusion>
</dependency>

1
这对我有用。升级到Java11并从另一个依赖项中引入的依赖项创建了冲突。一旦在POM中排除它,它就只使用Java提供的依赖项。我的是javax.xml.namespace.QName <exclusions> <exclusion> <groupId>weblogic</groupId> <artifactId>wlfullclient</artifactId> </exclusion> </exclusions> - Skystrider
1
我尝试了很多解决方案,只有这一个对我起作用。 - Enchantres

4

我在Eclipse 4.8.0和JDK 10下看到了非常类似的问题。例如:

import org.w3c.dom.Element;

在Eclipse中编译时出现错误:The import org.w3c.dom.Element cannot be resolved

即使在该导入上按下F3(打开声明),Eclipse也能够打开接口定义 - 在这种情况下是在xml-apis-1.4.01.jar下。

与此同时,直接从Maven构建的版本正常工作。

在这种情况下,修复方法是从pom.xml中删除此依赖项:

    <dependency>
        <groupId>xml-apis</groupId>
        <artifactId>xml-apis</artifactId>
        <version>1.4.01</version>
    </dependency>

然后在Eclipse中编译错误消失了。再次按下 F3,将显示 Element 接口,在项目的 JRE 系统库下的 java.xml 模块中找到。同时 Maven 构建仍然良好。这感觉像是 Eclipse 解析同时存在于 JDK 模块和依赖的 .jar 文件中的类时遇到的问题。有趣的是,在另一个环境下,即在 Eclipse 4.9.0 和 JDK 11 下,无论是否包含 xml-apis:1.4.01 依赖项,一切都很好。

但是2018-12 M3呢?我不会回到4.9.0。我将使用Java 11向前发展。我无法降级Eclipse! - Garret Wilson
在2018-12 RC1 + JDK 11下运行良好 - NullPointerException也消失了 - df778899
那么您的意思是删除通配符导入将解决问题?这只针对与 XML 相关的导入吗,还是我必须对所有导入都这样做?我需要对所有文件都这样做,还是只需对具有 XML 相关导入的文件这样做? - Garret Wilson
1
没错。我认为可能是任何JDK级别的通配符导入 - 例如import java.net.*也是一个问题。或者说Eclipse对于模块的通配符导入存在问题。它影响了您提到的源代码中的6个文件 - 更改集在此处(https://github.com/df789/jdk10-eclipse/commit/3a989132a4abdab5073827628079df5db93b1be3)。 - df778899
1
只是想提醒任何遇到此问题的人,xercesImpl:2.12.0也包括org.w3c.dom.html包,可能也会导致类似的问题。 - Thorsten Wendelmuth
显示剩余3条评论

3

这更像是一个解决方法,但是根据我的经验,可以通过进入“Java Build Path”、“Order and Export”选项卡,并将“Maven Dependencies”移到底部(在“JRE System Library”下面)来解决问题。


谢谢,我会尝试,但这并不是一个可行的解决方案,因为我们不断添加子模块和刷新Maven配置等,需要持续开发。 - Garret Wilson
1
不幸的是,即使是这个解决方法对我也没有起作用。 - Garret Wilson

2

谢谢提供这个线索,我之前一直找不出org.w3c.dom.Document冲突的引用在哪里。在Eclipse 2020-12中,我通过以下方法轻松找到了:选中Eclipse标记的import语句中的org.w3c.dom.Document,右键单击并选择“打开类型层次结构”,在类型层次结构对话框中右键单击顶部的Document,选择“实现者”>“工作区”,以显示导入org.w3c.dom.Document(或您所选的可从多个模块访问的任何类型)的所有项目中的所有JAR文件 – MikeOnline昨天

按照早期帖子上述的指示帮助我们解决了问题。我们所做的是用batik中的GenericDocument替换Document和用GenericElement替换Element - 编译错误消失了 - 现在我们只需要测试确保实现与java 8下的相同。感谢MikeOnline


0

JDK 9+ 带来了与项目 Jigsaw 相关的更改。JDK 被分解为各种模块,一些模块,如 javaee、jaxb 和 xml 相关的模块,默认情况下不再加载。您应该直接将这些模块添加到您的 Maven 构建中,而不是期望它们在 JRE 类路径中。请参见此 SO 问题


1
感谢您的回答。我认为所涉及的类不是其他SO问题中提到的那些类之一。 我的问题类都来自maven依赖项xml-apis-1.4.01。该依赖项(与所有其他maven依赖项一样)是构建路径的一部分。 - Carsten
最终这并不会改变任何事情,你必须定义一个 module-info.java 定义文件,参见 https://www.oracle.com/corporate/features/understanding-java-9-modules.html,否则它将无法工作。 - khmarbaise
感谢您的评论。但我不确定这是否正确。 在Eclipse路径中,Maven依赖项位于CLASSPATH而不是MODULEPATH中,因此它不应该像非模块jar一样处理吗?另外,为什么只有这一个jar受到影响,而其他许多类似的Maven依赖项却没有?最后,为什么只有Eclipse内部构建报告错误,而命令行Maven和Eclipse中的显式Maven构建都成功了呢? - Carsten
我遇到了同样的问题。我可以使用javac成功编译我的项目,但是Eclipse找不到这些类。你找到解决这个问题的方法了吗? - Tom
2
我将此答案标记为不相关,因为我看不出它与问题有任何关系。这是一个类路径问题,而不是模块问题。此外,它可以在命令行上使用Maven+JDK工作。如果它可以在Maven+JDK中工作但无法在Eclipse中工作,则问题在于Eclipse,而所有关于JDK如何被拆分成模块的散文都是次要的,虽然有趣且不一定不正确,但仍然是无关紧要的。 - Garret Wilson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接