Gradle 1.12用戶指南翻譯——第五十章. 依賴管理

本文由CSDN博客貌似掉線翻譯,其餘章節的翻譯請參見:
http://blog.csdn.net/column/details/gradle-translation.html
翻譯項目請關注Github上的地址:
https://github.com/msdx/gradledoc
本文翻譯所在分支:
https://github.com/msdx/gradledoc/tree/1.12。
直接瀏覽雙語版的文檔請訪問:
http://gradledoc.qiniudn.com/1.12/userguide/userguide.html。
另外,Android 手機用戶可經過我寫的一個程序瀏覽文檔,帶緩存功能的,目前0.2.1版本兼容 Android 2.2以上系統,項目地址以下:
https://github.com/msdx/gradle-doc-apk
翻譯不易,轉載請註明本文在CSDN博客上的出處:
http://blog.csdn.net/maosidiaoxian/article/details/53616326

關於我對Gradle的翻譯,以Github上的項目及http://gradledoc.qiniudn.com 上的文檔爲準。如發現翻譯有誤的地方,將首先在以上兩個地方更新。因時間精力問題,博客中發表的譯文基本不會同步修改。html


第五十章. 依賴管理

50.1. 介紹

依賴管理是每一個構建的關鍵特性,而且 Gradle 強調提供最好的依賴管理,容易理解以及使用各類各樣的方法來兼容。若是您熟悉使用 Maven 或 Ivy 的方法,那麼你會很高興地知道,Gradle 徹底兼容這兩種方法,除此以外,它還有足夠的靈活性,以支持徹底自定義的方法。java

這裏是 Gradle 支持的依賴管理的主要亮點:git

  • 依賴管理傳遞: Gradle 賦予你對你的項目依賴項樹的徹底控制。github

  • 對非管理依賴的支持:若是你的依賴只是版本控制下或共享的驅動器中的文件,Gradle 也提供了強大的功能以支持這種狀況。spring

  • 支持依賴定義的自定義:Gradle 的模塊依賴給你在構建腳本中描述依賴項層次結構的能力。apache

  • 一個徹底可自定義的依賴解析的方法:Gradle 爲你提供了自定義解析規則的能力,讓你能夠輕鬆替換依賴。編程

  • 徹底兼容 Maven 和 Ivy :若是你已經在 Maven POM 文件或 Ivy 文件中定義了依賴,Gradle 提供了一系列受歡迎的構建工具能夠進行無縫集成。api

  • 與現有依賴管理基礎結構的集成:Gradle 兼容 Maven 和 Ivy 倉庫。若是你使用 Archiva, Nexus,或者是 Artifactory, Gradle 100% 兼容全部倉庫的格式。數組

成千上萬的開源組件互相依賴,每一個組件都有一系列的版本及不兼容性,隨着構建變得愈來愈複雜,依賴管理經常出現各類問題。當一個構建的依賴樹變得笨拙時,你的構建工具不該該強迫你對依賴管理採起單1、不靈活的作法。一個正確的構建系統必須被設計得很靈活,而 Gradle 能夠應付任何狀況。緩存

50.1.1. 爲了遷移的靈活的依賴管理

依賴管理在從一個構建系統遷移到另外一個的過程當中尤爲具備挑戰性。若是你從一個像 Ant 或 Maven 這樣的工具遷移到 Gradle,你可能會面臨一些困難的狀況。例如,一個常見的模式是,有一個 Ant 項目,而它保存在文件系統中的一些 jar 文件缺乏版本號。其餘的構建系統在遷移以前須要對這種方法進行大量的替換。使用 Gradle,你可讓你的新構建適配任何現有的依賴來源或依賴元數據。這使得增量遷移到 Gradle 比其餘的要容易得多。在大多數的大項目中,構建遷移和對發展過程的任何更改都是增量的,由於大多數組織不能負擔得起中止一切事情並遷移到依賴管理的構建工具的想法。

即便你的項目使用一個自定義的依賴管理系統,或者是一些像 Eclipse 做爲依賴管理的主數據的 .classpath 文件,那麼能夠很容易地寫一個 Gradle 插件在 Gradle 中使用此數據。出於遷移的目的,這是在 Gradle 中的一種經常使用技術。(可是,一旦你已經遷移,遠離 .classpath 文件,直接使用 Gradle 的依賴管理功能會是一個好主意。)

50.1.2. 依賴管理和 Java

使人哭笑不得的是,以其豐富的開源組件庫著稱的語言,Java 居然沒有庫或者版本的概念。在 Java 中,沒有標準的方法來告訴 JVM 你使用的是 3.0.5 版本的 Hibernate,也沒有標準的方法來代表 foo-1.0.jar 依賴於 bar-2.0.jar。這致使了外部的解決方案一般都會基於構建工具。目前最受歡迎的解決方案是 Maven 和 Ivy。Maven 提供了一個完整的構建系統,而 Ivy 則徹底着眼於依賴管理。

這兩種工具都依賴於描述符 XML 文件,包含有關具體某個 jar 的依賴的信息。這二者都使用了存儲庫,在存儲庫中實際的 jar 文件和它們的描述文件都放在一塊兒,並且這二者經過窗口或其餘的方式都提供了 jar 版本衝突的解決方案。這二者都已成爲解決依賴衝突的標準,而 Gradle 最初使用的是 Ivy 引擎的依賴管理。Gradle 已經取代了 Ivy 上的直接依賴,而採用了原生的 Gradle 依賴解決引擎,它支持許多的依賴解決方案的方法,包括 POM 文件和 Ivy 描述文件。

50.2. 依賴管理的最佳實踐

因爲 Gradle 對依賴管理有強烈的主張,該工具使你能夠選擇兩個選項之間:遵循推薦的最佳實踐或支持任何你能想到的類型的模式。本節概述 Gradle 項目推薦的管理依賴的最佳實踐。

無論用什麼語言,對每一個項目而言,適當的依賴管理是很重要的。從一個由 Java 編寫的依賴了數以百計開源庫的複雜企業應用,再到依賴少許庫的最簡單的 Clojure 應用,依賴管理的辦法大不相同,而且能夠依賴於目標的技術、應用程序部署的方法和項目的性質。項目捆綁做爲可重用的庫,比起企業應用集成到更大的軟件和基礎設施的系統中,可能有不一樣的要求。儘管這一要求變化很大,Gradle 項目建議全部項目都遵循這套核心規則:

50.2.1. 在文件名中包含版本號(版本化 jar 文件)

在文件名中庫的版本必須是容易辨認的。Jar 的版本一般是在清單文件中,當你要檢查一個項目時它並不顯而易見。若是有人讓你看 20 個 jar 文件,你更喜歡哪種?一個文件命名爲 beanutils-beanutils-1.3.jar 的集合,仍是文件命名爲 spring.jar 的集合?若是依賴的文件名稱帶有版本號,那麼將更容易快速肯定依賴的版本。

若是版本不清楚,你可能引入一些很難找到的微妙的錯誤。例如可能有一個項目使用 Hibernate 2.5。想一下一個開發人員決定在她的機器安裝 3.0.5 的版本,以修復一個關鍵的安全 bug,但她忘記通知其餘團隊這個變化。她可能成功地解決了這個安全 bug,但她也可能有引入一些 bug 到代碼庫中,由於項目用到了 Hibernate 如今棄用的功能。一週後在集成的機器上可能會有一個異常,而這個異常沒法在任何人的機器上覆現。而後多個開發人員花了數天的時間在這個問題上,終於意識到若是他們知道 Hibernate 已經從 2.5 升級到 3.0.5,這個錯誤會很容易發現。

在 jar 文件中包含版本號提升了在你的項目中的表現,而且讓它們更易於維護。這種作法也減小了潛在的錯誤。

50.2.2. 管理傳遞依賴

傳遞依賴管理是一種技術,讓你的項目可以依賴那些反過來依賴其餘庫的庫。這種遞歸模式傳遞依賴的結果是,在一個依賴樹中,會包含你的項目的第一級依賴、第二級依賴,等等。若是你不把你的依賴做爲層次結構樹的第一級和第二級依賴的模型,那麼在對未組織的依賴進行混亂的組裝以後,就會很容易地失去控制。考慮 Gradle 項目自己,雖然 Gradle 僅有幾個直接的第一級依賴,當 編譯 Gradle 時在它的類路徑上會須要超過一百個的依賴。在更大的規模上,使用 Spring,Hibernate和其餘的庫,旁邊數百或數千個內部項目的企業應用,也有很是大的依賴樹。

