這一章我將介紹Gradle對依賴管理的強大支持,學習依賴分組和定位不一樣類型倉庫。依賴管理看起來很容易,可是當出現依賴解析衝突時就會很棘手,複雜的依賴關係可能致使構建中依賴一個庫的多個版本。Gradle經過分析依賴樹獲得依賴報告,你將很容易找到一個指定的依賴的來源。html
Gradle有本身的依賴管理實現,除了支持ant和Maven的特性外,Gradle關心的是性能、可靠性和複用性。
web
簡要概述依賴管理
幾乎全部基於JVM的項目都會或多或少依賴其餘庫,假設你在開發一個基於web的項目,你極可能會依賴很受歡迎的開源框架好比Spring MVC來提升效率。Java的第三方庫通常以JAR文件的形式存在,通常用庫名加版本號來標識。隨着開發的進行依賴的第三方庫增多小的項目變的愈來愈大,組織和管理你的JAR文件就很關鍵。api
不算完美的依賴管理技術
因爲Java語言並沒提供依賴管理的工具,因此你的團隊須要本身開發一套存儲和檢索依賴的想法。你可能會採起如下幾種常見的方法:緩存
- 手動複製JAR文件到目標機器,這是最原始的很容易出錯的方法。
- 使用一個共享的存儲介質來存儲JAR文件(好比共享的網盤),你能夠加載網絡硬盤或者經過FTP檢索二進制文件。這種方法須要開發者事先創建好與倉庫的鏈接,手動添加新的依賴到倉庫中。
- 把依賴的JAR文件同源代碼都添加到版本控制系統中。這種方法不須要任何額外的步驟,你的同伴在拷貝倉庫的時候就能檢索依賴的改變。另外一方面,這些JAR文件佔用了沒必要要的空間,當你的項目存在相互之間依賴的時候你須要頻繁的check-in的檢查源代碼是否發生了改變。
自動管理依賴的重要性
儘管上面的方法都能用,可是這距離理想的解決方案差遠了,由於他們沒有提供一個標準化的方法來命名和管理JAR文件。至少你得須要開發庫的準確版本和它依賴的庫(傳遞依賴),這個爲何這麼重要?網絡
準確知道依賴的版本架構
若是在項目中你沒有準確聲明依賴的版本這將會是一個噩夢,若是沒有文檔你根本沒法知道這個庫支持哪些特性,是否升級一個庫到新的版本就變成了一個猜謎遊戲由於你不知道你的當前版本。框架
管理傳遞依賴maven
在項目的早期開發階段傳遞依賴就會是一個隱患,這些庫是第一層的依賴須要的,好比一個比較常見的開發方案是將Spring和Hibernate結合起來這會引入超過20個其餘的開發庫,一個庫須要不少其餘庫來正常工做。下圖展現了Hibernate核心庫的依賴圖:工具
若是沒有正確的管理依賴,你能夠會遇到沒想到過的編譯期錯誤和運行期類加載問題。咱們能夠總結到咱們須要一個更好的方式來管理依賴,通常來說你想在項目元數據中聲明你的依賴和它的版本號。做爲一個項目自動化的過程,這個版本的庫會自動從中央倉庫下載、安裝到你的項目中,咱們來看幾個現有的開源解決方案。佈局
使用自動化的依賴管理
在Java領域裏支持聲明的自動依賴管理的有兩個項目:Apache Ivy(Ant項目用的比較多的依賴管理器)和Maven(在構建框架中包含一個依賴管理器),我再也不詳細介紹這兩個的細節而是解釋自動依賴管理的概念和機制。
Ivy和Maven是經過XML描述文件來表達依賴配置,配置包含兩部分:依賴的標識加版本號和中央倉庫的位置(能夠是一個HTTP連接),依賴管理器根據這個信息自動定位到須要下載的倉庫而後下載到你的機器中。庫能夠定義傳遞依賴,依賴管理器足夠聰明分析這個信息而後解析下載傳遞依賴。若是出現了依賴衝突好比上面的Hibernate core的例子,依賴管理器會試着解決。庫一旦被下載就會存儲在本地的緩存中,構建系統先檢查本地緩存中是否存在須要的庫而後再從遠程倉庫中下載。下圖顯示了依賴管理的關鍵元素:
Gradle經過DSL來描述依賴配置,實現了上面描述的架構。
自動依賴管理面臨的挑戰
雖然依賴管理器簡化了手工的操做,但有時也會遇到問題。你會發現你的依賴圖中會依賴同個庫的不一樣版本,使用日誌框架常常會遇到這個問題,依賴管理器基於一個特定的解決方案只選擇其中一個版原本避免版本衝突。若是你想知道某個庫引入了什麼版本的傳遞依賴,Gradle提供了一個很是有用的依賴報告來回答這個問題。下一節我會經過一個例子來說解。
聲明依賴
DSL配置block dependencies用來給配置添加一個或多個依賴,你的項目不只能夠添加外部依賴,下面這張表顯示了Gradle支持的各類不一樣類型的依賴。
這一章直接掃外部模塊依賴和文件依賴,咱們來看看Gradle APi是怎麼表示依賴的。
理解依賴的API表示
每一個Gradle項目都有一個DependencyHandler的實例,你能夠經過getDependencies()方法來獲取依賴處理器的引用,上表中每一種依賴類型在依賴處理器中都有一個相對應的方法。每個依賴都是Dependency的一個實例,group, name, version, 和classifier這幾個屬性用來標識一個依賴,下圖清晰的表示了項目(Project)、依賴處理器(DependencyHandler)和依賴三者之間的關係:
外部模塊依賴
在Gradle的術語裏,外部庫一般是以JAR文件的形式存在,稱之爲外部模塊依賴,表明項目層次外的一個模塊,這種類型的依賴是經過屬性來惟一的標識,接下來咱們來介紹每一個屬性的做用。
依賴屬性
當依賴管理器從倉庫中查找依賴時,須要經過屬性的結合來定位,最少須要提供一個name。
- group: 這個屬性用來標識一個組織、公司或者項目,能夠用點號分隔,Hibernate的group是org.hibernate。
- name: name屬性惟一的描述了這個依賴,hibernate的核心庫名稱是hibernate-core。
- version: 一個庫能夠有不少個版本,一般會包含一個主版本號和次版本號,好比Hibernate核心庫3.6.3-Final。
- classifier: 有時候須要另一個屬性來進一步的說明,好比說明運行時的環境,Hibernate核心庫沒有提供classifier。
依賴的寫法
你可使用下面的語法在項目中聲明依賴:
1 |
dependencies { |
你先聲明你要給哪一個配置添加依賴,
Java插件指定了若干依賴配置項,其描述以下:當項目的源代碼被編譯時,compile配置項中的依賴是必須的。
- runtime配置項中包含的依賴在運行時是必須的。
- testCompile配置項中包含的依賴在編譯項目的測試代碼時是必須的。
- testRuntime配置項中包含的依賴在運行測試代碼時是必須的。
- archives配置項中包含項目生成的文件(如Jar文件)。
- default配置項中包含運行時必須的依賴。
而後添加依賴列表,你能夠用map的形式來註明,你也能夠直接用冒號來分隔屬性,好比這樣的:
1 |
//聲明外部屬性 |
若是你項目中依賴比較多,你把一些共同的依賴屬性定義成外部屬性能夠簡化build腳本。
Gradle沒有給項目選擇默認的倉庫,當你沒有配置倉庫的時候運行deployTOLocalTomcat任務的時候回出現以下的錯誤:
$ gradle deployToLocalTomcat :deployToLocalTomcat FAILED FAILURE: Build failed with an exception. Where: Build file '/Users/benjamin/gradle-in-action/code/chapter5/cargo-configuration/build.gradle' line: 10 What went wrong: Execution failed for task ':deployToLocalTomcat'. > Could not resolve all dependencies for configuration ':cargo'. > Could not find group:org.codehaus.cargo, module:cargo-core-uberjar, version:1.3.1. Required by: :cargo-configuration:unspecified > Could not find group:org.codehaus.cargo, module:cargo-ant,version:1.3.1. Required by: :cargo-configuration:unspecified
到目前爲止還沒講到怎麼配置不一樣類型的倉庫,好比你想使用MavenCentral倉庫,添加下面的配置代碼到你的build腳本中:
1 |
repositories { |
檢查依賴報告
當你運行dependencies任務時,這個依賴樹會打印出來,依賴樹顯示了你build腳本聲明的頂級依賴和它們的傳遞依賴:
仔細觀察你會發現有些傳遞依賴標註了*號,表示這個依賴被忽略了,這是由於其餘頂級依賴中也依賴了這個傳遞的依賴,Gradle會自動分析下載最合適的依賴。
排除傳遞依賴
Gradle容許你徹底控制傳遞依賴,你能夠選擇排除所有的傳遞依賴也能夠排除指定的依賴,假設你不想使用UberJar傳遞的xml-api的版本而想聲明一個不一樣版本,你可使用exclude方法來排除它:
1 |
dependencies { |
exclude屬性值和正常的依賴聲明不太同樣,你只須要聲明group和(或)module,Gradle不容許你只排除指定版本的依賴。
有時候倉庫中找不到項目依賴的傳遞依賴,這會致使構建失敗,Gradle容許你使用transitive屬性來排除全部的傳遞依賴:
1 |
dependencies { |
動態版本聲明
若是你想使用一個依賴的最新版本,你可使用latest.integration,好比聲明 Cargo Ant tasks的最新版本,你能夠這樣寫 org.codehaus .cargo:cargo-ant:latest-integration
,你也能夠用一個+號來動態的聲明:
1 |
dependencies { |
Gradle的dependencies任務能夠清晰的看到選擇了哪一個版本,這裏選擇了1.3.1版本:
$ gradle –q dependencies ------------------------------------------------------------ Root project ------------------------------------------------------------ Listing 5.4 Excluding a single dependency Listing 5.5 Excluding all transitive dependencies Listing 5.6 Declaring a dependency on the latest Cargo 1.x version Exclusions can be declared in a shortcut or map notation. 120 CHAPTER 5 Dependency management cargo - Classpath for Cargo Ant tasks. \--- org.codehaus.cargo:cargo-ant:1.+ -> 1.3.1 \--- ...
###文件依賴
若是你沒有使用自動的依賴管理工具,你可能會把外部庫做爲源代碼的一部分或者保存在本地文件系統中,當你想把項目遷移到Gradle的時候,你不想去重構,Gradle很簡單就能配置文件依賴。下面這段代碼複製從Maven中央倉庫解析的依賴到libs/cargo目錄。
1 |
task copyDependenciesToLocalDir(type: Copy) { |
運行這個任務以後你就能夠在依賴中聲明Cargo庫了,下面這段代碼展現了怎麼給cargo配置添加JAR文件依賴:
1 |
dependencies { |
##配置遠程倉庫
Gradle支持下面三種不一樣類型的倉庫:
下圖是配置不一樣倉庫對應的Gradle API:
下面以Maven倉庫來介紹,Maven倉庫是Java項目中使用最爲普遍的一個倉庫,庫文件通常是以JAR文件的形式存在,用XML(POM文件)來來描述庫的元數據和它的傳遞依賴。全部的庫文件都存儲在倉庫的指定位置,當你在構建腳本中聲明瞭依賴時,這些屬性用來找到庫文件在倉庫中的準確位置。group屬性標識了Maven倉庫中的一個子目錄,下圖展現了Cargo依賴屬性是怎麼對應到倉庫中的文件的:
RepositoryHandler接口提供了兩個方法來定義Maven倉庫,mavenCentral方法添加一個指向倉庫列表的引用,mavenLocal方法引用你文件系統中的本地Maven倉庫。
添加Maven倉庫
要使用Maven倉庫你只須要調用mavenCentral方法,以下所示:
1 |
repositories { |
添加本地倉庫
本地倉庫默認在 <user_home>/.m2/repository目錄下,只須要添加以下腳原本引用它:
1 |
repositories { |
添加自定義Maven倉庫
若是指定的依賴不存在與Maven倉庫或者你想經過創建本身的企業倉庫來確保可靠性,你可使用自定義的倉庫。倉庫管理器容許你使用Maven佈局來配置一個倉庫,這意味着你要遵照artifact的存儲模式。你也能夠添加驗證憑證來提供訪問權限,Gradle的API提供兩種方法配置自定義的倉庫:maven()和mavenRepo()。下面這段代碼添加了一個自定義的倉庫,若是Maven倉庫中不存在相應的庫會從自定義倉庫中查找:
1 |
repositories { |