为什么 Maven 不解决公共配置的所有依赖项?

Why is Maven not resolving all dependencies for commons-configuration?

总结

当仅使用 commons-configuration 1.10 尝试 XMLConfiguration configuration = new XMLConfiguration("config/config.xml"); 时,我需要在我的 Maven 设置中添加更多依赖项(即 commons-collections 不比 3.2.1 更新)。为什么会这样,为什么 Maven 不简单地解决所有需要的依赖关系?

详情

我正在努力让 commons-configuration 工作。首先,我想使用最新版本 2.0-alpha2,它根本不能正常工作,因为我无法配置 Maven 以下载正确的资源 - 但那是另一回事了。

在我发现 1.10 版本实际上是 "one point ten"(而不是 "one point one zero")并且因此是 commons-configuration 1 的最新版本(并且包含在教程中)之后,我决定提供试试吧。

对于我的 Maven 依赖项(集成在 Eclipse 中)我使用了:

<dependency>
    <groupId>commons-configuration</groupId>
    <artifactId>commons-configuration</artifactId>
    <version>1.10</version>
</dependency>

但是,在尝试这个例子时:

package main;

import java.util.Iterator;

import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.XMLConfiguration;

public class ConfigurationTest {
    public static void main(String... args) {
        try {
            XMLConfiguration configuration = 
                    new XMLConfiguration("config/config.xml");
            Iterator<String> iterator = configuration.getKeys();
            while (iterator.hasNext()) {
                System.out.println(iterator.next());
            }
        } catch (ConfigurationException e) {
            e.printStackTrace();
        }
    }
}

与以下 config.xml:

<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<configuration>
  <property>value</property>
  <nestedproperty>
    <arrayvalue>0,1,2,3,4</arrayvalue>
    <property>anothervalue</property>
  </nestedproperty>
</configuration>

我收到错误:

Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/commons/collections/CollectionUtils
    at org.apache.commons.configuration.XMLConfiguration.constructHierarchy(XMLConfiguration.java:640)
    at org.apache.commons.configuration.XMLConfiguration.initProperties(XMLConfiguration.java:596)
    at org.apache.commons.configuration.XMLConfiguration.load(XMLConfiguration.java:1009)
    at org.apache.commons.configuration.XMLConfiguration.load(XMLConfiguration.java:972)
    at org.apache.commons.configuration.XMLConfiguration$XMLFileConfigurationDelegate.load(XMLConfiguration.java:1647)
    at org.apache.commons.configuration.AbstractFileConfiguration.load(AbstractFileConfiguration.java:324)
    at org.apache.commons.configuration.AbstractFileConfiguration.load(AbstractFileConfiguration.java:261)
    at org.apache.commons.configuration.AbstractFileConfiguration.load(AbstractFileConfiguration.java:238)
    at org.apache.commons.configuration.AbstractHierarchicalFileConfiguration.load(AbstractHierarchicalFileConfiguration.java:184)
    at org.apache.commons.configuration.AbstractHierarchicalFileConfiguration.<init>(AbstractHierarchicalFileConfiguration.java:95)
    at org.apache.commons.configuration.XMLConfiguration.<init>(XMLConfiguration.java:261)
    at main.ConfigurationTest.main(ConfigurationTest.java:12)

我首先希望他们(当然不是我)只是搞砸了一些 Maven 依赖项,因为我不会再为使用哪个版本而烦恼(我没有得到 2.0 工作,记得吗?)我决定通过将 Maven 依赖项替换为以下内容来降到版本 1.9:

<dependency>
    <groupId>commons-configuration</groupId>
    <artifactId>commons-configuration</artifactId>
    <version>1.9</version>
</dependency>

很好的解决了问题,测试用例是运行:

property
nestedproperty.arrayvalue
nestedproperty.property

但是当我尝试实现一个与 Very simple Apache-commons configuration example throws NoClassDefFoundError 中引用的示例类似的示例及其后续问题时,我得到了与那里引用的完全相同的错误 - 但解决方案是导入 org.apache.commons.beanutils.PropertyUtils 不工作,因为我缺少 beanutils。所以基本上通过降级我只是从缺少集合的错误切换到缺少 beanutils。

有一个 dependency overview,您可以在其中查看在执行操作时使用了哪些依赖项。我有点惊讶地得知 1.10 版现在使用了比 1.9 版在构造函数调用中使用的其他依赖项(即 CollectionUtils)。由于 1.10 和 1.9 中存在依赖性问题,我只是坚持使用较新的版本。

我在以下工件中找到了 CollectionUtils(我被它的 maven repository 指向那里):

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-collections4</artifactId>
    <version>4.0</version>
</dependency>

可悲的是,那个(起初对我来说并不明显)没有在包 collections 中定义 class CollectionUtils,而是在包 collections4 中定义。在依赖概述上暗示了这个问题,但他们只提到了早期版本可能存在的问题......我似乎已经不再考虑它了,只是简单地将依赖更改为:

<dependency>
    <groupId>commons-collections</groupId>
    <artifactId>commons-collections</artifactId>
    <version>3.2.1</version>
</dependency>

在使用这些依赖项后,我得到了所有的工作(或多或少,但我现在得到的异常不再依赖于缺少的 class 定义):

<dependencies>
    <dependency>
        <groupId>commons-configuration</groupId>
        <artifactId>commons-configuration</artifactId>
        <version>1.10</version>
    </dependency>
    <dependency>
        <groupId>commons-collections</groupId>
        <artifactId>commons-collections</artifactId>
        <version>3.2.1</version>
    </dependency>
    <dependency>
        <groupId>commons-beanutils</groupId>
        <artifactId>commons-beanutils</artifactId>
        <version>1.9.2</version>
    </dependency>
</dependencies>

为什么我必须自己添加依赖项?我认为使用 Maven 的全部意义在于避免必须做这样的事情,并且在 javadoc 和源文件方面它做得很好。

到目前为止,我确信依赖项在设计上没有包含在层次结构中(是吗?),可能是为了避免开销。但是,有没有一种方法可以简单地一次获取所有依赖项,或者更好地获取我需要的所有依赖项?为什么要这样设计?

如果我们分析 commons-configuration 的 POM,我们会发现 commons-collections 依赖项是可选的:

  <dependencies>
    <dependency>
      <groupId>commons-collections</groupId>
      <artifactId>commons-collections</artifactId>
      <version>3.2.1</version>
      <optional>true</optional>
    </dependency>
    ...

此外,来自 Maven 文档:

If a user wants to use functionality related to an optional dependency, they will have to redeclare that optional dependency in their own project.

Maven 尝试为您在 pom.xml 中使用的库解决所有必要的依赖关系。好吧,有时您有一些依赖项,这些依赖项仅对某些特定功能是必需的,并且您不想强迫您的依赖项的用户在不使用它时下载它。然后你将你的依赖声明为 optional. This happened with commons-collections within commons-configuration. See commons-configuration-pom here

Commons 配置网站的 Runtime dependencies 页面上解释了这个问题。

引用该页面:

A lot of dependencies are declared in the Maven POM. These are all needed during compile time. On runtime however you only need to add the dependencies to your classpath that are required by the parts of the Commons Configuration package you are using. The following table helps you to determine which dependencies you have to include based on the components you intend to use.

其他答案从 Maven 的角度解释了为什么这有效。这个答案旨在为 Commons 配置人员提供某种防御。他们至少警告过你!

如果依赖于其他 Apache Commons 组件,他们会花时间测试各种版本,并在该页面底部发布有关兼容性的信息。