當這些大的依賴樹須要更改時,你常常得解決一些依賴的版本衝突。好比說一個開源庫須要一個日誌庫的一個版本,而另外一個庫須要日誌庫的另外一個版本。Gradle 和其餘的構建工具都可以處理這種關係樹和解決衝突問題,但不一樣的是,Gradle 讓你能夠控制傳遞依賴和衝突的解決。

雖然你能夠嘗試手動管理這個問題,你很快就會發現這種方法不能擴展。若是你想要擺脫第一級的依賴,你不能真正肯定還有哪些 jar 文件你是須要刪除的。第一級依賴的依賴項也多是第一級依賴自己,或者也多是另外一個第一級依賴的傳遞依賴。若是你嘗試本身管理傳遞依賴,最終的結果是你的構建會變得很脆弱:沒有人敢去改變你的依賴,由於破壞構建的風險過高。項目的類路徑會變得一片狼藉,而且,若是類路徑出現問題時,那簡直就是人間地獄。

注: 在一個項目中,咱們發如今類路徑中有一個神祕、LDAP 相關的 jar 包。沒有代碼引用這個 jar 包,這個 jar 包也與該項目沒有任何鏈接。沒人能弄清楚這個 jar 包是幹什麼用的,直到它被從構建中移除,而後應用在試圖向 LDAP 進行身份驗證的時候,遇到了很大的性能問題。這個神祕的 jar 包是一個必需傳遞的,第四級的依賴,很容易被忽略,由於沒有人會費心去使用託管的傳遞依賴。

Gradle 向你提供了不一樣的方式來表達第一級的和傳遞的依賴。經過 Gradle 你能夠混合使用和適配一些方法;例如,你能夠在 SCM 中存儲你的 jar 包,而不須要 XML 描述符文件,而且仍然使用傳遞依賴管理。

50.2.3. 解決版本衝突的問題

相同的 jar 包的衝突版本應該被檢測到,而且要麼解決,要麼拋出異常。若是你不使用傳遞依賴管理,版本衝突沒被發現,那麼在類路徑中沒法預測的順序,將致使不知道會使用哪個版本的依賴。對於許多開發人員都會更改依賴的大型項目,成功的構建將會少之又少,由於依賴的順序可能會直接影響到構建是否成功(或者在產品中是否會出現一個 bug)。

若是你尚未處理過在類路徑中 jar 包版本衝突的麻煩,這裏有一個小趣聞等着你。在一個有30個子模塊的大型項目中,向子項目添加的一個依賴改變了類路徑的順序, Spring 2.5 與老的 2.4 版本的順序被交換。雖然能夠繼續構建,開發者已經開始注意到在生產中出現了各類使人驚訝(和驚人可怕)的 bug。然而,更糟糕的是,無心下降版本的 Spring 向系統引入了幾個安全漏洞,如今須要在整個組織中進行全面的安全審覈。

總之,版本衝突是很很差的,你應該管理你的傳遞依賴,以免它們。你也可能想要了解版本衝突用到的地方,而且在你的整個組織中統一一個指定版本的依賴。經過一個好的衝突報告工具,像 Gradle,這些信息能夠用於與整個組織溝通,並在一個單一的版本上實現標準化。若是你以爲你不會發生版本衝突,再想一想。 不一樣的第一級依賴,依賴於一系列不一樣的重疊版本的其餘依賴,這種狀況是很常見的,而 JVM 還不能提供簡單的方法,使得能在類路徑中讓相同的 jar 包能夠有不一樣的版本(請參閱第 50.1.2 節,「依賴管理和 Java」)。

Gradle 提供瞭如下的衝突解決策略:

  • Newest:使用最新版本的依賴這是 Gradle 的默認策略,只要版本都能向後兼容,每每是合適的選擇。
  • Fail:一個版本衝突將致使構建失敗。這種策略強制在構建腳本中明確地解決全部的版本衝突。有關如何顯式選擇一個特定版本的詳細信息,請參閱ResolutionStrategy 

雖然上面介紹的策略一般足夠解決大部分的衝突,可是 Gradle 也提供更細粒度的機制,以解決版本衝突:

  • 強制配置第一級依賴。若是衝突中的依賴已是第一級的依賴,那麼這種方法會頗有用。請參閱DependencyHandler中的示例。
  • 強制配置任何依賴項(無論是否可傳遞)。若是衝突中的依賴是傳遞依賴,那麼這種方法會頗有用。它也能夠用於強制第一級依賴的版本。請參閱ResolutionStrategy中的示例。
  • 依賴解析規則是一個在 Gradle 1.4 引進的孵化中的功能,讓你能夠對特定的依賴細粒度地控制所選定的版本。

爲了解決版本衝突問題,報告依賴關係圖也是頗有幫助的。這些報告是依賴管理的另外一個功能。

50.2.4. 使用動態版本和變化的模塊

有許多狀況,是你想要使用一個特定依賴的最新版本,或者是某個版本範圍內的最新版。這能夠是在開發中須要,或者你可能正在開發一個庫,它被設計爲使用一個範圍內的依賴版本。你能夠經過使用動態版本很容易地依賴這些不斷變化的依賴。一個動態的版本能夠是一個版本範圍(例如2.+),也能夠是表示可用的最新版本的佔位符(例如latest.integration)。

另外,你請求的模塊隨着時間推移,即便是同一版本,有時也可能改變了。這種變化模塊的類型的一個例子是 MavenSNAPSHOT模塊,它老是指向最新發布的構件。換句話說,一個標準的 Maven snapshot 是一個這樣的模塊,它永遠不會不變,能夠說,它是「不斷變化的模塊」。

動態版本變化模塊的主要區別是,當你解析一個動態版本時,你會獲得真正的、 靜態的版本做爲模塊名稱。當你解析一個變化模塊時,這個 artifacts 使用你請求的版本進行命名,但下層的 artifacts 可能隨時會有變化。

默認狀況下,Gradle 對動態版本和變化模塊的緩存時間是24小時。你可使用命令行選項重寫默認的緩存模式。你能夠經過resolution strategy修改你的構建的緩存到期時間(見第 50.9.3 節,「調整控制依賴緩存」)。

50.3. 依賴配置

在 Gradle 中,依賴被分組到配置中。配置有一個名字和許多屬性,而且它們可以互相繼承。許多 Gradle 插件會向你的 project 添加預約義的配置。例如,Java 插件會添加一些配置來表示它所須要的不一樣的類路徑。詳細信息請參閱第 23.5 節,「依賴管理」 。固然,你能夠添加自定義配置到這上面。關於自定義配置,有許多的用例。這是很是方便的,例如添加依賴時不須要構建或測試你的軟件(好比,將會與發佈的軟件一塊兒的額外的 JDBC 驅動程序)。

一個項目的配置被一個 configurations 對象所管理。你傳給這個 configurations 對象的閉包會經過它對應的 API 被應用。要了解更多關於此 API 的內容,能夠看看ConfigurationContainer

若是要定義配置:

示例 50.1. 配置的定義

build.gradle

configurations {
    compile
}

若是要訪問配置:

示例 50.2. 訪問配置

build.gradle

println configurations.compile.name
println configurations['compile'].name

配置一個配置:

示例 50.3. 配置一個配置

build.gradle

configurations {
    compile {
        description = 'compile classpath'
        transitive = true
    }
    runtime {
        extendsFrom compile
    }
}
configurations.compile {
    description = 'compile classpath'
}

50.4. 如何聲明依賴

你能夠聲明幾種不一樣類型的依賴:

表 50.1. 依賴類型

類型 描述
外部模塊依賴 對一些倉庫中的外部模塊的依賴
項目依賴 在同一個構建中對另外一個項目的依賴
文件依賴 對本地文件系統中的一些文件的依賴
客戶端模塊依賴 對外部模塊的依賴,該塊部模塊的 artifacts 存儲於一些倉庫中,可是模塊的元數據由本地構建指定。當你想要重寫模塊的元數據時,你可使用這種類型的依賴。
Gradle API 依賴 對當前的 Gradle 版本的 API 的依賴當你正在開發自定義的 Gradle 插件和任務類型時,你可使用這種類型的依賴。
本地的 Groovy 依賴 對當前的 Gradle 所使用的 Groovy 版本的依賴。當你正在開發自定義的 Gradle 插件和任務類型時,你可使用這種類型的依賴。

