为什么Maven会包含同一依赖的多个版本?

7
我有一个Maven Java Web应用程序(.WAR)项目,其中包括几个库,包括Wicket库(但我认为问题不在于Wicket本身,而是与Maven有关)。
问题在于:即使我只包含Wicket 6.20.0,结果的.WAR文件仍包含Wicket库的两个副本:6.20.0和6.18.0,如您可以在此屏幕截图中看到的那样。

enter image description here

考虑到一些冲突的导入,我使用以下命令打印了依赖树:

mvn dependency:tree

命令...但是依赖树中没有提到Wicket 6.18.0!我还使用Eclipse的“依赖层次结构”视图进行了双重检查,并确认没有该导入的痕迹。

我甚至在整个Eclipse工作区中搜索了字符串“6.18.0”,但找不到它的任何位置!

我如何找出是什么导致了该库的重复版本被包含?


3
请问您能否发布您的POM文件? - BackSlash
5个回答

9
Maven不是这样工作的。具有相同artifactId和groupId但版本不同的多个依赖项将结果为单个依赖项(使用的版本不确定)。在WAR的同一lib文件夹中存在两个具有相同artifactId和groupId但具有两个不同版本的构件可能与以下原因之一有关:你没有执行mvn clean package而只是mvn package;你使用了有缺陷的Maven war插件,请尝试更新它以进行检查;你有一个Maven插件,在组件构建期间将Wicket jars 6.18.0复制到目标文件夹的WEB-INF/lib文件夹中;你正在构建的maven WAR项目具有类型为WAR的依赖项。在这种情况下,WAR依赖项的依赖关系会被覆盖在你正在构建的WAR项目中。

一个有趣的Maven问题,由于WAR依赖关系而导致重复的JAR:

WEB-INF/lib中可以有不同版本的JAR,作为war的依赖项


你的回答评论表明你的构建中实际上有WAR依赖项。
不幸的是,目前没有真正有效的长期解决方案来绕过这个限制。

如我在评论中所说,使用maven war插件的packagingExcludes属性是实际问题的有效解决方法:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-war-plugin</artifactId>
    <version>2.4</version>
    <configuration>
        <!-- ... -->
        <packagingExcludes>WEB-INF/lib/wicket-*-6.18.0.jar</packagingExcludes>
    </configuration>
</plugin>

但要注意,使用这个方法会使你的构建变得不够健壮。当你更新WAR依赖的版本时,如果新版本再次拉取一个不同版本的Wicket,你仍然有可能在构建的WAR中拥有两个不同版本的重复JAR包。
通常更好的方法是使用覆盖功能,通过指定maven-war-pluginoverlay元素来应用覆盖。它专注于对WAR依赖项应用的覆盖,并且可以提前解决问题。因此,您可以定义排除任何来自WAR依赖项的Wicket JAR包:
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <version>2.4</version>
    <artifactId>maven-war-plugin</artifactId>
    <configuration>     
        <overlays>
            <overlay>
                <groupId>com.whatever.youlike</groupId> 
                <artifactId>myArtifact</artifactId>
                <excludes>
                    <exclude>WEB-INF/lib/wicket-*.jar</exclude>                 
                </excludes>
            </overlay>
        </overlays>
    </configuration>
</plugin>

这种方法更好,但仍然是一种解决方法。 当依赖的WAR更新并拉取新的依赖项(除Wicket之外)时,可能会遇到相同类型的问题。
我认为只有在没有选择时才应声明对WAR构件的依赖关系。 由于POM和项目重构是可能的,引入一个公共JAR依赖关系,两个WAR都依赖于它,并且仅包含两个WAR的共同源代码和资源,可以使事情变得更简单。

