Gradle入門系列(3):依賴管理

轉載:
本文由 伯樂在線 - JustinWu 翻譯。未經許可,禁止轉載!
英文出處:Petri Kainulainen。歡迎加入翻譯組

在現實生活中,要創造一個沒有任何外部依賴的應用程序并非不可能,但也是極具挑戰的。這也是為什么依賴管理對于每個軟件項目都是至關重要的一部分。

這篇教程主要講述如何使用Gradle管理我們項目的依賴,我們會學習配置應用倉庫以及所需的依賴,我們也會理論聯系實際,實現一個簡單的演示程序。

讓我們開始吧。

倉庫管理簡介

本質上說,倉庫是一種存放依賴的容器,每一個項目都具備一個或多個倉庫。

Gradle支持以下倉庫格式:

我們來看一下,對于每一種倉庫類型,我們在構建中應該如何配置。

在構建中加入Ivy倉庫

我們可以通過URL地址或本地文件系統地址,將Ivy倉庫加入到我們的構建中。

如果想通過URL地址添加一個Ivy倉庫,我們可以將以下代碼片段加入到build.gradle文件中:

repositories {
    ivy {
        url "http://ivy.petrikainulainen.net/repo"
    }
}

如果想通過本地文件系統地址添加一個Ivy倉庫,我們可以將以下代碼片段加入到build.gradle文件中:

repositories {
    ivy {       
        url "../ivy-repo"
    }
}

小貼士:如果你想要獲得更多關于Ivy倉庫配置的信息,你可以參考以下資源:

我們繼續,下面是如何在構建中加入Maven倉庫。

在構建中加入Maven倉庫

與Ivy倉庫很類似,我們可以通過URL地址或本地文件系統地址,將Maven倉庫加入到我們的構建中。

如果想通過URL地址添加一個Maven倉庫,我們可以將以下代碼片段加入到build.gradle文件中:

repositories {
    maven {
        url "http://maven.petrikainulainen.net/repo"
    }
}

如果想通過本地文件系統地址添加一個Maven倉庫,我們可以將以下代碼片段加入到build.gradle文件中:

repositories {
    maven {       
        url "../maven-repo"
    }
}

在加入Maven倉庫時,Gradle提供了三種“別名”供我們使用,它們分別是:

  • mavenCentral()別名,表示依賴是從Central Maven 2 倉庫中獲取的。
  • jcenter()別名,表示依賴是從Bintary’s JCenter Maven 倉庫中獲取的。
  • mavenLocal()別名,表示依賴是從本地的Maven倉庫中獲取的。

如果我們想要將Central Maven 2 倉庫加入到構建中,我們必須在build.gradle文件中加入以下代碼片段:

repositories {
    mavenCentral()
}

小貼士:如果你想要獲取更多關于Maven倉庫配置的信息,你可以參考這篇文章:
section 50.6.4 Maven Repositories of the Gradle User Guide

我們繼續,下面是如何在構建中加入Flat Directory倉庫。

在構建中加入Flat Directory倉庫

如果我們想要使用Flat Directory倉庫,我們需要將以下代碼片段加入到build.gradle文件中:

repositories {
    flatDir {
        dirs 'lib'
    }
}

這意味著系統將在lib目錄下搜索依賴,同樣的,如果你愿意的話可以加入多個目錄,代碼片段如下:

repositories {
    flatDir {
        dirs 'libA', 'libB'
    }
}

小貼士:如果你想要獲得更多關于Flat Directory倉庫配置的信息,你可以參考以下資源:

我們繼續,下面要講的是,如何使用Gradle管理項目中的依賴。

依賴管理簡介

在配置完項目倉庫后,我們可以聲明其中的依賴,如果我們想要聲明一個新的依賴,可以采用如下步驟:

  1. 指定依賴的配置。
  2. 聲明所需的依賴。

讓我們看一下詳細步驟:

配置中的依賴分類

在Gradle中,依賴是按照指定名稱進行分類的,這些分類被稱為配置項,我們可以使用配置項聲明項目的外部依賴。

Java插件指定了若干依賴配置項,其描述如下:

  • 當項目的源代碼被編譯時,compile配置項中的依賴是必須的。
  • runtime配置項中包含的依賴在運行時是必須的。
  • testCompile配置項中包含的依賴在編譯項目的測試代碼時是必須的。
  • testRuntime配置項中包含的依賴在運行測試代碼時是必須的。
  • archives配置項中包含項目生成的文件(如Jar文件)。
  • default配置項中包含運行時必須的依賴。

我們繼續,下面是如何在項目中聲明依賴。

聲明項目依賴

最普遍的依賴稱為外部依賴,這些依賴存放在外部倉庫中。一個外部依賴可以由以下屬性指定:

  • group屬性指定依賴的分組(在Maven中,就是groupId)。
  • name屬性指定依賴的名稱(在Maven中,就是artifactId)。
  • version屬性指定外部依賴的版本(在Maven中,就是version)。