50.4.1. 外部模塊依賴

外部模塊依賴是最多見的依賴。它們引用外部倉庫中的模塊。

示例 50.4. 模塊依賴

build.gradle

dependencies {
    runtime group: 'org.springframework', name: 'spring-core', version: '2.5'
    runtime 'org.springframework:spring-core:2.5', 'org.springframework:spring-aop:2.5'
    runtime(
        [group: 'org.springframework', name: 'spring-core', version: '2.5'],
        [group: 'org.springframework', name: 'spring-aop', version: '2.5']
    )
    runtime('org.hibernate:hibernate:3.0.5') {
        transitive = true
    }
    runtime group: 'org.hibernate', name: 'hibernate', version: '3.0.5', transitive: true
    runtime(group: 'org.hibernate', name: 'hibernate', version: '3.0.5') {
        transitive = true
    }
}

有關更多的例子和完整的參考,請參閱DependencyHandler 

Gradle 爲模塊依賴提供了不一樣的標記法。有 string 標記法和 map 標記法。模塊依賴有一個 API,用於進行進一步的配置。要了解全部該 API 的內容,能夠參閱ExternalModuleDependency 。該 API 提供了一些屬性和配置方法。經過 string 標記法,你能夠定義一個屬性的子集。而經過使用 map 標記法,你能夠定義全部的屬性。要訪問完整的 API,使用 map 或 string 標記法,你能夠把單個的依賴與一個閉包一塊兒指定給一個配置。

若是你定義了一個模塊依賴,Gradle 會在倉庫中查找相應的模塊描述符文件(pom.xmlivy.xml)。若是存在此類模塊描述符文件,它會進行分析,並下載此模塊的 artifacts (例如hibernate-3.0.5.jar)以及其依賴項(例如 cglib)。若是不存在這樣的模塊描述符文件,Gradle 會查找一個hibernate-3.0.5.jar 文件。在 Maven 中,一個模塊僅能有一個 artifact。在 Gradle 和 Ivy 中,一個模塊能夠具備多個 artifacts。每一個 artifact 能夠有一組不一樣的依賴。

50.4.1.1. 依賴有多個 artifacts 的模塊

正如前面提到的,一個 Maven 模塊都只有一個 artifact。所以,當你的項目依賴於一個 Maven 模塊時,這個模塊的 artifact 是哪個會很明顯。而使用 Gradle 或Ivy的話,狀況就不一樣了。Ivy 的依賴描述符( ivy.xml)能夠定義多個 artifact。有關更多的信息,請參閱 ivy.xml引用的Ivy。在 Gradle 中,當你聲明一個對 Ivy 模塊的依賴時,實際上你是在那個模塊的  default 配置上聲明瞭一個依賴。因此實際上,你依賴的 artifacts 集(一般是一些 jar 包) 是與該模塊的  default 配置相關聯的 artifacts 集。如下是一些比較重要的狀況:
  • 一個模塊的 default 配置包含了不但願有的 artifacts。一個依賴只是聲明瞭所需的 artifacts ,而不是依賴整個配置。
  • 所需的 artifact 屬於一個非 default的配置。這個配置被顯式地命名爲這個依賴聲明的一部分。
還有一些其餘的狀況,須要調整依賴聲明。請參閱  DependencyHandler 的例子和聲明依賴的完整參考。

50.4.1.2. Artifact only notation

如上所述,若是找不到模塊的描述符文件,默認狀況下 Gradle 會下載一個與模塊的名稱相同的 jar 文件。但有時候,即便存儲庫包含了模塊描述符,而你只是想下載 artifact jar而不下載它的依賴項。[14] 而有時候你想要從一個倉庫中下載一個 zip,而它沒有模塊描述符。Gradle 提供了一個 artifact only notation 用於這些案例狀況——只是對你想要下載的擴展前加個 '@' 標誌:

示例 505. Artifact only notation

build.gradle

dependencies {
    runtime "org.groovy:groovy:2.2.0@jar"
    runtime group: 'org.groovy', name: 'groovy', version: '2.2.0', ext: 'jar'
}

一個 artifact only notation 建立了一個模塊依賴,它只下載指定擴展名的 artifact 文件。現有的模塊描述符將被忽略。

50.4.1.3. Classifiers

Maven 依賴管理有 classifier 的概念。[15] 而 Gradle 支持這一點。若是你想從一個 Maven 倉庫中獲取 classified 依賴項,你能夠這樣寫:

示例 50.6. 使用 classifier 的依賴

build.gradle

compile "org.gradle.test.classifiers:service:1.0:jdk15@jar"
    otherConf group: 'org.gradle.test.classifiers', name: 'service', version: '1.0', classifier: 'jdk14'

如上面的第一行所示,classifiers 能夠與artifact only notation 一塊兒使用。

它能夠輕鬆地遍歷一個配置的依賴 artifacts:

示例 50.7. 遍歷一個配置

build.gradle

task listJars << {
    configurations.compile.each { File file -> println file.name }
}

gradle -q listJars的輸出結果

> gradle -q listJars
hibernate-core-3.6.7.Final.jar
antlr-2.7.6.jar
commons-collections-3.1.jar
dom4j-1.6.1.jar
hibernate-commons-annotations-3.2.0.Final.jar
hibernate-jpa-2.0-api-1.0.1.Final.jar
jta-1.1.jar
slf4j-api-1.6.1.jar

50.4.2. 客戶端模塊依賴

客戶端模塊依賴容許直接在構建腳本中聲明傳遞依賴。它們是外部庫的模塊描述符的替代者。

示例 50.8. 客戶端模塊依賴 - 傳遞依賴

build.gradle

dependencies {
    runtime module("org.codehaus.groovy:groovy-all:2.2.0") {
        dependency("commons-cli:commons-cli:1.0") {
            transitive = false
        }
        module(group: 'org.apache.ant', name: 'ant', version: '1.9.3') {
            dependencies "org.apache.ant:ant-launcher:1.9.3@jar", "org.apache.ant:ant-junit:1.9.3"
        }
    }
}

這裏定義了一個對 Groovy 的依賴。Groovy 自己具備依賴。但 Gradle 不會去查找一個 XML 描述符來找出它的依賴,而是從構建文件中獲取信息。一個客戶端模塊的依賴能夠是正常的模塊依賴,或者是 artifact 依賴項或是另外一個客戶端模塊。能夠看一看 API 文檔: ClientModule

在當前版本客戶端模塊有一個缺陷。假設你的項目是一個庫,你想要這個庫上傳到你公司的 Maven 或Ivy 倉庫。Gradle 會將你的項目的 jar 包以及這僕依賴的 XML 描述文件上傳到公司倉庫。若是你使用了客戶端模塊,在 XML 描述符文件中的依賴聲明就會不正確。咱們將在將來版本的 Gradle 修正這一點。

50.4.3. 項目依賴

對於多項目構建,Gradle 能區分外部依賴與做爲多項目構建的一部分的某個項目上的依賴。對於後者,你能夠聲明項目依賴

50.9. 項目依賴

build.gradle

dependencies {
    compile project(':shared')
}

詳細信息請參閱ProjectDependency的 API 文檔

多項目構建將在第 56 章,多項目生成中進行詳述。

50.4.4. 文件依賴

文件依賴容許你直接將一組文件添加到配置中,而不用先將它們添加到存儲庫。這將會很是有用,好比你沒法,或者是不想要把某些文件放到倉庫。或者是你若是不想使用任何倉庫來存儲你的依賴。

若是想添加一些文件做爲配置的依賴,你只須要傳一個文件集合做爲依賴:

示例 50.10. 文件依賴

build.gradle

dependencies {
    runtime files('libs/a.jar', 'libs/b.jar')
    runtime fileTree(dir: 'libs', include: '*.jar')
}

文件依賴項不會包含在你的項目的發佈的依賴描述中。然而,文件依賴會被包含在同一個構建的傳遞項目依賴裏。這意味着它們不能在當前的構建外使用,但它們能夠在同一個構建中使用。

你能夠聲明哪些任務將產生做爲文件依賴的文件。例如,你能夠在經過構建生成文件的時候這樣作。

示例 50.11. 生成文件依賴

build.gradle

dependencies {
    compile files("$buildDir/classes") {
        builtBy 'compile'
    }
}

task compile << {
    println 'compiling classes'
}

