在 Eclipse 下利用 gradle 構建系統【轉】
在 Eclipse 下利用 gradle 構建系統
簡介: 構建系統時候常常要用到 Ant, Maven 等工具,對於初學者來說,它們還是過於複雜,上手還是需要時間的。本文將向讀者介紹一種全新的構建專案的方式 gradle,它簡單、上手快,能大大節省專案的時間和成本。
基本開發環境
- 作業系統: 本教程使用的為 Windows Vista Enterprise, 如果您的系統是 Linux 的,請選擇下載對應版本的其他工具,包括開發工具、Java EE 伺服器、Apache Ant、SoapUI。
-
開發工具:
Eclipse IDE for SOA Developers 版本,請到
- Java EE 伺服器: Apache-Tomcat-6.0.18,可以到 http://tomcat.apache.org/download-60.cgi 下載,使用 5.0 以上的任何版本都可以的,當然,您也可以使用 Jboss 等其他 Java EE 伺服器。
- Jdk: 到 http://java.sun.com 下載 1.5.0_17 版本,下載後安裝即可。
Ant 是我們過去構建系統基本都會用到的,xml 指令碼檔案中包括若干 task 任務,任務之間可以互相依賴,對於一個大的專案來說,這些 xml 檔案維護起來的確不是一件容易的事情,還有那些專案依賴的而沒有版本號的 jar 包,有時真的讓人頭疼,後來 Maven 出現了,基於中央倉庫的編譯相對於 Ant 來說的確是好了很多,但是,是不是 Ant,Maven 就是我們構建專案的唯一選擇呢?呵呵,當然不了,利用 Gradle 來構建系統我認為將成為 java 構建專案的最佳選擇,簡單,快速,對初學者無苛刻要求,可以說是拿來就會用,而且我們再也不用看那些冗長而複雜的 xml 檔案了,因為 Gradle 是基於 Groovy 語言的,Groovy 大家應該很熟悉吧,是基於 Java Virtual Machine 的敏捷開發語言,它結合了 Python、Ruby 和 Smalltalk 的許多強大的特性,如果你是一個 Ant 的完全支持者,也沒有問題,因為 Gradle 可以很平滑的來呼叫 Ant 檔案的,我這樣說你可能不接受 Gradle,下面我們就會通過一個個具體例項來講解 Ant,Maven,Gradle 構建專案的過程,通過例子我們能很容易明白它們的差異。Let ’ s go。
新建一個 Java project, 命名為 ant_project
然後新建一個 HelloWorld 類,我們下面就是將這個專案通過 Ant 來編譯,打包,類的程式碼列表如清單 1 所示:
package org.ant.test; public class HelloWorld { public String sayHello(String name){ return "Hello "+name; } } |
然後再新建一個 build 檔案,命名為 build.xml, 內容如清單 3 所示:
<?xml version="1.0" encoding="UTF-8"?> <project name="project" default="default"> <target name="default" depends="depends" description="description"> <javac srcdir="src" destdir="bin" includes="org/**"></javac> <jar basedir="bin" destfile="dist/ant_project.jar"></jar> <war destfile="dist/ant_project.war" webxml="WEB-INF/web.xml"> <classes dir="bin"></classes> </war> </target> <!-- - - - - - - - - - - - - - - - - - target: depends - - - - - - - - - - - - - - - - - --> <target name="depends"> </target> </project> |
熟悉 ant 的同學們對於上面的指令碼應該很容易看明白,這裡就不詳細解釋了,主要功能就是把這個工程編譯然後打成 jar 和 war 包。 到目前為止 ant_project 的目錄結構如圖 2 所示:
執行 ant 指令碼。
E:\gdcc\tools\apache-ant-1.6.5\bin\ant -f E:\ws_IBM\ant_project\build.xml 注:ant 放在了 E:\gdcc\tools\apache-ant-1.6.5 目錄下。 執行結果如下: Buildfile: E:\ws_IBM\ant_project\build.xml depends: default: [javac] Compiling 1 source file to E:\ws_IBM\ant_project\bin [jar] Building jar: E:\ws_IBM\ant_project\dist\ant_project.jar [war] Building war: E:\ws_IBM\ant_project\dist\ant_project.war BUILD SUCCESSFUL Total time: 859 milliseconds |
這是個非常簡單的工程,我們將他打成了 jar,war 包,所需要的 build 檔案大約在 10 行左右,下面我們再看看用 Gradle 的情況。
- 使用 cmd 命令,然後敲入 gradle – version,如出現以下資訊,表示環境配置成功。
C:\Documents and Settings\suchu>gradle -version Gradle 0.9-preview-1 Gradle buildtime: Monday, March 29, 2010 4:51:14 PM CEST Groovy: 1.7.1 Ant: Apache Ant version 1.8.0 compiled on February 1 2010 Ivy: 2.1.0 Java: 1.6.0_12 JVM: 11.2-b01 JVM Vendor: Sun Microsystems Inc. |
注:以上資訊根據不同版本的 Gradle 或者不同的環境也許不同,但都是正確的。
新建一個 Java project, 命名為 gradle_project
然後新建一個 java bean 名為 HelloWorld 內容和上面的一樣,可以參考 ant_project。 為了實現編譯,打包功能,我們需要新建一個名為 build.gradle 的檔案。 檔案內容見清單 3 所示:
apply plugin: 'java' |
是不是很驚訝,的確,真的就只要這麼短短的一行,而它的功能卻是相當的強大的,能編譯,打成 jar 包,執行測試指令碼等。 到目前為止,專案的結構如圖 4 所示:
這裡需要注意一點的是,專案包的結構最好是按照 Gradle 期望的來建立,當然也可以通過配置來改變。 下面我們來執行下 build.gradle 檔案。 執行 cmd 命令,進入 gradle_project 專案路徑下,然後執行 gradle build 命令,命令顯示資訊如清單 5 所示。
E:\ws_IBM\gradle_project>gradle build :compileJava :processResources :classes :jar :assemble :compileTestJava :processTestResources :testClasses :test :check :build BUILD SUCCESSFUL Total time: 5.125 secs |
我們再看下生成物,這個命令首先在 gradle_project 下新建了 build 目錄,build 目錄包含 classes, dependency-cache, libs,tmp 四個目錄,libs 下包含 jar 包,jar 包包含 main 下的所有 java 檔案和和資原始檔。 一個簡單的例子到這裡就演示完了,怎麼樣是不是指令碼很簡潔,用起來很簡單,產生想繼續學習的興趣了吧,別急,下面我們會繼續來探究 Gradle 的神奇之處。
下面我們來介紹幾個常用的命令,clean,這個命令是將剛才產生的 build 目錄刪除掉; Assemble,這個命令是編譯 java 檔案但是不執行檢查程式碼質量等的命令,執行時顯示的資訊如清單 6 所示:
E:\ws_IBM\gradle_project>gradle assemble :compileJava :processResources UP-TO-DATE :classes :jar :assemble BUILD SUCCESSFUL |
和清單 5 比較下,他們的區別應該很容易看出來,那麼我們怎麼樣來執行檢查程式碼質量的命令而不需要打成 jar 包之類的額外工作呢,check 命令正好滿足你的要求,此命令就是編譯 java 檔案並執行那些類似 Checkstyle,PMD 等外部外掛命令來檢查我們自己的原始碼。Check 命令執行顯示的資訊如清單 7 所示:
E:\ws_IBM\gradle_project>gradle check :compileJava UP-TO-DATE :processResources UP-TO-DATE :classes UP-TO-DATE :compileTestJava UP-TO-DATE :processTestResources UP-TO-DATE :testClasses UP-TO-DATE :test UP-TO-DATE :check UP-TO-DATE BUILD SUCCESSFUL |
這裡需要說明一點的是 Gradle 是增量式編譯的,只編譯那些有變動的 java 類或資原始檔的,如 UP-TO-DATE 表示是有更新的。 現在 javadoc 越來越受到人們的重視,尤其對於那些複雜的需要介面呼叫的的專案,javadoc 的地位就更加突出了,如果我們使用 Ant 需要在 build 檔案中增加清單 8 的片段。
<target name="javadoc"> <!-- destdir 是 javadoc 生成的目錄位置 --> <javadoc destdir="${distDir}" encoding="UTF-8" docencoding="UTF-8"> <!-- dir 是你的原始碼位置,記住是 java 檔案的位置而不是 class 檔案的位置, 第一次用這個命令容易忽略這點 切記 --> <packageset dir="${srcDir}"> <!-- exclude 是去掉那些不想生成 javadoc 的類檔案 --> <exclude name="${excludeClasses}" /> </packageset> </javadoc> </target> |
然後我們用 ant javadoc 命令來執行,即可生成 javadoc。那麼我們 利用 Gradle 是怎樣來生成 javadoc 的呢,都需要做那些額外的工作呢? build.gradle 檔案是否需要修改呢?我們的回答是,不用,什麼都不用修改,什麼都不用做,只需利用 gradle javadoc 命令,即可生成我們期望的 javadoc。 通常我們新建一個專案,.classpath 檔案的內容如清單 9 所示:
<?xml version="1.0" encoding="UTF-8"?> <classpath> <classpathentry kind="src" path="src"/> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER /org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/jdk1.6.0_12"/> <classpathentry kind="output" path="bin"/> </classpath> |
通過上面的知識我們知道,Gradle 期望的目錄結構和自動生成的是有些差別的,比如原始碼路徑,編譯後的檔案放置目錄等,那麼我們能不能通過 Gradle 命令來統一一下呢,使原專案結構與 Gradle 期望的一致,以免開發者將程式碼放置到了錯誤的目錄結構下,那樣 Gradle 是不管理它們的。下面我們就通過一個簡單的方法來實現上面的需求,首先我們來簡單修改下 build.gradle 檔案,新增 apply plugin: 'eclipse'這麼一行,然後我們使用命令 gradle eclipse 即可。.classpath 檔案的變化如清單 9 所示。
<?xml version="1.0" encoding="UTF-8"?> <classpath> <classpathentry kind="src" path="src/main/java"/> <classpathentry kind="output" path="build/classes/main"/> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> </classpath> |
War 包是我們經常要用到的,上面我們利用 Ant 指令碼生成過 war 包,那麼 Gradle 又是怎樣來生成 war 包的呢?經過上面的學習或許你已經猜出來了,需要增加一個 plugin,完全正確,只需要將 apply plugin: 'war' 這一行加入到 build.gradle 檔案中,然後執行 gradle War 命令即可,簡單的簡直要命,是不是,呵呵!
我們上面講過,Gradle 對其所能控制的目錄結構是有一定的要求的,那麼如果我們的專案已經開始很長時間了,現在的專案結構不滿足 Gradle 的要求,那麼我們還能不能利用 Gradle 呢?答案當然是肯定的,下面我們就介紹怎樣在老專案上使用 Gradle,方法很簡單,當然如果過於複雜我們也沒必要再這裡介紹它了,直接使用 Ant 就好了。首先我們需要在 build.gradle 檔案中增加如清單 10 所示的內容。
sourceSets { main { java.srcDir "$projectDir/src" } } |
然後我們就可以使用 Gradle 提供的所有命令和方法了。
大家都知道,一個專案在編譯過程中要依賴很多 jar 包的,在 Ant 中我們通過新增 classpath 來實現的,如清單 11 所示。
<path id="j2ee"> <pathelement location="${servlet.jar}" /> <pathelement location="${jsp-api.jar}" /> <pathelement location="${ejb.jar}" /> <pathelement location="${jms.jar}" /> </path> <javac destdir="${build.classes}" srcdir="${src.dir}" debug="${javac.debug}" deprecation="${javac.deprecation}"> <include name=" "/> <classpath refid="j2ee"/> </javac> |
那麼 Gradle 又是怎樣來做的呢?通過上面的知識的學習,你是否有一個大概的思路呢?假如我們現在有一個 java 類叫 HelloWorldTest,這個類中引用了 junit 這個 jar 包中的類,這時候我們用 Gradle 要怎樣來編譯這個類呢? 首先我們新建一個目錄叫 libs,這個目錄就是放置專案所依賴的所有 jar 包,當然包括 HelloWorldTest 類所依賴的 junit-4.4.jar 包,然後我們要修改下 build.gradle 檔案,增加內容見清單 12。
repositories { flatDir(dirs: "$projectDir/libs") } dependencies { compile ':junit:4.4' } |
注:repositories 相當一個儲存 jar 包的倉庫,我們可以指定本地的依賴 jar 包,也可以利用 Maven 所指定的倉庫,如 mavenCentral(); 通過 dependencies 來包含所有真正要依賴的 jar 包,格式為 goup:name:version,':junit:4.4:' 就是表示 dirs 路徑下的 junit-4.4.jar 這個包。
Copy 是我們經常要用到的一個命令,java 類的 copy,資原始檔的 copy 等等。如果是 Ant 我們會在 build.xml 檔案中加入清單 13 中的內容。
複製單個檔案到另一個檔案 <copy file="myfile.txt" tofile="mycopy.txt"/> 複製單個檔案到一個目錄 <copy file="myfile.txt" todir="../some/other/dir"/> 複製一個目錄到另一個目錄 <copy todir="../new/dir"> <fileset dir="src_dir"/> </copy> 複製一部分檔案到一個目錄下 <copy todir="../dest/dir"> <fileset dir="src_dir"> <exclude name="**/*.java"/> </fileset> </copy> <copy todir="../dest/dir"> <fileset dir="src_dir" excludes="**/*.java"/> </copy> |
我們知道 copy 任務中有很多屬性,這裡我們就不一一列出了,我們還是主要看下 Gradle 是如何來實現這些功能的。
我們只需要在 build.gradle 檔案中加入清單 14 中的內容。
task copyOne(type: Copy) { from 'src/main/test' into 'build/anotherDirectory' } |
注:把 test 目錄下的所有檔案複製到 anotherDirectory 目錄下。 然後我們利用命令 E:\ws_IBM\gradle_project>gradle copyOne 來執行即可。
有時候一個目錄下的檔案數目很多,而我們只想複製某一部分檔案,比如只複製 java 檔案或資原始檔等,這時候我們就要用到 copy 任務的 include 屬性,這一點和 Ant 是一樣的。比如只複製 java 檔案到某一指定目錄,實現這個需求我們要在 build.gradle 檔案中增加清單 15 的內容。
task copyTwo(type: Copy) { from 'src/main/test' into 'build/anotherDirectory' include '**/*.java' } |
如果我們只想排除一些檔案,不想把這一類檔案 copy 過去,這時候我們要用到 exclude 屬性,比如我們不想把 java 檔案複製到指定目錄中,那麼我們只需要將上面清單 15 中的 include 替換成 exclude 即可。
做專案時經常會遇到一個 project 中的類依賴另一個 project 中類的情況,如果用 Ant,我們會這樣做,首先將被依賴的類檔案打成 jar 包,然後利用 copy 命令將這個 jar 包複製到指定目錄下,我們可以想象到要向 build.xml 新增好多行程式碼,這裡我們就不一一列出了,不會的同學們可以參考上面的知識。下面我們看下 Gradle 是怎樣來完成這一需求的,Gradle 不但可以講 jar 包釋出到本地的指定目錄中,而且還可以釋出到遠端目錄中,我們看下清單 16 的內容。
publishJarFile { repositories { flatDir(dirs: file('jarsDerectory')) } } |
然後我們利用 gradle publishJarFile 命令即可。 注:清單 16 是將工程下的 java 類檔案全部打成 jar 包,然後放到工程目錄下的 jarsDerectory 子目錄中。
Maven 對於 jar 包的倉庫管理方法給我們提供了很多方便,Gradle 完全可以利用 Maven 的這一優點的,我們在上面已經講過了如何來使用,那麼我們又是怎麼來做到將專案所需要的 jar 包更新到倉庫中呢?具體解決方法見清單 17。
apply plugin: 'maven' publishToMaven { repositories.mavenDeployer { repository(url: "file://localhost/tmp/myRepo/") } } |
做專案時候,經常會碰到多個工程的情況,最通常的情況我們也分為伺服器端和客戶端兩部分,這種情況我們過去用 Ant 時候會在每個工程下面都建立個 build.xml 檔案或者建立一個 build.xml 檔案,然後在這個 build.xml 檔案中建立不同工程的 target,將將被引用的工程打成 jar 包來供其他工程引用,那麼 Gradle 是怎樣來完成這樣的需求的呢?下面我們舉個具體的例子來詳細演示下。首先我們新建一個主工程命名為 gradle_multiProject, 然後在主工程下在新建一個子工程命名為 sub_projectOne, 在兩個工程下面都有一個各自獨立的 src 並且符合 Gradle 要求的目錄結構,在每個工程下面都建個類命名為 HelloWorld,類內容同清單 1. 然後我們新建個 settings.gradle 檔案,內容見清單 18。
include "sub_projectone" |
然後在新建一個我們熟悉的 build.gradle 檔案,檔案內容見清單 19。
Closure printProjectName = { task -> println "I'm $task.project.name" } task hello << printProjectName project(':sub_projectone') { task hello << printProjectName } |
然後我們使用命令 gradle – q hello 執行一下,執行結果如清單 20 所示。
E:\ws_IBM\gradle_multiProject>gradle -q hello I'm gradle_multiProject I'm sub_projectone |
我們會發現,這個命令將主工程和子工程的名字都打印出來了,為什麼會這樣呢?我想你一定猜對了,因為我們在 build.gradle 檔案中使用了 project() 方法,方法內傳入的是子工程的名稱,如果我們子工程不止一個,那麼我們又該怎樣來呼叫呢?這時候我們只需要呼叫另一個方法 allprojects 即可,注意 allprojects 方法是不需要傳入引數的,它返回的是當前工程和當前工程下面的所有子工程的列表。上面演示的內容其實我們不經常用到的,這裡簡單的介紹下就是為了說明 gradle 給我們提供了好多方法來供我們呼叫,在多工程的環境下我們可以靈活的使用它們來達到我們的要求,下面我們就步入正題來看看在多工程情況下,gradle 是如何來編譯,打包各自工程的。這裡我們新增些內容到 build.gradle 檔案,內容見清單 21。
subprojects{ apply plugin: 'java' } |
然後我們用命令 gradle build,發現主工程下面的所有子工程都新增了一個 build 資料夾,這個資料夾下包含編譯生成的 class 檔案和 jar 檔案,而主工程的 src 下的程式碼卻沒有被編譯,打包。那麼我們怎樣做能讓主工程和子工程同時被編譯,打包呢?方法很簡單,我們只需要在 build.gradle 檔案中增加 apply plugin: 'java' 這麼一行程式碼,現在完整的 build.gradle 內容見清單 22。
apply plugin: 'java' subprojects{ apply plugin: 'java' } |
是不是很難想象,就這麼幾行程式碼就完成了將所有工程中的程式碼都編譯了並且都打成了 jar 檔案。有的朋友會問了,如果子工程與主工程他們打成的包不一樣,有的是需要 jar 包,有的需要打成 war 包等等,這樣的需求我們該怎樣做呢,很簡單我們只需要在需要打成 war 包的工程下面新建立個 build.gradle 檔案,該檔案內容為 apply plugin: 'war',然後我們我們在主工程目錄下使用 gradle build 命令即可生成我們需要的 war 包了,Gradle 就是使用這種方法來滿足那種差異性的需求的。
使用 Ant 的朋友們一定會深有感觸的吧!也許有些朋友會有反面的一些聲音,尤其對那些 Ant 的熱愛者們,一定會說,Ant 如果你使用的好,封裝的好一樣可以很簡潔並且也能達到這個效果的,的確是這樣的,Gradle 只不過是把我們經常要使用的一些功能項給封裝成了方法,然後我們呼叫這些方法即可了,再說了,Gradle 呼叫 Ant 指令碼也是可以的,如果你一定要用 Ant, 那麼你用 Gradle 來組織一下邏輯也是不錯的選擇。下面我們簡單看下在 Gradle 中式怎樣來呼叫 Ant 指令碼的。
首先我們建立 Ant 檔案 build.xml, 檔案詳細內容見清單 23.
<project> <target name="hello"> <echo>Hello, from Ant</echo> </target> </project> |
然後我們在建立個 build.gradle 檔案,檔案詳細內容見清單 24。
ant.importBuild 'build.xml' |
簡單吧,一句話的事情而已,呵呵。然後我們使用 gradle hello 命令來看下結果,結果見清單 25。
E:\gdcc\me\gradle-0.9-preview-1\samples\userguide\ant\hello>gradle hello :hello [ant:echo] Hello, from Ant BUILD SUCCESSFUL Total time: 9.734 secs |
可以看出,的確呼叫的是 Ant 的 build.xml 檔案吧。