小貼士:這些屬性在Maven倉庫中是必須的,如果你使用其他倉庫,一些屬性可能是可選的。打個比方,如果你使用Flat directory倉庫,你可能只需要指定名稱和版本

我們假設我們需要指定以下依賴:

  • 依賴的分組是foo。
  • 依賴的名稱是foo。
  • 依賴的版本是0.1。
  • 在項目編譯時需要這些依賴。

我們可以將以下代碼片段加入到build.gradle中,進行依賴聲明:

dependencies {
    compile group: 'foo', name: 'foo', version: '0.1'
}

我們也可以采用一種快捷方式聲明依賴:[group]:[name]:[version]。如果我們想用這種方式,我們可以將以下代碼段加入到build.gradle中:

dependencies {
    compile 'foo:foo:0.1'
}

我們也可以在同一個配置項中加入多個依賴,傳統的方式如下:

dependencies {
    compile (
        [group: 'foo', name: 'foo', version: '0.1'],
        [group: 'bar', name: 'bar', version: '0.1']
    )
}

如果采用快捷方式,那可以是這樣:

dependencies {
    compile 'foo:foo:0.1', 'bar:bar:0.1'
}

自然地,聲明屬于不同配置項的依賴也是可以的。比如說,如果我們想要聲明屬于compile和testCompile配置項的依賴,可以這么做:

dependencies {
    compile group: 'foo', name: 'foo', version: '0.1'
    testCompile group: 'test', name: 'test', version: '0.1'
}

同樣的,給力的快捷方式又來了( ̄︶ ̄)

dependencies {
    compile 'foo:foo:0.1'
    testCompile 'test:test:0.1'
}

小貼士:你可以從這篇文章中獲得更多關于依賴聲明的信息。

我們已經學習了依賴管理的基礎知識,下面我們來實現一個演示程序。

創建演示程序

演示程序的需求是這樣的:

  • 演示程序的構建腳本必須使用Maven central倉庫。
  • 演示程序必須使用Log4j寫入日志。
  • 演示程序必須包含包含單元測試,保證正確的信息返回,單元測試必須使用JUnit編寫。
  • 演示程序必須創建一個可執行的Jar文件。

我們來看一下怎樣實現這些需求。

配置倉庫

我們的演示程序的一個需求是構建腳本必須使用Maven central倉庫,在我們使用Maven central倉庫配置構建腳本后,源代碼如下:

apply plugin: 'java'
 
repositories {
    mavenCentral()
}
 
jar {
    manifest {
        attributes 'Main-Class': 'net.petrikainulainen.gradle.HelloWorld'
    }
}

我們再來看一下如何對我們的演示程序進行依賴聲明。

依賴聲明

在build.gradle文件中,我們聲明了兩個依賴:

在我們聲明了這些依賴后,build.gradle文件是這樣的:

apply plugin: 'java'
 
repositories {
    mavenCentral()
}
 
dependencies {
    compile 'log4j:log4j:1.2.17'
    testCompile 'junit:junit:4.11'
}
 
jar {
    manifest {
        attributes 'Main-Class': 'net.petrikainulainen.gradle.HelloWorld'
    }
}

我們繼續,稍微加入一些代碼。

編寫代碼

為了實現我們演示程序的需求,“我們不得不過度工程化一下”,我們會按照下列步驟創建程序:

  1. 創建一個MessageService類,當其中的getMessage()方法被調用時,返回字符串“Hello World!”。
  2. 創建一個MessageServiceTest類,確保MessageService類中的getMessage()方法返回字符串“Hello World!”。
  3. 創建程序的主類,從MessageService對象獲取信息,并使用Log4j寫入日志。
  4. 配置Log4j。

我們按部就班的操作一下。

首先,在src/main/java/net/petrikainulainen/gradle目錄下新建一個MessageService類并加以實現,代碼如下:

package net.petrikainulainen.gradle;
 
public class MessageService {
 
    public String getMessage() {
        return "Hello World!";
    }
}

其次,在src/main/test/net/petrikainulainen/gradle目錄下新建一個MessageServiceTest類,編寫單元測試,代碼如下:

package net.petrikainulainen.gradle;
 
import org.junit.Before;
import org.junit.Test;
 
import static org.junit.Assert.assertEquals;
 
public class MessageServiceTest {
 
    private MessageService messageService;
 
    @Before
    public void setUp() {
        messageService = new MessageService();
    }
 
    @Test
    public void getMessage_ShouldReturnMessage() {
        assertEquals("Hello World!", messageService.getMessage());
    }
}

第三,在src/main/java/net/petrikainulainen/gradle目錄下新建一個HelloWorld類,這是程序的主類,從MessageService對象獲取信息,并使用Log4j寫入日志,代碼如下:

package net.petrikainulainen.gradle;
 
import org.apache.log4j.Logger;
 
public class HelloWorld {
 
    private static final Logger LOGGER = Logger.getLogger(HelloWorld.class);
 
    public static void main(String[] args) {
        MessageService messageService = new MessageService();
 
        String message = messageService.getMessage();
        LOGGER.info("Received message: " + message);
    }
}