task list(dependsOn: configurations.compile) << {
    println "classpath = ${configurations.compile.collect {File file -> file.name}}"
}

gradle -q list的輸出結果

> gradle -q list
compiling classes
classpath = [classes]

50.4.5. Gradle API 依賴

你能夠經過使用DependencyHandler.gradleApi()方法,來聲明一個當前的 Gradle 版本的 API 上的依賴。當你在開發自定義 Gradle 任務或插件時將會頗有用。

示例 50.12. Gradle API 依賴

build.gradle

dependencies {
    compile gradleApi()
}

50.4.6. 本地 Groovy 依賴

能夠經過使用DependencyHandler.localGroovy()方法,來聲明對與 Gradle 一塊兒發佈的 Groovy 的依賴。當你在開發自定義 Gradle 任務或在 Groovy 中的插件時將會頗有用。

示例 50.13. Gradle 的 Groovy 依賴

build.gradle

dependencies {
    compile localGroovy()
}

50.4.7. 排除傳遞依賴

經過配置或者是經過依賴,你能夠排除一個傳遞依賴:

示例 50.14. 排除傳遞依賴

build.gradle

configurations {
    compile.exclude module: 'commons'
    all*.exclude group: 'org.gradle.test.excludes', module: 'reports'
}

dependencies {
    compile("org.gradle.test.excludes:api:1.0") {
        exclude module: 'shared'
    }
}

若是你爲一個特定的配置定義一個 exclude,則解析此配置或任何繼承的配置時,對於全部的依賴,所排除的傳遞依賴將會被過濾掉。若是你想要從你的全部配置中排除傳遞依賴,你能夠用簡明的方式,使用 Groovy 的 spread-dot 運算符來表示,如這個例子所示例。在定義一個 exclude 時,你能夠只指定 organization 或者 module 名稱,或者是二者都指定。能夠看看 Dependency 和 Configuration 的 API 文檔。

不是每一個傳遞依賴均可以被排除 — — 一些傳遞依賴多是應用程序能正確運行的必要條件。通常來講,能夠被排除的傳遞依賴,在運行時並不須要,或者是保證在目標環境或平臺上可用。

你應排除每一個依賴或每一個配置嗎?事實證實,在大多數狀況下你想要排除每個配置。下面是爲何可能想要排除傳遞依賴的一些緣由。記住,對於其中一些用例,有着比排除更好的解決方案!

  • 因爲許可證的緣由而讓依賴不受歡迎。
  • 在任何的遠程倉庫中這個依賴都不可用。
  • 在運行時不須要這個依賴。
  • 這個依賴有一個版本與所須要的版本衝突。使用這個案例,請參考第 50.2.3 節,「解決版本衝突」和關於ResolutionStrategy文檔,以瞭解這個問題潛在的更好的解決方案。

基本上,在大多數狀況下對每個配置都是排除傳遞依賴。這種依賴聲明的方式更加明確。它也更準確,由於每一個依賴排除規則並不能保證給定的傳遞依賴不會顯示在配置中。例如,某些其餘的依賴,並無任何排除規則,可能會帶上那個多餘的傳遞依賴。

其餘相關依賴關係排除的示例,能夠參考 ModuleDependencyDependencyHandler

50.4.8. 可選屬性

一個依賴的全部屬性都是可選的,除了 name。這取決於倉庫類型,這些信息實際上須要用於在倉庫中找到這個依賴。請參閱 50.6 節,「倉庫」。例如,若是你使用 Maven 倉庫,你須要定義group,name 和 version。若是你使用文件系統倉庫,你可能只須要 name 或 name 和 version。

示例 50.15. 依賴的可選屬性

build.gradle

dependencies {
    runtime ":junit:4.10", ":testng"
    runtime name: 'testng' 
}

你也能夠向一個配置指定依賴 notations 的集合或數組:

示例 50.16. 依賴的集合和數組

build.gradle

List groovy = ["org.codehaus.groovy:groovy-all:2.2.0@jar",
               "commons-cli:commons-cli:1.0@jar",
               "org.apache.ant:ant:1.9.3@jar"]
List hibernate = ['org.hibernate:hibernate:3.0.5@jar', 'somegroup:someorg:1.0@jar']
dependencies {
    runtime groovy, hibernate
}

50.4.9. 依賴配置

在 Gradle ,一個依賴能夠有不一樣的配置 (就像你的項目能夠有不一樣的配置)。若是你不顯式指定任何東西,Gradle 會使用依賴的默認配置。對於 Maven 存儲庫的依賴,至少默認配置是惟一可用的一個。若是你使用 Ivy 存儲庫,而且想要爲你的依賴定義一個非默認配置,就要使用 map 標記法而且聲明:

示例 50.17. 依賴配置

build.gradle

dependencies {
    runtime group: 'org.somegroup', name: 'somedependency', version: '1.0', configuration: 'someConfiguration'
}

一樣的項目依賴,你須要聲明:

示例 50.18. 項目的依賴配置

build.gradle

dependencies {
    compile project(path: ':api', configuration: 'spi')
}

50.4.10. 依賴報告

你能夠從命令行生成依賴報告 (參閱 第11.6.4節,「列出項目依賴」)。經過使用 Project report 插件(參閱 第 41 章, Project Report 插件),在構建中能夠建立一個這樣的報告。

從 Gradle 1.2 起,有一個新的編程 API 用於訪問解析的依賴信息。依賴報告(見前面一段)正是使用此 API。這個 API 可讓你查看解析的依賴圖,並提供有關依賴的信息。在將來的版本,這個API 將提供更多有關解析結果的詳細信息。關於這個 API 的更多信息,請參考ResolvableDependencies.getResolutionResult()上的文檔。ResolutionResult API的可能用法:

  • 建立高級的依賴報告,以適應你的用例。
  • 啓用使構建邏輯基於依賴樹的內容來決定。

50.5. 使用依賴

下面的示例咱們使用如下依賴設置:

示例 50.19. Configuration.copy

build.gradle

configurations {
    sealife
    alllife
}

dependencies {
    sealife "sea.mammals:orca:1.0", "sea.fish:shark:1.0", "sea.fish:tuna:1.0"
    alllife configurations.sealife
    alllife "air.birds:albatros:1.0"
}

這些依賴有如下的傳遞依賴:

shark-1.0 -> seal-2.0, tuna-1.0

orca-1.0 -> seal-1.0

tuna-1.0 -> herring-1.0

你可使用配置來訪問它們的聲明依賴或其中一個子集:

示例 50.20. 訪問聲明依賴

build.gradle

task dependencies << {
    configurations.alllife.dependencies.each { dep -> println dep.name }
    println()
    configurations.alllife.allDependencies.each { dep -> println dep.name }
    println()
    configurations.alllife.allDependencies.findAll { dep -> dep.name != 'orca' }.each { dep -> println dep.name }
}

gradle -q dependencies的輸出結果

> gradle -q dependencies
albatros

albatros
orca
shark
tuna

albatros
shark
tuna

dependencies返回只明確屬於配置的依賴。allDependencies包括了擴展配置的依賴。

要得到配置依賴的library文件,你能夠這樣:

示例 50.21. Configuration.files

build.gradle

task allFiles << {
    configurations.sealife.files.each { file ->
        println file.name
    }
}

gradle -q allFiles的輸出結果

> gradle -q allFiles
orca-1.0.jar
shark-1.0.jar
tuna-1.0.jar
herring-1.0.jar
seal-2.0.jar

有時你想要配置依賴的某個子集(例如單個依賴)的 library 文件。

示例 50.22. 指定的 Configuration.files

build.gradle

task files << {
    configurations.sealife.files { dep -> dep.name == 'orca' }.each { file ->
        println file.name
    }
}

gradle -q files的輸出結果

> gradle -q files
orca-1.0.jar
seal-2.0.jar

Configuration.files方法老是獲取整個配置的全部 artifacts。而後,由指定的依賴篩選獲取的文件。正如你在示例中所看到的,傳遞依賴都被包括在內。

你還能夠複製配置。你能夠選擇指定只複製原始配置裏的一個子集的依賴。複製的方法有兩種。copy方法只複製明確屬於配置的依賴。copyRecursive方法將複製全部依賴,包括擴展配置的依賴。

示例 50.23. Configuration.copy

build.gradle

task copy << {
    configurations.alllife.copyRecursive { dep -> dep.name != 'orca' }.allDependencies.each { dep ->
        println dep.name
    }
    println()
    configurations.alllife.copy().allDependencies.each { dep ->
        println dep.name
    }
}

