2015-10-20 35 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

相關問題