问题:

我们进行了一个集成测试,该测试创建了一个Spring ClassPathXmlApplicationContext ,同时这样做导致NoSuchMethodError爆炸。 事实证明,我们对Spring构件的依赖版本存在冲突。 这本身不是一个不寻常的问题-使用Maven依赖插件使用verbose选项解决了这些问题。 但是,当Maven插件错误时,您该怎么办?

调查:

我们开始深入研究,发现AbstractAutowireCapableBeanFactory的getTypeForFactoryMethod方法尝试访问GenericTypeResolver resolveReturnTypeForGeneric方法,并在java.lang.NoSuchMethodError: org.springframework.core.GenericTypeResolver.resolveReturnTypeForGenericMethod(Ljava/lang/reflect/Method; 。

初步调查和谷歌搜索发现,该方法是在3.2.0中添加的,而我们应该在3.1.1中运行。 进一步的调查确定spring-data-mongodb依赖于范围[3.0.7-4) 1的 spring框架,并且由于maven在给定范围2的情况下采用了最新的可用版本,因此它尝试采用3.2.2。 注意,在显式版本依赖项和范围依赖项之间存在冲突的情况下,上述更改有所变化,但是IINM在确定spring mongo的依赖项时没有冲突。

该问题被两个症状进一步掩盖:

我们还有其他使用这种模式的项目,没有问题-这可以通过以下事实来解释:Maven的冲突解决机制选择默认情况下找到的最近版本3

dependency:tree显示它带来了3.1.1,而带来了3.2.2-因为堆栈跟踪显示了其他结果,所以我编写了一个测试,检查上述每个类来自哪个jar,并验证了AbstractAutowireCapableBeanFactory类确实来自spring-beans 3.2.2而不是3.1.1,如“ mvndependency:tree”所示(非常感谢http://bit.ly/10zD1iV提供了在运行时查找类的jar的代码段)。

Maven依赖项:在构件中使用显示spring-beans:3.1.1的树输出

>:mvn dependency:tree -Dverbose -Dincludes=org.springframework
...
(omitted for clarity)
...
[INFO] --- maven-dependency-plugin:2.1:tree (default-cli) @ wix-feature-toggle-administration ---
[INFO] artifact org.springframework:spring-beans: checking for updates from central
[INFO] artifact org.springframework:spring-beans: checking for updates from snapshots
[INFO] artifact org.springframework:spring-expression: checking for updates from central
[INFO] artifact org.springframework:spring-expression: checking for updates from snapshots
[INFO] artifact org.springframework:spring-tx: checking for updates from central
[INFO] artifact org.springframework:spring-tx: checking for updates from snapshots
[INFO] com.wixpress.common:wix-feature-toggle-administration:jar:2.180.0-SNAPSHOT
...
[INFO] +- org.springframework.data:spring-data-mongodb:jar:1.0.1.RELEASE:compile
[INFO] |  +- org.springframework:spring-beans:jar:3.1.1.RELEASE:compile
[INFO] |  |  \- (org.springframework:spring-core:jar:3.2.2.RELEASE:compile - omitted for conflict with 3.1.1.RELEASE)
[INFO] |  +- org.springframework:spring-expression:jar:3.1.1.RELEASE:compile
[INFO] |  |  \- (org.springframework:spring-core:jar:3.2.2.RELEASE:compile - omitted for conflict with 3.1.1.RELEASE)
[INFO] |  \- org.springframework.data:spring-data-commons-core:jar:1.2.1.RELEASE:compile
[INFO] |     +- (org.springframework:spring-beans:jar:3.1.1.RELEASE:compile - omitted for duplicate)
[INFO] |     \- (org.springframework:spring-tx:jar:3.1.1.RELEASE:compile - omitted for duplicate)
[INFO] +- com.wixpress.common:wix-framework:jar:2.180.0-SNAPSHOT:compile
[INFO] |  +- org.springframework:spring-core:jar:3.1.1.RELEASE:compile
[INFO] |  |  \- org.springframework:spring-asm:jar:3.1.1.RELEASE:compile
...
I've removed additional outputs for clarity. The additional outputs were all 3.1.1 and were further down the tree (so irrelevant due to maven conflict resolving mechanism)

工件中使用了证明spring-beans:3.2.2的测试(断言错误中的jvm在说什么)

package com.wixpress.springVersionBug;


import org.junit.*;
import org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory;
import org.springframework.core.GenericTypeResolver;
import java.security.CodeSource;
import static org.hamcrest.Matchers.endsWith;

/**
 * @author ittaiz
 * @since 3/24/13
 */

public class SpringVersionTest {


    @Test
    public void verifySpringBeans311InClasspath(){
        verifyCorrectSpringVersionInClasspathFor(AbstractAutowireCapableBeanFactory.class,"spring-beans-3.1.1.RELEASE.jar");
    }
    @Test
    public void verifySpringCore311InClasspath(){
        verifyCorrectSpringVersionInClasspathFor(GenericTypeResolver.class,"spring-core-3.1.1.RELEASE.jar");
    }
    public void verifyCorrectSpringVersionInClasspathFor(Class springClass,String expectedJarFileName){
        CodeSource springClassCodeSource = springClass.getProtectionDomain().getCodeSource();
        Assert.assertNotNull("expecting "+expectedJarFileName+" to be loaded by non-system class loader",springClassCodeSource);
        Assert.assertThat(springClassCodeSource.getLocation().toString(),endsWith(expectedJarFileName));
    }
}

当spring-beans成为3.2.2时, spring-core工件出现在3.1.1中的原因是我们的框架显式依赖于spring-core而该工件显式依赖于框架。 这意味着来自框架的spring-core 3.1.1是2跳,比来自spring-data-mongodb的3.2.2短。

解:

依赖spring-data-mongodb同时像这样排除spring-beans :

<dependency>
  <groupId>org.springframework.data</groupId>
  <artifactId>spring-data-mongodb</artifactId>
  <version>1.0.1.RELEASE</version>
  <exclusions>
    <exclusion>
      <groupId>org.springframework</groupId>
      <artifactId>spring-beans</artifactId>
    </exclusion>
  </exclusions>
</dependency>

开放问号:

为什么dependency:tree(在详细模式下)没有显示在3.2.2中而是在3.1.1中显示spring-beans,同时明确指定由于冲突而删除了spring-core 3.2.2? 我将此归结为依赖项插件中的错误。

  1. http://repo1.maven.org/maven2/org/springframework/data/spring-data-mongodb-parent/1.0.1.RELEASE/spring-data-mongodb-parent-1.0.1.RELEASE.pom ↩
  2. http://www.maestrodev.com/better-builds-with-maven/creating-applications-with-maven/resolving-dependency-conflicts-and-using-version-ranges/ ↩
  3. http://www.maestrodev.com/better-builds-with-maven/creating-applications-with-maven/resolving-dependency-conflicts-and-using-version-ranges/ ↩

参考: 当 Wix IO博客上的Maven依赖插件来自我们的JCG合作伙伴 Yoav Abrahami时。

翻译自: https://www.javacodegeeks.com/2013/04/when-maven-dependency-plugin-lies.html