gradle -q copy 的輸出結果

> gradle -q copy
albatros
shark
tuna

albatros

要重點注意,複製的配置所返回的文件,每每並不老是與原始配置的依賴子集所返回的文件同樣。在子集的依賴和不屬於子集的依賴之間,存在版本衝突的狀況下,解析的結果可能會有所不一樣。

示例 50.24. Configuration.copy 與 Configuration.files

build.gradle

task copyVsFiles << {
    configurations.sealife.copyRecursive { dep -> dep.name == 'orca' }.each { file ->
        println file.name
    }
    println()
    configurations.sealife.files { dep -> dep.name == 'orca' }.each { file ->
        println file.name
    }
}

gradle -q copyVsFiles的輸出結果

> gradle -q copyVsFiles
orca-1.0.jar
seal-1.0.jar

orca-1.0.jar
seal-2.0.jar

在上面的例子中 orca 依賴 seal-1.0 而 shark 依賴於 seal-2.0。原始配置所以有版本衝突,因此被解析爲更新的 seal-2.0 版本。files 方法所以返回 seal-2.0 做爲 orca的傳遞依賴。複製的配置只有 orca 一個依賴,所以沒有版本衝突,seal-1.0 做爲傳遞依賴被返回。

一旦一個配置被解析,那它就是不可變的了。若是去修改它的狀態,或者是它的某個依賴的狀態,將會引起一個異常。你能夠永遠複製一個解析了的配置。這個複製的配置處於未解析的狀態,而且能夠被刷新解析。

想了解更多關於這個配置類的API,能夠參閱它的 API 文檔:Configuration.

50.6. 倉庫

Gradle 倉庫管理,基於 Apache Ivy,爲你提供了有關倉庫佈局和獲取策略的許多自由。另外,Gradle 提供了各類方便的方法來添加預配置的倉庫。

您能夠配置任意數量的倉庫,每個都會被Gradle 獨立處理。若是 Gradle 在特定倉庫中查找模塊描述符,它將嘗試從同一倉庫中下載該模塊的全部 artifacts。雖然模塊的元數據和模塊 artifacts 必須位於同一倉庫,但也有可能單個倉庫有多個 Url,給多個位置去搜索元數據文件和 jar 文件。

有幾種不一樣類型的存儲庫能夠聲明:

表 50.2. 倉庫類型

類型 描述
Maven 中央倉庫 一個會在 Maven 中央倉中查找依賴的預配置倉庫。
Maven JCenter 倉庫 一個會在 Bintray 的 Jcenter 查找依賴的預配置倉庫。
Maven 本地倉庫 一個會在本地 Maven 倉庫中查找依賴的預配置倉庫。
Maven 倉庫 一個 Maven 倉庫。能夠位於本地文件系統上,或在某個遠程的位置。
Ivy 倉庫 一個 Ivy 倉庫能夠位於本地文件系統上,或在某個遠程的位置。
Flat 目錄倉庫 一個在本地文件系統上的簡單的倉庫。不支持任何元數據格式。

50.6.1. Maven 中央倉庫

