- 在現(xiàn)實(shí)生活中,,要?jiǎng)?chuàng)造一個(gè)沒有任何外部依賴的應(yīng)用程序并非不可能,但也是極具挑戰(zhàn)的,。這也是為什么依賴管理對(duì)于每個(gè)軟件項(xiàng)目都是至關(guān)重要的一部分,。
這篇教程主要講述如何使用Gradle管理我們項(xiàng)目的依賴,我們會(huì)學(xué)習(xí)配置應(yīng)用倉庫以及所需的依賴,,我們也會(huì)理論聯(lián)系實(shí)際,,實(shí)現(xiàn)一個(gè)簡單的演示程序。
讓我們開始吧。
倉庫管理簡介
本質(zhì)上說,,倉庫是一種存放依賴的容器,,每一個(gè)項(xiàng)目都具備一個(gè)或多個(gè)倉庫。
Gradle支持以下倉庫格式:
我們來看一下,,對(duì)于每一種倉庫類型,,我們?cè)跇?gòu)建中應(yīng)該如何配置。
在構(gòu)建中加入Ivy倉庫
我們可以通過URL地址或本地文件系統(tǒng)地址,,將Ivy倉庫加入到我們的構(gòu)建中,。
如果想通過URL地址添加一個(gè)Ivy倉庫,我們可以將以下代碼片段加入到build.gradle文件中:
1 2 3 4 5 | repositories {
ivy {
url "http://ivy./repo"
}
}
|
如果想通過本地文件系統(tǒng)地址添加一個(gè)Ivy倉庫,,我們可以將以下代碼片段加入到build.gradle文件中:
1 2 3 4 5 | repositories {
ivy {
url "../ivy-repo"
}
}
|
小貼士:如果你想要獲得更多關(guān)于Ivy倉庫配置的信息,,你可以參考以下資源:
我們繼續(xù),下面是如何在構(gòu)建中加入Maven倉庫,。
在構(gòu)建中加入Maven倉庫
與Ivy倉庫很類似,,我們可以通過URL地址或本地文件系統(tǒng)地址,將Maven倉庫加入到我們的構(gòu)建中,。
如果想通過URL地址添加一個(gè)Maven倉庫,,我們可以將以下代碼片段加入到build.gradle文件中:
1 2 3 4 5 | repositories {
maven {
url "http://maven./repo"
}
}
|
如果想通過本地文件系統(tǒng)地址添加一個(gè)Maven倉庫,我們可以將以下代碼片段加入到build.gradle文件中:
1 2 3 4 5 | repositories {
maven {
url "../maven-repo"
}
}
|
在加入Maven倉庫時(shí),,Gradle提供了三種“別名”供我們使用,,它們分別是:
- mavenCentral()別名,表示依賴是從Central Maven 2 倉庫中獲取的,。
- jcenter()別名,,表示依賴是從Bintary’s JCenter Maven 倉庫中獲取的。
- mavenLocal()別名,,表示依賴是從本地的Maven倉庫中獲取的,。
如果我們想要將Central Maven 2 倉庫加入到構(gòu)建中,我們必須在build.gradle文件中加入以下代碼片段:
1 2 3 | repositories {
mavenCentral()
}
|
小貼士:如果你想要獲取更多關(guān)于Maven倉庫配置的信息,,你可以參考這篇文章:
section 50.6.4 Maven Repositories of the Gradle User Guide
我們繼續(xù),,下面是如何在構(gòu)建中加入Flat Directory倉庫。
在構(gòu)建中加入Flat Directory倉庫
如果我們想要使用Flat Directory倉庫,,我們需要將以下代碼片段加入到build.gradle文件中:
1 2 3 4 5 | repositories {
flatDir {
dirs 'lib'
}
}
|
這意味著系統(tǒng)將在lib目錄下搜索依賴,,同樣的,如果你愿意的話可以加入多個(gè)目錄,,代碼片段如下:
1 2 3 4 5 | repositories {
flatDir {
dirs 'libA' , 'libB'
}
}
|
小貼士:如果你想要獲得更多關(guān)于Flat Directory倉庫配置的信息,,你可以參考以下資源:
我們繼續(xù),下面要講的是,,如何使用Gradle管理項(xiàng)目中的依賴,。
依賴管理簡介
在配置完項(xiàng)目倉庫后,我們可以聲明其中的依賴,如果我們想要聲明一個(gè)新的依賴,,可以采用如下步驟:
- 指定依賴的配置,。
- 聲明所需的依賴。
讓我們看一下詳細(xì)步驟:
配置中的依賴分類
在Gradle中,,依賴是按照指定名稱進(jìn)行分類的,這些分類被稱為配置項(xiàng),,我們可以使用配置項(xiàng)聲明項(xiàng)目的外部依賴,。
Java插件指定了若干依賴配置項(xiàng),其描述如下:
- 當(dāng)項(xiàng)目的源代碼被編譯時(shí),,compile配置項(xiàng)中的依賴是必須的,。
- runtime配置項(xiàng)中包含的依賴在運(yùn)行時(shí)是必須的。
- testCompile配置項(xiàng)中包含的依賴在編譯項(xiàng)目的測試代碼時(shí)是必須的,。
- testRuntime配置項(xiàng)中包含的依賴在運(yùn)行測試代碼時(shí)是必須的,。
- archives配置項(xiàng)中包含項(xiàng)目生成的文件(如Jar文件)。
- default配置項(xiàng)中包含運(yùn)行時(shí)必須的依賴,。
我們繼續(xù),,下面是如何在項(xiàng)目中聲明依賴。
聲明項(xiàng)目依賴
最普遍的依賴稱為外部依賴,,這些依賴存放在外部倉庫中,。一個(gè)外部依賴可以由以下屬性指定:
- group屬性指定依賴的分組(在Maven中,就是groupId),。
- name屬性指定依賴的名稱(在Maven中,,就是artifactId)。
- version屬性指定外部依賴的版本(在Maven中,,就是version),。
小貼士:這些屬性在Maven倉庫中是必須的,如果你使用其他倉庫,,一些屬性可能是可選的,。打個(gè)比方,如果你使用Flat directory倉庫,,你可能只需要指定名稱和版本,。
我們假設(shè)我們需要指定以下依賴:
- 依賴的分組是foo。
- 依賴的名稱是foo,。
- 依賴的版本是0.1,。
- 在項(xiàng)目編譯時(shí)需要這些依賴。
我們可以將以下代碼片段加入到build.gradle中,,進(jìn)行依賴聲明:
1 2 3 | dependencies {
compile group: 'foo' , name: 'foo' , version: '0.1'
}
|
我們也可以采用一種快捷方式聲明依賴:[group]:[name]:[version],。如果我們想用這種方式,我們可以將以下代碼段加入到build.gradle中:
1 2 3 | dependencies {
compile 'foo:foo:0.1'
}
|
我們也可以在同一個(gè)配置項(xiàng)中加入多個(gè)依賴,傳統(tǒng)的方式如下:
1 2 3 4 5 6 | dependencies {
compile (
[group: 'foo' , name: 'foo' , version: '0.1' ],
[group: 'bar' , name: 'bar' , version: '0.1' ]
)
}
|
如果采用快捷方式,,那可以是這樣:
1 2 3 | dependencies {
compile 'foo:foo:0.1' , 'bar:bar:0.1'
}
|
自然地,,聲明屬于不同配置項(xiàng)的依賴也是可以的。比如說,,如果我們想要聲明屬于compile和testCompile配置項(xiàng)的依賴,,可以這么做:
1 2 3 4 | dependencies {
compile group: 'foo' , name: 'foo' , version: '0.1'
testCompile group: 'test' , name: 'test' , version: '0.1'
}
|
同樣的,給力的快捷方式又來了( ̄︶ ̄)
1 2 3 4 | dependencies {
compile 'foo:foo:0.1'
testCompile 'test:test:0.1'
}
|
小貼士:你可以從這篇文章中獲得更多關(guān)于依賴聲明的信息,。
我們已經(jīng)學(xué)習(xí)了依賴管理的基礎(chǔ)知識(shí),,下面我們來實(shí)現(xiàn)一個(gè)演示程序。
創(chuàng)建演示程序
演示程序的需求是這樣的:
- 演示程序的構(gòu)建腳本必須使用Maven central倉庫,。
- 演示程序必須使用Log4j寫入日志,。
- 演示程序必須包含包含單元測試,保證正確的信息返回,,單元測試必須使用JUnit編寫,。
- 演示程序必須創(chuàng)建一個(gè)可執(zhí)行的Jar文件。
我們來看一下怎樣實(shí)現(xiàn)這些需求,。
配置倉庫
我們的演示程序的一個(gè)需求是構(gòu)建腳本必須使用Maven central倉庫,,在我們使用Maven central倉庫配置構(gòu)建腳本后,源代碼如下:
1 2 3 4 5 6 7 8 9 10 11 | apply plugin: 'java'
repositories {
mavenCentral()
}
jar {
manifest {
attributes 'Main-Class' : 'net.petrikainulainen.gradle.HelloWorld'
}
}
|
我們?cè)賮砜匆幌氯绾螌?duì)我們的演示程序進(jìn)行依賴聲明,。
依賴聲明
在build.gradle文件中,,我們聲明了兩個(gè)依賴:
在我們聲明了這些依賴后,,build.gradle文件是這樣的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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'
}
}
|
我們繼續(xù),稍微加入一些代碼,。
編寫代碼
為了實(shí)現(xiàn)我們演示程序的需求,,“我們不得不過度工程化一下”,我們會(huì)按照下列步驟創(chuàng)建程序:
- 創(chuàng)建一個(gè)MessageService類,,當(dāng)其中的getMessage()方法被調(diào)用時(shí),,返回字符串“Hello World!”。
- 創(chuàng)建一個(gè)MessageServiceTest類,,確保MessageService類中的getMessage()方法返回字符串“Hello World!”,。
- 創(chuàng)建程序的主類,從MessageService對(duì)象獲取信息,,并使用Log4j寫入日志,。
- 配置Log4j。
我們按部就班的操作一下,。
首先,,在src/main/java/net/petrikainulainen/gradle目錄下新建一個(gè)MessageService類并加以實(shí)現(xiàn),,代碼如下:
1 2 3 4 5 6 7 8 | package net.petrikainulainen.gradle;
public class MessageService {
public String getMessage() {
return "Hello World!" ;
}
}
|
其次,在src/main/test/net/petrikainulainen/gradle目錄下新建一個(gè)MessageServiceTest類,,編寫單元測試,,代碼如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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目錄下新建一個(gè)HelloWorld類,,這是程序的主類,,從MessageService對(duì)象獲取信息,并使用Log4j寫入日志,,代碼如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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文件如下:
1 2 3 4 5 | 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
|
這樣就好了,我們看看如何執(zhí)行測試,。
執(zhí)行測試
我們可以通過以下命令執(zhí)行測試,。
當(dāng)測試通過時(shí),我們能看到如下輸出:
1 2 3 4 5 6 7 8 9 10 11 12 | > gradle test
:compileJava
:processResources
:classes
:compileTestJava
:processTestResources
:testClasses
: test
BUILD SUCCESSFUL
Total time : 4.678 secs
|
然而,,如果測試失敗,,我們將看到如下輸出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | > 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
|
正如我們所看到的,如果單元測試失敗了,,輸出信息中將描述以下信息:
- 哪一個(gè)測試失敗了,。
- 執(zhí)行了幾個(gè)測試,其中幾個(gè)失敗了,。
- 測試報(bào)告的位置,,測試報(bào)告提供了失敗(或成功)的測試的額外信息,。
當(dāng)我們執(zhí)行單元測試時(shí),,Gradle會(huì)在相應(yīng)目錄創(chuàng)建測試報(bào)告:
- build/test-results目錄包含每次測試執(zhí)行的原始數(shù)據(jù)。
- build/reports/tests目錄包含一個(gè)HTML報(bào)告,,描述了測試的結(jié)果,。
HTML測試報(bào)告是一個(gè)非常有用的工具,因?yàn)?strong>它描述了測試失敗的原因,。比如說,,如果我們的單元測試認(rèn)為MessageService類中的getMessage()方法返回字符串“Hello Worl1d!”,那么HTML報(bào)告看上去就像下圖一樣:
我們繼續(xù),,下面是如何打包和運(yùn)行我們的演示程序,。
打包和運(yùn)行程序
我們能夠可以使用以下任意一種命令打包程序:gradle assembly或gradle build,這兩個(gè)命令都會(huì)在build/libs目錄中創(chuàng)建dependency-management.jar文件,。
當(dāng)我們使用java -jar dependency-management.jar命令運(yùn)行演示程序時(shí),,我們可以看到如下輸出:
1 2 3 4 5 6 7 8 9 10 11 12 13 | > 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
|
拋出異常的原因是,,當(dāng)我們運(yùn)行程序時(shí),Log4j的依賴在classpath中沒有找到,。
解決這個(gè)問題最簡單的方式是創(chuàng)建一個(gè)所謂的“胖”Jar文件,,即把所有程序運(yùn)行所需的依賴都打包到Jar文件中去。
通過查閱Gradle Cookbook中的教程,,可以修改構(gòu)建腳本,,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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'
}
}
|
現(xiàn)在,我們可以運(yùn)行演示程序了(打包后),,一切正常:
1 2 | > java -jar dependency-management.jar
INFO - HelloWorld - Received message: Hello World!
|
這些就是今天的內(nèi)容了,,我們總結(jié)一下學(xué)到了什么。
總結(jié)
這篇教程教會(huì)了我們四個(gè)方面的內(nèi)容:
- 我們學(xué)會(huì)了配置構(gòu)建所需的倉庫,。
- 我們學(xué)會(huì)了如何聲明所需的依賴以及依賴的分類(分組),。
- 我們了解了Gradle會(huì)在測試執(zhí)行后創(chuàng)建一個(gè)HTML測試報(bào)告。
- 我們學(xué)會(huì)了創(chuàng)建一個(gè)所謂的“胖”Jar文件,。
如果你想要玩一玩這篇教程所涉及的演示程序,,你可以從GitHub那獲取。
|