2015-10-20 92 views
2

我试图通过实验将一个MAVEN java项目移植到gradle。我遇到的其中一个问题是由于NoSuchMethodError在运行时(执行期间)发生而未能执行单元测试。我打电话给FileUtils.write()方法。NoSuchMethodError在测试下运行gradle

我修改代码来跟踪在加载文件实用程序类的类加载器提供类路径和我有以下几点:

C:/Sdk/gradle-2-7/lib/commons-io-1.4.jar 
C:/Users/<me>/.gradle/caches/modules-2/files-2.1/commons-io/commons-io/2.4/b1b6ea3b7e4aa4f492509a4952029cd8e48019ad/commons-io-2.4.jar 

出的,我看到有2个版本的commons-IO的在测试运行期间的类路径和来自Gradle的类路径是第一个,因此具有更高的优先级。

根本原因是什么?这可以如何解决?

实际上,我希望除了我的gradle项目的依赖关系中明确声明的classpath之外,没有JAR可用。


更新:看来,我已经得到了有关的根本原因的想法 - 被测试的项目是“gradle这个插件”,并编译它的gradle中我有build.gradle到指定以下内容:

dependencies { 
    compile gradleApi() 
} 

这渗透到我的项目捕获所有gradle依赖项。虽然我还没有看到的方式来解决这个问题:

  • 我无法将其删除,因为该项目不会编译
  • 我不能排除一些事情,因为gradle这个不支持此为gradleApi()(见http://gradle.1045684.n5.nabble.com/exclude-some-dependencies-from-gradleApi-dependency-td5712103.html) 。
  • 我不能只添加那些我真正需要的gradle jar作为依赖关系 - 我没有看到任何方式在编译依赖关系中明确引用它们,我没有看到任何包含这些工件的公共存储库。注意:对于MAVEN构建,我已手动将它们上载到本地MAVEN库中。
+0

是您试图使用FileUtils.write但旧版本(1.4)没有该方法(类'碰撞')的问题? – Ethan

+0

这是发生的问题,但根问题稍有不同 - 我在插件中引用的库使用commons-io的较新版本,而gradle提供的运行时提供较旧的版本。所以...... gradle应该在测试时以某种方式提供ClassLoaders的分离,让“自己的代码”和“单元测试下的插件代码”与不同版本的第三方库共存。 –

+0

Gradle在这里做着正确的事情,保护您免受不正确的测试。当你的插件被加载时,它会运行到你的测试看到的相同的jar错过匹配。我会尝试使用Java8或Groovy来解决需要这个库。 – Ethan

回答

0

好吧,我最初的诊断是关于“Gradle没有将插件的类路径与自己的类路径隔离”是错误的。根本原因实际上是...

dependencies { 
    compile gradleApi() 
} 

...摄入许多依赖项(包括commons-io:1.4)。而正确的解决方案(至少可行的对我来说)是只包括必须的jar明确:

versions = [ 
    gradle: "2.8", 
    groovy: "2.4.4", 
] 

dependencies { 
    compile "org.codehaus.groovy:groovy-all:${versions.groovy}" 
    compile "org.gradle:gradle-base-services:${versions.gradle}" 
    compile "org.gradle:gradle-base-services-groovy:${versions.gradle}" 
    compile "org.gradle:gradle-core:${versions.gradle}" 
    compile files("lib/gradle-platform-jvm-${versions.gradle}.jar") 
} 

注:

  • gradle这个核心的依赖应该通过公共工件标识符(通过直接JAR文件不能被引用引用)。通过这种方式,gradle在运行时使用关联的POM来构建传递运行时依赖关系。否则,在执行单元测试期间,您将得到NoClassDefFoundError,因为其他Gradle的内部组件不在执行classpath中。

  • 看起来好像并非所有gradle的工件都在公共仓库(jcenter)中可用。如果在你的插件中你要建立对其他'本地'任务(比如'jar')的依赖关系,那么它们的实现必须通过JAR文件直接引用。

0

TL; DR

不要使用commons-10:2.1,要么使用Java8,Groovy中,或做一个帮手,而不是使用commons-IO的。

Explination

的根本原因是gradleApi()包括公共-10:1.4,当测试正在执行最终不得不在classpath中,这两个公共-io的罐子和1.4版本恰好是较早如此这就是为什么你得到NoSuchMethodError

发生这种情况是因为Gradle没有像Maven那样在单独的类加载器中隔离每个插件。鉴于Gradle的工作方式,你不能。这是因为在Gradle中,一个好的设计策略是有多个插件连在一起做一件事。你有一个插件可以将事物配置为通用的,并且对你将如何使用它没有意见。然后,你有一个非常有见地的插件,配置了一个有很多假设的项目。当用户不像你一样分享相同的观点时,这很有用,他们可以应用基本插件并应用他们自己的意见。

更具体的例子是你有一个包含其他插件使用的extension的jar。这可能是像“failBuildOnQualityError”。然后,每个单独的插件(使用不同的坐标)将尝试使用该扩展来查看当checkstyle,findbugs,jacoco等发现问题时它们是否应该失败。

+1

“不要使用commons-io:2.1”表示不要使用任何使用“commons-io:2.1”的库,这意味着 - 使用Java8,Groovy ......重写整个库“当然不可接受。 –

+0

另一种方法是创建一个主类,然后在javaexec中加载一个完全独立的类加载器,您可以在其中添加任何想要的jar。 – Ethan

相关问题