第四,在src/main/resources目錄中,使用log4j.properties配置log4j,log4j.properties文件如下:

log4j.appender.Stdout=org.apache.log4j.ConsoleAppender
log4j.appender.Stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.Stdout.layout.conversionPattern=%-5p - %-26.26c{1} - %m\n
 
log4j.rootLogger=DEBUG,Stdout

這樣就好了,我們看看如何執行測試。

執行測試

我們可以通過以下命令執行測試。

gradle test

當測試通過時,我們能看到如下輸出:

> gradle test
:compileJava
:processResources
:classes
:compileTestJava
:processTestResources 
:testClasses
:test
 
BUILD SUCCESSFUL
 
Total time: 4.678 secs

然而,如果測試失敗,我們將看到如下輸出:

> gradle test
:compileJava
:processResources
:classes
:compileTestJava
:processTestResources
:testClasses
:test
 
net.petrikainulainen.gradle.MessageServiceTest > getMessage_ShouldReturnMessageFAILED
    org.junit.ComparisonFailure at MessageServiceTest.java:22
 
1 test completed, 1 failed
:test FAILED
 
FAILURE: Build failed with an exception.
 
* What went wrong:
Execution failed for task ':test'.
> There were failing tests. See the report at: file:///Users/loke/Projects/Java/Blog/gradle-examples/dependency-management/build/reports/tests/index.html
 
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
 
BUILD FAILED
 
Total time: 4.461 secs

正如我們所看到的,如果單元測試失敗了,輸出信息中將描述以下信息:

  • 哪一個測試失敗了。
  • 執行了幾個測試,其中幾個失敗了。
  • 測試報告的位置,測試報告提供了失敗(或成功)的測試的額外信息。

當我們執行單元測試時,Gradle會在相應目錄創建測試報告:

  • build/test-results目錄包含每次測試執行的原始數據。
  • build/reports/tests目錄包含一個HTML報告,描述了測試的結果。

HTML測試報告是一個非常有用的工具,因為它描述了測試失敗的原因。比如說,如果我們的單元測試認為MessageService類中的getMessage()方法返回字符串“Hello Worl1d!”,那么HTML報告看上去就像下圖一樣:


image

我們繼續,下面是如何打包和運行我們的演示程序。

打包和運行程序

我們能夠可以使用以下任意一種命令打包程序:gradle assembly或gradle build,這兩個命令都會在build/libs目錄中創建dependency-management.jar文件。

當我們使用java -jar dependency-management.jar命令運行演示程序時,我們可以看到如下輸出:

> java -jar dependency-management.jar
 
Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/log4j/Logger
    at net.petrikainulainen.gradle.HelloWorld.<clinit>(HelloWorld.java:10)
Caused by: java.lang.ClassNotFoundException: org.apache.log4j.Logger
    at java.net.URLClassLoader$1.run(URLClassLoader.java:372)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:360)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    ... 1 more

拋出異常的原因是,當我們運行程序時,Log4j的依賴在classpath中沒有找到。

解決這個問題最簡單的方式是創建一個所謂的“胖”Jar文件,即把所有程序運行所需的依賴都打包到Jar文件中去。

通過查閱Gradle Cookbook中的教程,可以修改構建腳本,如下:

apply plugin: 'java'
 
repositories {
    mavenCentral()
}
 
dependencies {
    compile 'log4j:log4j:1.2.17'
    testCompile 'junit:junit:4.11'
}
 
jar {
    from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
    manifest {
        attributes 'Main-Class': 'net.petrikainulainen.gradle.HelloWorld'
    }
}

現在,我們可以運行演示程序了(打包后),一切正常:

> java -jar dependency-management.jar 
INFO  - HelloWorld                 - Received message: Hello World!

這些就是今天的內容了,我們總結一下學到了什么。

總結

這篇教程教會了我們四個方面的內容:

  • 我們學會了配置構建所需的倉庫。
  • 我們學會了如何聲明所需的依賴以及依賴的分類(分組)。
  • 我們了解了Gradle會在測試執行后創建一個HTML測試報告。
  • 我們學會了創建一個所謂的“胖”Jar文件。

如果你想要玩一玩這篇教程所涉及的演示程序,你可以從GitHub那獲取。

關于作者:JustinWu

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,527評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,687評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,640評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,957評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,682評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,011評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,009評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,183評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,714評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,435評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,665評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,148評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,838評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,251評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,588評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,379評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,627評論 2 380

推薦閱讀更多精彩內容

  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,829評論 18 139
  • Spring Boot 參考指南 介紹 轉載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,923評論 6 342
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,764評論 25 708
  • 轉載注明出處:http://www.lxweimin.com/p/5255b100930e 0. 前言 完全由個人翻...
    王三的貓阿德閱讀 2,548評論 0 4
  • 1.介紹 如果你正在查閱build.gradle文件的所有可選項,請點擊這里進行查閱:DSL參考 1.1新構建系統...
    Chuckiefan閱讀 12,166評論 8 72