+1,因为你的第一个要点指引了我正确的方向,详见下面我的回答。基本上,这是一个依赖项,但是它是一种“WAR”类型的依赖项(我认为它们被称为覆盖层?)。显然,这些不会显示在构建树/依赖关系中。 - Master_T
强烈不建议在打包为WAR的构件中包含WAR类型依赖项,原因如您所见。它不会显示在dependency:tree中,因为它不是依赖关系解析的一部分。它以“原始”方式复制了lib/WAR依赖项中包含的jar文件到lib/WAR构建组件中。抱歉我不知道确切的名称:可能是怪物卡车复制?;) - davidxxx
1
自从编辑后,我接受了你的回答,因为我不喜欢采纳自己的答案:D 有兴趣了解具体细节的人,请参见我的答案:https://dev59.com/qaLia4cB1Zd3GeqPnbYq#44632079 - Master_T

2

嗯,我在探索中弄清楚了。

该项目有一个“war”类型的依赖项:

<dependency>
    <groupId>com.whatever.youlike</groupId>
    <artifactId>myArtifact</artifactId>
    <version>1.0.7-SNAPSHOT</version>
    <type>war</type>
</dependency>

显然(我之前不知道,我的过错)这些类型的依赖关系将通过将所有库复制到主WAR/libs文件夹中来包含自己在classpath中,但是这些将不会显示在依赖树/依赖层次结构中。 我通过在WAR插件中配置明确的排除解决了这个问题。
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-war-plugin</artifactId>
    <version>2.4</version>
    <configuration>
        <!-- ... -->
        <packagingExcludes>WEB-INF/lib/wicket-*-6.18.0.jar</packagingExcludes>
    </configuration>
</plugin>

1
有效的解决方法。+1 但是它有一些限制。当WAR依赖关系发生变化时,您仍然有风险在构建的WAR中具有两个不同版本的重复jar。我认为长期来看,进行pom重构将是更好的解决方案。 - davidxxx
+1,你说得对,这不是一个优雅的解决方案。我们将在下一个版本中使两个项目使用相同的版本。目前,这个快速修复可以完成工作。 - Master_T
进一步澄清:我通过WAR依赖项将其包含在内,因为所包含的项目在我们的JAR仓库中不存在。我只需要其中的一个类,重构整个项目以提取单个类到单独的共享JAR文件会显得过度。 - Master_T
你只是为了使用其中一个类而导入了一个WAR文件吗?这是一个重要的依赖关系,具有重要的副作用,以满足非常简单的需求。就我个人而言,我不会重构整个项目,但我会将这个类和所有属于同一问题的类提取到自己的构件(JAR)中,并使两个WAR文件依赖于此依赖项。如果您已经有一个适合保存这些类的构件,那么使用它仍然比增加依赖项的数量更好,因为在没有太多内容的情况下增加依赖项通常是一个坏迹象。 - davidxxx
是的,我知道,那就是我所暗示的...它会完成的,但不在这个版本中,因为时间很紧,我需要的是一个快速解决方案 ;) - Master_T
如果您只需要 WAR 项目中的一个类,您应该更改该项目并让它仅生成一个 jar 文件(archiveClasses,archiveClasses),其中仅包含 WAR 项目中的类。这可以用作真正的依赖项... - khmarbaise

1
使用clean install,这个双重依赖可能就会消失。

我总是保持清洁 ;) 问题是另一个,可以看下面我的答案。无论如何,还是谢谢你的帮助。 - Master_T

0

因为其他库可能使用相同的库但是不同的版本,或者您尝试了不同的版本但没有执行mvn clean


我在每次构建时都进行清理。问题是另一个,有关详细信息请参见我的答案。无论如何,还是谢谢你的建议。 - Master_T

0

mvn dependency:tree 命令会告诉你正确的信息 - 这里看到的是一个 Eclipse / 构建问题。

清除项目中所有的目标和构建区域。如果需要,可以将其从源代码控制检出到新文件夹中。

或者,您可以在 IntelliJ IDEA 中构建项目,查看是否获得了正确的依赖项(很可能会)。


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