若要添加中央 Maven 2 倉庫(http://repo1.maven.org/maven2),只需添加下面的代碼到你的構建腳本中:

示例 50.25. 添加 Maven 中央倉庫

build.gradle

repositories {
    mavenCentral()
}

如今 Gradle 將會在此倉庫中查找你的依賴。

50.6.2. Maven JCenter 倉庫

BintrayJCenter 是全部流行的 Maven OSS artifacts 的up-to-date 集合,包括直接發佈到 Bintray 的 artifacts。

若要添加 JCenter Maven 倉庫(http://jcenter.bintray.com),只需添加下面的內容到你的構建腳本中:

示例 50.26. 添加 Bintray Jcenter Maven 倉庫

build.gradle

repositories {
    jcenter()
}

如今 Gradle 將會在 JCenter 倉庫中查找你的依賴。

50.6.3. 本地 Maven 倉庫

你能夠這樣把本地的 Maven 緩存做爲倉庫使用:

示例 50.27. 添加本地 Maven 緩存做爲倉庫:

build.gradle

repositories {
    mavenLocal()
}

Gradle 使用與 Maven 相同的邏輯來標識你本地的 Maven 緩存的位置。若是在settings.xml中定義一個本地倉庫的位置,那麼這個位置將會被使用。在USER_HOME/.m2 的settings.xml 比在 M2_HOME/confsettings.xml優先。若是沒有settings.xml可用,Gradle 將使用默認的位置USER_HOME/.m2/repository

50.6.4. Maven 倉庫

要添加一個自定義的 Maven 倉庫,你能夠以下操做:

示例 50.28. 添加一個自定義的 Maven 倉庫

build.gradle

repositories {
    maven {
        url "http://repo.mycompany.com/maven2"
    }
}

有時候,一個倉庫會出現 POM 文件發佈在一個地方,而 JAR 文件和其餘構件發佈在另外一個地方。要定義一個這樣的倉庫,你能夠這樣:

示例 50.29. 爲 JAR 文件添加額外的 Maven 倉庫

build.gradle

repositories {
    maven {
        // Look for POMs and artifacts, such as JARs, here
        url "http://repo2.mycompany.com/maven2"
        // Look for artifacts here if not found at the above location
        artifactUrls "http://repo.mycompany.com/jars"
        artifactUrls "http://repo.mycompany.com/jars2"
    }
}

Gradle 將會在第一個URL中查找 POM 和 JAR 文件。若是那裏找不到 JAR,就會用 artifact URLs 來查找 JAR 文件。

50.6.4.1. 訪問密碼保護的 Maven 倉庫

要訪問一個使用基本的身份驗證的 Maven 倉庫,當你在定義該倉庫時要指定使用的用戶名和密碼:

示例 50.30. 訪問密碼保護的 Maven 倉庫

build.gradle

repositories {
    maven {
        credentials {
            username 'user'
            password 'password'
        }
        url "http://repo.mycompany.com/maven2"
    }
}

最好是把你的用戶名和密碼寫在gradle.properties中,而不是直接寫在構建文件中。

50.6.5. 平面的目錄倉庫

若是你想要把一個(平面)文件系統目錄做爲倉庫使用,只需輸入:

示例 50.31. 平面倉庫的解決

build.gradle

repositories {
    flatDir {
        dirs 'lib'
    }
    flatDir {
        dirs 'lib1', 'lib2'
    }
}

這將會添加一些用於查找依賴的倉庫,它會在一個或多個目錄中尋找。若是你只使用平面目錄解析器,那麼你不須要去設置一個依賴的全部屬性。請參閱 第50.4.8 節,「可選屬性」

50.6.6. Ivy 存儲庫

使用一個標準佈局的 Ivy 存儲庫:

示例 50.32. Ivy存儲庫

build.gradle

repositories {
    ivy {
        url "http://repo.mycompany.com/repo"
        layout "maven"
    }
}

詳細信息請參閱IvyArtifactRepository 

50.6.6.1. 爲一個 Ivy 存儲庫定義自定義的模式

若要定義非標準佈局的 Ivy 倉庫,你能夠定義一個倉庫模式佈局:

示例 50.33. Ivy 倉庫模式佈局

build.gradle

repositories {
    ivy {
        url "http://repo.mycompany.com/repo"
        layout "pattern", {
            artifact "[module]/[revision]/[type]/[artifact].[ext]"
        }
    }
}

50.6.6.2. 使用 Maven 兼容佈局的 Ivy 倉庫

做爲可選的功能,一個使用模式佈局的倉庫能夠有它本身的以Maven 風格奠基的「組織」部分,該部分使用斜槓替換點做爲分隔符。例如,組織my.company將表示爲my/company

示例50.34. 使用 Maven 兼容佈局的 Ivy 倉庫

build.gradle

repositories {
    ivy {
        url "http://repo.mycompany.com/repo"
        layout "pattern", {
            artifact "[organisation]/[module]/[revision]/[artifact]-[revision].[ext]"
            m2compatible = true
        }
    }
}

50.6.6.3. 爲一個 Ivy 倉庫定義不一樣的 artifact 和 Ivy 文件位置

若要定義一個從不一樣的位置獲取 Ivy 文件和 artifacts 的 Ivy 倉庫,您可使用模式佈局,每一個單獨的模式用於定位到 Ivy 文件和 artifacts:

示例 50.35. 自定義模式的 Ivy 倉庫

build.gradle

repositories {
    ivy {
        url "http://repo.mycompany.com/repo"
        layout "pattern", {
            artifact "3rd-party-artifacts/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]"
            artifact "company-artifacts/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]"
            ivy "ivy-files/[organisation]/[module]/[revision]/ivy.xml"
        }
    }
}

每一個artifactivy都指定了一個倉庫,添加一個額外的的模式來使用。這些模式以定義它們的順序來使用。

50.6.6.4. 訪問密碼保護的 Ivy 倉庫

要訪問一個使用基本的身份驗證的 Ivy 倉庫,當你在定義該倉庫時要指定使用的用戶名和密碼:

示例 50.36. Ivy存儲庫

build.gradle

repositories {
    ivy {
        url 'http://repo.mycompany.com'
        credentials {
            username 'user'
            password 'password'
        }
    }
}

50.6.7. 使用倉庫

若要訪問一個倉庫:

示例 50.37. 訪問一個倉庫:

build.gradle

println repositories.localRepository.name
    println repositories['localRepository'].name

要配置一個倉庫:

示例 50.38. 倉庫配置

build.gradle

repositories {
    flatDir {
        name 'localRepository'
    }
}
repositories {
    localRepository {
        dirs 'lib'
    }
}
repositories.localRepository {
    dirs 'lib'
}

50.6.8. 更多關於 Ivy 解析器的信息

Gradle因爲 Ivy 在它的 hood 之下,對倉庫很是靈活。

  • 對於與倉庫通訊的協議,有不少的選項(好比文件系統,http, ssh……)

  • 每一個倉庫均可以有其本身的佈局。

比方說,你能夠聲明一個junit:junit:3.8.2庫的依賴。如今 Gradle 是如何發現它在存儲庫中的?某種程度上依賴信息必須映射到一個路徑上。相比於固定路徑的 Maven,使用 Gradle 你能夠定義一個模式,該模式定義了路徑的樣子。這裏有一些例子:[16]

// Maven2 layout (if a repository is marked as Maven2 compatible, the organization (group) is split into subfolders according to the dots.)
someroot/[organisation]/[module]/[revision]/[module]-[revision].[ext]

// Typical layout for an Ivy repository (the organization is not split into subfolder)
someroot/[organisation]/[module]/[revision]/[type]s/[artifact].[ext]

// Simple layout (the organization is not used, no nested folders.)
someroot/[artifact]-[revision].[ext]

要添加任何一種倉庫 (你能夠很簡單地編寫你本身的) ,你能夠:

示例 50.39. 自定義倉庫的定義

build.gradle

repositories {
    ivy {
        ivyPattern "$projectDir/repo/[organisation]/[module]-ivy-[revision].xml"
        artifactPattern "$projectDir/repo/[organisation]/[module]-[revision](-[classifier]).[ext]"
    }
}

其中由 Ivy (也所以由 Gradle )提供解析器的,它的概述能夠在這裏找到。經過Gradle,你只是不用經過XML來配置它們,而是經過它們的API。

50.7. 依賴解析的工做原理

Gradle 將獲取你的依賴聲明和倉庫定義,並經過一個稱爲依賴項解析的過程嘗試下載全部依賴項。下面是這個過程的工做原理的簡要概述。

  • 給出所需的依賴項,Gradle 首先嚐試爲該依賴解析模塊。每一個倉庫按順序進行檢查,首先查找指示該模塊存在的模塊描述符文件(POM 或 Ivy 文件)。若是沒有找到模塊描述符,Gradle 將搜索表示模塊存在於存儲庫中的主要模塊 artifact文件。

    • 若是依賴被聲明爲一個動態版本(像1.+),Gradle 將會把它解析到在倉庫中的最新的可用的靜態版本(如1.2)。對於 Maven 倉庫,是經過maven metadata.xml文件來實現,而對於 Ivy 存儲庫,則是經過目錄列表。

    • 若是模塊描述符是一個具備的父 POM 文件聲明的 POM 文件,Gradle 將以遞歸方式嘗試爲該 POM 文件解析每一個父模塊。

  • 一旦已爲該模塊檢查了每一個倉庫,Gradle 將會選擇使用「最好」的一個。它使用如下標準來完成:

    • 對於動態版本,「高」的靜態版本優於「低」的版本。
    • 由模塊描述符文件(Ivy或 POM 文件)聲明的文件優於只有一個artifact 文件的模塊。
    • 前面的倉庫的模塊優於後面的倉庫的模塊。

    當依賴由一個靜態版原本聲明,而且在倉庫中找到模塊描述符文件時,將不會再繼續搜索後面的倉庫,這個過程的其他部分是短路的。

  • 而後這個模塊的全部artifact將從上面的過程所選擇的 同一個倉庫 中請求。

50.8. 微調依賴解析過程

在大多數狀況下,Gradle 的默認依賴管理將會在你的構建中按你所想的解析依賴關係。然而,在某些狀況下,會有必要調整依賴解析,以確保你的構建能獲得正確的依賴關係。

有許多種方式均可以影響到Gradle解析依賴。

50.8.1. 強制一個特定模塊的版本

強制一個模塊的版本,是告訴 Gradle 對於指定的依賴(無論是否傳遞依賴),始終使用一個特定的版本,而覆蓋在發佈的模塊描述符中所指定的任何版本。當解決版本衝突時,這可能很是有用——有關詳細信息請參閱第 50.2.3 節,「解決版本衝突」

強制版本也能夠用於處理傳遞依賴所帶來的流氓元數據。若是傳遞依賴有質量較差的元數據,致使了依賴解析時的問題時,你能夠強制 Gradle 對於這一依賴使用一個較新的,固定的版本。有關示例,請參見ResolutionStrategy。請注意,「依賴解析規則」(見下文),提供了一種更強大的機制,來取代一個損壞的模塊依賴。請參閱50.8.2.3 節,「黑名單替換指定版本」

50.8.2. 使用依賴解析規則

一個依賴解析規則爲每個解析依賴執行,並提供功能強大的 api 用於在解析依賴以前處理這個依賴的請求。此功能還在孵化中,但目前提供了對於一個請求的依賴更改組、 名稱及版本的功能,容許在解析過程當中,把一個依賴替換爲另外一個徹底不一樣的模塊。

依賴解析規則提供了一種很是強大的方式來控制依賴解析過程,並能夠用於實如今依賴管理中各類高級模式的排序。下面將概述其中的某些模式。更多的信息和代碼示例請參閱ResolutionStrategy

50.8.2.1. 模塊化可發佈的單位

一般一個組織會使用一個版本發佈一組庫;這些庫將在一塊兒構建,測試以及發佈。這些庫造成一個「可發佈的單位」,被設計並打算做爲一個總體使用。而一塊兒使用來自不一樣的可發佈單位的庫,也不會有意義。

但傳遞依賴解析會很容易破壞這種協議。舉個例子:

  • module-a 依賴於 releasable-unit:part-one:1.0
  • module-a 依賴於 releasable-unit:part-one:1.0

一個依賴於module-amodule-b的構建,將在可發佈單位內得到這個庫的不一樣版本。

依賴解析規則使你可以在構建中強制指定可發佈的單位。想象一下,一個可發佈的單位,由有「org.gradle」 組的全部庫定義。咱們能夠強制全部這些庫使用一致的版本:

示例 50.40. 強制一個組的全部庫使用一致的版本

build.gradle

configurations.all {
    resolutionStrategy.eachDependency { DependencyResolveDetails details ->
        if (details.requested.group == 'org.gradle') {
            details.useVersion '1.4'
        }
    }
}

50.8.2.2. 實現一個自定義的版本方案

在一些企業的環境中,能夠聲明在 gradle 構建中的模塊版本列表,是在外部維護和審覈的。依賴解析規則提供了這種模式的整潔的實現:

  • 在構建腳本中,開發人員使用模塊組與名稱聲明依賴,但使用佔位符版本,例如:「default」。
  • 這個「default」版本經過一個依賴解析規則被解析爲一個特定的版本,這個規則將在一個核準模塊的公司中查找該版本。

該規則實現能夠整齊地封裝在一個公司的插件中,並在組織內和全部構建共享。

示例 50.41. 使用自定義的版本方案

build.gradle

configurations.all {
    resolutionStrategy.eachDependency { DependencyResolveDetails details ->
        if (details.requested.version == 'default') {
            def version = findDefaultVersionInCatalog(details.requested.group, details.requested.name)
            details.useVersion version
        }
    }
}

def findDefaultVersionInCatalog(String group, String name) {
    //some custom logic that resolves the default version into a specific version
    "1.0"
}

50.8.2.3. 黑名單替換特定版本

依賴解析規則提供了一個機制,用於把一個依賴的指定版本列入黑名單,並提供一個替代的版本。若是某個依賴的版本壞了,而且不該該被使用,這個機制將很是有用。一個依賴解析規則將會使這個版本被替換爲一個已知的好的版本。一個壞的模塊的例子是,在一個庫上聲明的一個依賴沒法在任何公共倉庫中找到,但爲何不能使用一個特定的模塊版本,而更但願要另外一個版本,還有不少其餘緣由。

在下面的示例中,想象版本1.2.1包含了重要的修復程序,並應始終優先於1.2使用。提供的規則將強制執行:在任什麼時候間遇到了1.2版本,都將會替換爲1.2.1。注意,這與上面描述的強制使用一個版本不一樣,這個模塊的其餘版本將不受影響。這意味着,若是這個版本也因依賴傳遞被獲取到,「使用最新」的衝突解決策略仍然會選擇 1.3 版本。

示例 50.42. 黑名單替換特定版本

build.gradle

configurations.all {
    resolutionStrategy.eachDependency { DependencyResolveDetails details ->
        if (details.requested.group == 'org.software' && details.requested.name == 'some-library' && details.requested.version == '1.2') {
            //prefer different version which contains some necessary fixes
            details.useVersion '1.2.1'
        }
    }
}

50.8.2.4. 使用一個兼容模塊替換一個依賴模塊

有時一個徹底不一樣的模塊能夠做爲請求的模塊依賴的替代者。示例包括,使用「groovy」來代替「groovy-all」,或者使用「log4j-over-slf4j」來代替「log4j」。從 Gradle 1.5 開始,你可使用依賴解析規則來進行這些替換:

示例 50.43. 在解析中更改依賴組及名稱

build.gradle

configurations.all {
    resolutionStrategy.eachDependency { DependencyResolveDetails details ->
        if (details.requested.name == 'groovy-all') {
            //prefer 'groovy' over 'groovy-all':
            details.useTarget group: details.requested.group, name: 'groovy', version: details.requested.version
        }
        if (details.requested.name == 'log4j') {
            //prefer 'log4j-over-slf4j' over 'log4j', with fixed version:
            details.useTarget "org.slf4j:log4j-over-slf4j:1.7.5"
        }
    }
}

50.8.3. 啓用 Ivy 動態解析模式

Gradle 的Ivy 倉庫實現支持至關於 Ivy 的動態解析的模式。一般狀況下,Gradle 將rev屬性用於在ivy.xml文件中包含的每一個依賴定義。在動態解析模式中,Gradle 將優先使用revConstraint屬性來代替rev屬性,用於一個給定的依賴定義。若是不存在revConstraint屬性,則使用rev屬性。

若要啓用動態解析模式,你須要在倉庫定義進行合適的設置。下面展現了幾個例子。注意,動態解析模式只對 Gradle 的 Ivy 倉庫有效。它不能用於 Maven 倉庫,或自定義的 Ivy DependencyResolver實現。

示例 50.44. 啓用動態解析模式

build.gradle

// Can enable dynamic resolve mode when you define the repository
repositories {
    ivy {
        url "http://repo.mycompany.com/repo"
        resolve.dynamicMode = true
    }
}

// Can use a rule instead to enable (or disable) dynamic resolve mode for all repositories
repositories.withType(IvyArtifactRepository) {
    resolve.dynamicMode = true
}

50.8.4. 組件元數據規則

每一個模塊(也稱爲組件)都有相關的元數據上,像它的組,名稱,版本,依賴,等等。這個元數據一般來源於模塊的描述符。元數據規則容許模塊的元數據在構建腳本中被操縱。它們在模塊描述符下載以後,被全部候選版本之間選擇以前生效。這使得元數據成爲自定義依賴解析的另外一種手段。

其中一個 Gradle 能理解的模塊元數據是模塊的狀態模式。這一律念,也能夠從 Ivy 中,隨着時間推移,一個模塊發展的成熟水平能夠得知。默認狀態模式,按狀態的成熟程度排序,分別是integrationmilestonerelease。除了狀態模式之外,模塊也有一個(當前) 的狀態,這個狀態必須是它的狀態模式中的值之一。若是沒有在(Ivy)描述符中指定,Ivy 模塊和 Maven snapshot模塊的狀態默認爲integration ,不是snapshot 的 Maven 模塊則默認爲release

一個模塊的狀態以及狀態模式,會在解析 latest 版本選擇器時考慮到。具體而言,latest.someStatus 將會解析成有着someStatus 或更成熟狀態的最高的模塊版本。例如,在適當的位置使用默認的狀態模式,latest.integration將選擇最高的模塊版本,不論其狀態(由於integration是成熟度最低的狀態),而latest.release將選擇release狀態的最高模塊版本。這裏是在代碼中的表現:

示例 50.45,「Latest」版本選擇器

build.gradle

dependencies {
    config1 "sea.fish:tuna:latest.integration"
    config2 "sea.fish:tuna:latest.release"
}

task listFish << {
    configurations.config1.each { println it.name }
    println()
    configurations.config2.each { println it.name}
}

gradle -q listFish的輸出結果

> gradle -q listFish
tuna-1.5.jar

tuna-1.4.jar

下一個示例演示了基於在一個模塊的元數據規則中聲明的自定義狀態模式的latest選擇器:

示例 50.46. 自定義狀態模式

build.gradle

dependencies {
    config3 "air.birds:albatros:latest.silver"
    components {
        eachComponent { ComponentMetadataDetails details ->
            if (details.id.group == "air.birds") {
                details.statusScheme = ["bronze", "silver", "gold", "platinum"]
            }
        }
    }
}

task listBirds << {
    configurations.config3.each { println it.name }
}

gradle -q listBirds的輸出結果

> gradle -q listBirds
albatros-2.0.jar

50.9. 依賴緩存

Gradle 包含了一個高度複雜的依賴緩存機制,該機制力求減小依賴解析中的遠程請求,同時努力保證依賴解析結果的正確及可再生性。

Gradle 依賴緩存包含 2 個主要類型的存儲:

  • 一個基於文件的下載構件存儲,包括像jars這樣的二進制文件,以及像 POM 文件以及 Ivy 文件這樣的原始下載的元數據。下載的構件的存儲路徑包括了 SHA1 校驗和,意味着 2 個具備相同名稱但內容不一樣的構件能夠很容易地被緩存。

  • 解析的模塊元數據的二進制存儲,包括解析動態版本、 模塊描述符和構件的結果。

從下載的構件的存儲中分享出緩存的元數據,將容許咱們使用緩存作一些很是有用的東西,而若是使用一個透明的只有文件的緩存佈局則會很困難。

Gradle 緩存中不容許本地緩存隱藏問題和,以及一直是許多構建工具的挑戰的創造神祕及難以調試的行爲。這一新行爲經過帶寬和存儲空間的有效途徑被實現。在此過程當中,Gradle 實現了可靠和可重複的的企業構建。

50.9.1. Gradle 依賴緩存的主要特色

50.9.1.1. 單獨的元數據緩存

Gradle 以二進制格式在元數據緩存中保留了依賴解析的各方面的記錄。存儲在元數據緩存中的信息包括:

  • 解析一個動態版本(例如1.+)到一個具體的版本(例如1.2)的結果。
  • 對於特定模塊的解析後的模塊元數據,包括模塊構件和模塊依賴。
  • 對於特定構件的解析後的構件元數據,包括指向下載的構件工件的信息。
  • 在特定的倉庫中缺乏的特定模塊或工件,避免反覆地嘗試訪問不存在的資源。

每一個元數據緩存中的條目包括了一條存儲庫中提供的信息,以及可用於緩存到期的時間戳的記錄。

50.9.1.2. 倉庫緩存是獨立的

如上文所述,每一個倉庫是一個單獨的元數據緩存。一個倉庫由它的 URL、類型和佈局來區分。若是一個模塊或構件以以前沒有從這個存儲庫中解析過,Gradle 將嘗試在這個存儲庫中解析這個模塊。這將始終涉及到存儲庫中的遠程查找,然而,在許多狀況下都沒有下載的須要(見第 50.9.1.3 節,「構件重用」下文)、。

若是所需的構件,在構建所指定的任何倉庫中都沒有找到的話,依賴解析將會失敗,不管本地緩存是否從一個其餘的倉庫中取回這個構件。倉庫獨立容許構建之間用一種先進方法的方法彼此隔離,之前沒有構建工具能作到這樣。這是一個關鍵的功能,能在任何環境中建立可靠,重複性好的構建。

50.9.1.3. 構件重用

在下載構件前,Gradle 試圖經過下載與該項目關聯的sha文件以肯定所需的構件的校驗和。若是校驗和能夠獲取到,而且若是已經存在具備相同 id 和校驗和的構件,則不會再去下載這個構件。若是校驗和不能從遠程服務器檢索,構件將被下載(而且忽略它所匹配的現有的構件)。

同時考慮構件從不一樣的倉庫下載,Gradle 還將試圖重用本地 Maven 庫中發現的構件。若是一個候選的構件已經經過 Maven 下載,而且若是它能夠和遠程服務器定義的校驗和匹配,Gradle 將使用這個構件。

50.9.1.4. 基於校驗和的存儲

不一樣的倉庫提供不一樣的二進制構件以響應相同的構件標識符是可能的。這種常見的狀況是 Maven 的快照構件,但任何構件也均可以不改變它的標識符而從新發布。經過基於其 SHA1 校驗和緩存構件,Gradle 是可以保持同一構件的多個版本。這意味着,當對一個存儲庫解析時,Gradle 將永遠不會覆蓋從一個不一樣的倉庫中緩存的構件文件。這不須要讓一個單獨的文件在每個倉庫中存儲就能夠作到。

50.9.1.5. 緩存鎖

Gradle 依賴緩存使用基於文件的鎖來確保,它能夠安全地經過多個 Gradle 進程並行使用。每當二進制元數據存儲區正在讀取或寫入時,鎖都會被持有,但會在慢的操做好比下載遠程工做時釋放。

50.9.2. 重寫緩存的命令行選項

50.9.2.1. 離線

--offline 命令行開機告訴 Gradle 老是從緩存中使用依賴模塊,不管它們是否被再次檢查。在使用離線運行時,Gradle 將不會嘗試訪問網絡來執行依賴解析。若是所需的模塊在依賴緩存中不存在,構建執行將會失敗。

50.9.2.2. 刷新

有時,Gradle 依賴緩存可能與已配置的倉庫的實際狀態不一樣步。也許一個存儲庫的最初配置不正確,或許是一個「無改變」的模塊被不正確地發佈。要刷新依賴緩存中的全部依賴項,請使用--refresh-dependencies命令行選項。

--refresh-dependencies 選項告訴 Gradle 在解析模塊和構件時忽略全部緩存條目。對全部已配置的倉庫執行新的解析,經過從新計算動態版本,模塊刷新,以及下載構件。然而,在再次下載以前Gradle 可能將會檢查前一個下載的構件是否有效。這是經過比較發佈在倉庫中的 SHA1 值和如今已經下載好的工件的 SHA1 值來完成的。

50.9.3. 依賴緩存的微調控制

你能夠在一個配置中經過使用ResolutionStrategy對緩存的某些方面進行微調。

默認狀況下,Gradle 緩存動態版本的時間爲 24 小時。若是要改變Gradle對解析一個動態版本的緩存時間,可使用:

示例 50.47. 動態版本緩存控制

build.gradle

configurations.all {
    resolutionStrategy.cacheDynamicVersionsFor 10, 'minutes'
}

默認狀況下,Gradle 的變化模塊將緩存 24 小時。要修改 Gradle 對變化模塊的元數據和構件的緩存時間,請使用:

示例 50.48. 變化模塊的緩存控制

build.gradle

configurations.all {
    resolutionStrategy.cacheChangingModulesFor 4, 'hours'
}

更多詳細信息請參閱ResolutionStrategy的 API 文檔。

50.10. 傳遞依賴管理的策略

許多項目依賴於Maven 中央倉庫。這不是沒有問題的。

  • Maven 中央存儲庫可能會下線,或者響應時間很長。

  • 許多項目的 POM 文件會有錯誤的信息(好比,commons-httpclient-3.0的POM文件聲明瞭 JUnit 是運行時依賴)。

  • 對於許多項目而言,可能不是隻有正確的一組依賴(因POM格式的影響會多或少)。

若是您的項目依賴於 Maven 中央倉,你極可能須要額外的自定義倉庫,由於:

  • 你可能須要尚未上傳到Maven 中央倉的依賴。

  • 你想要正確地處理 Maven 中央倉 POM 文件中錯誤的元數據。

  • 你不想曝光給想要對你的項目進行構建的人,Maven 中央倉停機或者有時候響應時間太長。

想在設置一個自定義的倉庫並不算什麼。[17]但想讓這個倉庫保持最新的狀態,可能會很乏味。對一個新的版本,你老是要建立新的 XML 描述符和目錄。你的自定義存倉庫是另外一個基礎結構元素,它可能有停機時間而且須要更新。要啓用歷史版本,你須要保留全部過去的庫,而且須要備份。它是一個間接層。你還要查找另一個信息源。儘管這一切真的不是大問題,但累加起來就有影響了。倉庫管理器,好比像 Artifactory 或 Nexus 則會使這些工做變得輕鬆。可是好比開源項目一般沒有主機用於這些產品。這種情況經過一些新的服務也改變了,好比Bintray ,它可讓開發者使用自助服務的倉庫平臺託管和分發他們發佈的二進制文件。Bintray 還支持共享通過他們審覈的構件,經過JCenter公共倉庫,爲全部普通的OSS java 構件提供一個單一的解析地址(見第 50.6.2 節,「Maven JCenter 存儲庫」)。

這也是爲何一些項目更願意把他們的庫存儲於他們的版本控件系統的緣由。這種作法 Gradle 也徹底支持。庫能夠存在一個平面目錄中,而沒有任何 XML 模塊描述符文件。然而 Gradle 能提供完整的傳遞依賴管理。您可使用客戶端模塊依賴,或者是工件依賴來表達依賴關係,後者的狀況中第一級依賴是沒有傳遞依賴的。人們能夠從 svn 檢出這樣一個項目,而且具體必要的一切,來構建它。

若是您是使用像 Git 同樣的分佈式版本控制系統,因爲人們會檢出整個歷史,你可能不想使用版本控制系統來保存這些庫。但即便是這樣, Gradle 的靈活性也可使你的生活更輕鬆。例如你可使用一個共享平面目錄,而不包括如上所述能夠有徹底的傳遞依賴管理的XML 描述符。

你也可使用混合策略。若是你主要關心的是 POM 文件和維護的自定義 XML 描述符中的元數據不正確,客戶端模塊提供了一種替代方案。但你固然也能夠仍然使用 Maven2 倉庫和你自定義的存儲庫,做爲只放 jars和依然使用傳遞依賴管理的倉庫。或者,你能夠只爲元數字不正確的 POMs 提供客戶端模塊。對於這些jar 和不正確的 POMs,你依然要使用遠程倉庫。

50.10.1. 隱式傳遞依賴

還有另一種方法,用於處理沒有XML 描述符文件的傳遞依賴。你可使用 Gradle 來作,但咱們不推薦它。咱們提到它是爲了完整性,以及和其餘構建工具進行比較。

訣竅是隻使用構件依賴,並在列表中對它們進行分組。對於這種方法,你將用某種方式表達,你第一級的依賴項和傳遞依賴是什麼(見第 50.4.8 條,「可選屬性」)。但缺點是,對於 Gradle 依賴管理而言,全部的依賴都被認爲是第一級依賴。依賴報告不會顯示你真正的依賴關係圖,而且compile任務會使用全部的依賴,而不僅是第一級依賴。總之,比起使用客戶端模塊,你的構建再也不那麼容易維護和可靠。而你不會有其餘收穫。



[14Gradle 支持部分多項目構建(參見 第 56 章, 多項目構建)。

[16http://ant.apache.org/ivy/history/latest-milestone/concept.html,你能夠了解到更多關於 ivy 模式的內容。

[17若是你想要從 Maven 中央倉庫停機保護你的項目不變得更加複雜時,你可能須要設置一個倉庫代理。在企業環境中,這是至關廣泛的。而對於一個開放源碼項目而言,看起來則有點小題大作了。

相關文章
相關標籤/搜索