級(jí)別: 中級(jí)
Sing Li (westmakaha@yahoo.com), 作家, Wrox Press
2007 年 1 月 08 日
現(xiàn)代軟件項(xiàng)目不再是單個(gè)本地團(tuán)隊(duì)獨(dú)立開發(fā)的產(chǎn)物。隨著健壯的企業(yè)級(jí)開源組件的可用性日益提高,當(dāng)今的軟件項(xiàng)目需要項(xiàng)目團(tuán)隊(duì)間的動(dòng)態(tài)協(xié)作,往往也需要混合使用在全球范圍內(nèi)創(chuàng)建和維護(hù)的組件。如今,Apache Maven 構(gòu)建系統(tǒng)步入了第二代,它和由 Internet 帶來的全球軟件開發(fā)時(shí)代之前所創(chuàng)建的那些遺留構(gòu)建工具不同,它完全是重新設(shè)計(jì)的,以應(yīng)對(duì)這些現(xiàn)代的挑戰(zhàn)。本教程將帶您從頭領(lǐng)略 Maven 2。
開始之前
現(xiàn)代軟件開發(fā)基于健壯的企業(yè)級(jí)開源技術(shù),它需要一類新的構(gòu)建工具和項(xiàng)目協(xié)作工具。Apache Maven 2 的核心引擎旨在簡化往往十分復(fù)雜的大型協(xié)作性軟件項(xiàng)目的構(gòu)建和管理。在大型項(xiàng)目團(tuán)隊(duì)環(huán)境中工作是種挑戰(zhàn),而 Maven 2 以友好為設(shè)計(jì)目的,即便對(duì)那些不熟悉這種挑戰(zhàn)的開發(fā)人員而言,Maven 2 也足夠友好。本教程首先圍繞單個(gè)的新手開發(fā)展開,然后逐漸介紹 Maven 2 中的一些協(xié)作性的概念和功能。我們鼓勵(lì)您在本教程提供的介紹之上,探究本文范圍之外的 Maven 2 的一些高級(jí)功能。
關(guān)于本教程
本教程將循序漸進(jìn)地向您介紹有關(guān) Maven 2 的基礎(chǔ)概念和實(shí)踐練習(xí),具體內(nèi)容包括:
讀完本教程,您會(huì)理解并欣賞 Maven 2 背后的設(shè)計(jì)理念。也將進(jìn)一步熟悉在使用 Maven 2 構(gòu)建的項(xiàng)目中工作所需的基本技能。這是一張通往 Apache 和 Codehaus 社區(qū)中大多數(shù)大型項(xiàng)目的通行證。最為重要的是,您將可以把 Maven 2 應(yīng)用到日常的項(xiàng)目構(gòu)建和管理活動(dòng)中。
先決條件
您應(yīng)當(dāng)大體上熟悉 Java™ 開發(fā)。本教程假設(shè)您理解構(gòu)建工具的價(jià)值和基本操作,包括依賴項(xiàng)管理和輸出打包。要求您能將 Eclipse 3.2 用作 IDE,從而在 Eclipse 中使用 Maven 2.x 插件。如果您熟悉大型開源項(xiàng)目(如 Apache 軟件基金會(huì)管理下的項(xiàng)目),那就太好了。理解 Java 5 編碼(包括泛型)會(huì)很有幫助。如果有各種項(xiàng)目構(gòu)建技術(shù)的經(jīng)驗(yàn)(如 Ant、autoconf、make 和 nmake)也很好,但不做強(qiáng)制性要求。
autoconf
make
nmake
系統(tǒng)需求
為繼續(xù)學(xué)習(xí)和試驗(yàn)本教程中的代碼,您需要有效安裝 Sun's JDK 1.5.0_09 (或其后的版本)或 IBM JDK 1.5.0 SR3。
針對(duì)教程中關(guān)于 Eclipse 的 Maven 2.x 的插件部分,需要有效安裝 Eclipse 3.2.1 或其后版本。
本教程的推薦系統(tǒng)配置如下:
本教程中的說明基于 Microsoft Windows 操作系統(tǒng)。教程中涵蓋的所有工具也可以在 Linux® 和 UNIX® 系統(tǒng)上運(yùn)行。
Maven 2 概覽
Maven 是一個(gè)頂級(jí)的 Apache Software Foundation 開源項(xiàng)目,創(chuàng)建它最初是為了管理 Jakarta Turbine 項(xiàng)目復(fù)雜的構(gòu)建過程。從那以后,不論是開源開發(fā)項(xiàng)目還是私有開發(fā)項(xiàng)目都選擇 Maven 作為項(xiàng)目構(gòu)建系統(tǒng)。Maven 快速地發(fā)展著,如今已是第二版,Maven 已經(jīng)從針對(duì)單個(gè)復(fù)雜項(xiàng)目的定制構(gòu)建工具成長為廣泛使用的構(gòu)建管理系統(tǒng),其豐富的功能可以應(yīng)用于大多數(shù)的軟件開發(fā)場(chǎng)景。
概括來講,Maven 2 能夠:
Maven 2 —— 概念性的概覽
為捕捉項(xiàng)目構(gòu)建知識(shí),Maven 2 依賴于一套發(fā)展中的有關(guān)事物如何運(yùn)轉(zhuǎn)的概念性模型。部分模型被硬編碼為 Maven 代碼庫的一部分,這些模型通過新的 Maven 發(fā)布版本不斷得到精煉。圖 1 解釋了 Maven 2 的關(guān)鍵模型:
圖 1. Maven 2 對(duì)象和操作模型
圖 1 中的關(guān)鍵組件為:
如果您對(duì)其中一些概念還有點(diǎn)模糊,請(qǐng)不要擔(dān)心。接下來的部分會(huì)用具體的例子來鞏固這些模型背后的概念。
Maven 2 —— 物理概覽
圖 2 揭示了 Maven 2 的操作方式和與之交互的方式,同時(shí)顯示了它的物理構(gòu)成。圖 2 提供了有關(guān)如何與 Maven 進(jìn)行交互的概覽:
圖 2. Maven 2 操作和交互模型
圖 2 中,POM 是 Maven 對(duì)您的特定項(xiàng)目的理解。這個(gè)模型由包含在一系列 pom.xml 文件中的聲明性描述構(gòu)成。這些 pom.xml 文件構(gòu)成一棵樹,每個(gè)文件能從其父文件中繼承屬性。Maven 2 提供一個(gè) Super POM。這個(gè) Super POM 位于層級(jí)樹的頂端,它包含所有項(xiàng)目的默認(rèn)通用屬性;每個(gè)項(xiàng)目的 POM 都從這個(gè) Super POM 處繼承。
依賴項(xiàng)被指定為 pom.xml 文件的一部分。Maven 根據(jù)其依賴項(xiàng)管理模型解析項(xiàng)目依賴項(xiàng)。Maven 2 在本地存儲(chǔ)庫和全球存儲(chǔ)庫尋找依賴性組件(在 Maven 術(shù)語里稱作工件 )。在遠(yuǎn)程存儲(chǔ)庫中解析的工件被下載到本地存儲(chǔ)庫中,以便使接下來的訪問可以有效進(jìn)行。Maven 2 中的這個(gè)依賴項(xiàng)解析器可以處理可遞 依賴項(xiàng)。即,它能有效地解析您的依賴項(xiàng)所依賴的那些依賴項(xiàng)。
Maven 引擎通過插件 親自執(zhí)行幾乎所有的文件處理任務(wù)。插件被配置和描述在 pom.xml 文件中。依賴項(xiàng)管理系統(tǒng)將插件當(dāng)作工件來處理,并根據(jù)構(gòu)建任務(wù)的需要來下載插件。每個(gè)插件都能和生命周期中的不同階段聯(lián)系起來。Maven 引擎有一個(gè)狀態(tài)機(jī),它運(yùn)行在生命周期的各個(gè)階段,在必要的時(shí)候調(diào)用插件。
理解 Maven 2 依賴項(xiàng)管理模型
在有效利用 Maven 2 之前,您需要理解 Maven 2 依賴項(xiàng)管理模型是如何運(yùn)行的。
依賴項(xiàng)開發(fā)適應(yīng)于這樣的項(xiàng)目,其軟件組件(稱作模塊 )是由不同的項(xiàng)目團(tuán)隊(duì)開發(fā)的。它支持持續(xù)獨(dú)立開發(fā),也支持對(duì)所有依賴模塊進(jìn)行精煉。
這個(gè)團(tuán)隊(duì)協(xié)作場(chǎng)景在通過 Internet 建立和維護(hù)的開源項(xiàng)目中十分常見,由于內(nèi)部開發(fā)大受開源或外包世界的沖擊和影響,這種場(chǎng)景在合作開發(fā)的圈子里日益盛行。
解析項(xiàng)目依賴項(xiàng)
Maven 2 依賴項(xiàng)管理引擎幫助解析構(gòu)建過程中的項(xiàng)目依賴項(xiàng)。
Maven 2 本地存儲(chǔ)庫是磁盤上的一個(gè)目錄,通常位于 HomeDirectory/.m2/repository。這個(gè)庫扮演著高性能本地緩存的角色,存儲(chǔ)著在依賴項(xiàng)解析過程中下載的工件。遠(yuǎn)程存儲(chǔ)庫要通過網(wǎng)絡(luò)訪問。可以在 settings.xml 配置文件中維護(hù)一個(gè)遠(yuǎn)程存儲(chǔ)庫列表以備使用。
實(shí)踐中,依賴項(xiàng)在 pom.xml 文件內(nèi)的 <dependencies> 元素中指定,并作為 POM 的一部分注入到 Maven 中。
<dependencies>
項(xiàng)目依賴項(xiàng)存儲(chǔ)在存儲(chǔ)庫服務(wù)器(在 Maven 術(shù)語中簡單地稱之為存儲(chǔ)庫 )上。要成功的解析依賴項(xiàng),需要從包含該工件的存儲(chǔ)庫里找到所需的依賴性工件。
可以在一個(gè) settings.xml 文件中指定影響 Maven 操作的配置屬性。默認(rèn)的設(shè)置文件是 MavenInstallationDirectory/conf/settings.xml。Maven 2 用戶可通過維護(hù) UserHomeDirectory/.m2/settings.xml 來覆蓋一些配置屬性。參見 Maven 設(shè)置參考,獲取更多有關(guān)可配置設(shè)置的信息。
基于 POM 中的項(xiàng)目依賴項(xiàng)信息,該依賴項(xiàng)解析器試圖以下列方式解析依賴項(xiàng):
默認(rèn)情況下,第二步中所涉及的第一個(gè)遠(yuǎn)程存儲(chǔ)庫是一個(gè)能在全球訪問的集中式 Maven 2 存儲(chǔ)庫,它包含了最流行的開源項(xiàng)目的一些工件。在內(nèi)部開發(fā)中,可以設(shè)置額外的遠(yuǎn)程存儲(chǔ)庫來包含從內(nèi)部開發(fā)模塊中發(fā)布的工件。可以使用 settings.xml 中的 <repositories> 元素來配置這些額外的遠(yuǎn)程存儲(chǔ)庫。
<repositories>
確保單個(gè)的工件
將 Maven 2 用于項(xiàng)目構(gòu)建時(shí),依賴項(xiàng)解析通過一個(gè)集中的存儲(chǔ)庫確保只存在一個(gè)依賴性工件,而不考慮有多少項(xiàng)目或子項(xiàng)目引用該工件。這是多模塊項(xiàng)目構(gòu)建中一個(gè)重要的屬性,因?yàn)榘鄠€(gè)工件會(huì)導(dǎo)致一些項(xiàng)目一致性和集成方面的問題。
存儲(chǔ)庫和坐標(biāo)
Maven 2 存儲(chǔ)庫存儲(chǔ) Maven 在一個(gè)項(xiàng)目的依賴項(xiàng)解析中使用過的工件集。在本地磁盤上訪問本地存儲(chǔ)庫,通過網(wǎng)絡(luò)訪問遠(yuǎn)程存儲(chǔ)庫。
工件通常被打包成包含二進(jìn)制庫或可執(zhí)行庫的 JAR 文件。這被認(rèn)為是工件的一個(gè)類型。但在實(shí)踐中,工件也可以是 WAR、EAR 或其他代碼捆綁類型。
Maven 2 利用操作系統(tǒng)的目錄結(jié)構(gòu)對(duì)存儲(chǔ)在存儲(chǔ)庫中的工件集進(jìn)行快速索引。這個(gè)存儲(chǔ)庫索引系統(tǒng)依賴于這種能力來通過工件的坐標(biāo) 惟一標(biāo)識(shí)工件。
Maven 坐標(biāo)
Maven 坐標(biāo)是一組可以惟一標(biāo)識(shí)工件的三元組值。坐標(biāo)包含了下列三條信息:
com.ibm.devworks
OpsImp
mmm.nnn.bbb-qqqqqqq-dd
mmm
nnn
bbb
qqqqq
dd
對(duì) Maven 坐標(biāo)的使用貫穿于 Maven 配置文件和 POM 文件中。例如,要在命名為 OpsImp 的模塊上指定項(xiàng)目依賴項(xiàng)(在 1.0-SNAPSHOT 級(jí)別),pom.xml 文件應(yīng)包含清單 1 所示的部分:
<dependencies> <dependency> <groupId>com.ibm.devworks</groupId> <artifactId>OpsImp</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies>
特別限定詞 SNAPSHOT 告訴 Maven 2:該項(xiàng)目或模塊還處于開發(fā)狀態(tài)中,它應(yīng)該獲取最新版的可用工件。
SNAPSHOT
要將該項(xiàng)目指定為依賴于 JUnit 進(jìn)行單元測(cè)試,可以將 JUnit 3.8.1 的坐標(biāo)作為一個(gè)依賴項(xiàng)添加到該項(xiàng)目的 pom.xml 文件中,如清單 2 所示:
<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> </dependency> </dependencies>
深入 Maven 存儲(chǔ)庫
由于 Maven 存儲(chǔ)庫是普通的目錄樹,所以可以很容易地看到工件是如何存儲(chǔ)到磁盤上的。圖 3 是本地存儲(chǔ)庫的一部分,顯示了 JUnit 3.8.1 工件的位置:
從圖 3 中可以看出,Maven 維護(hù)了一個(gè)工件的 POM 文件,同時(shí)也為該工件和其存儲(chǔ)庫中的 POM 維護(hù)了檢驗(yàn)和散列。當(dāng)工件在存儲(chǔ)庫間轉(zhuǎn)移時(shí),這些文件幫助確保工件的完整性。該工件已由 Maven 的依賴項(xiàng)管理引擎從中央存儲(chǔ)庫下載并放置到本地存儲(chǔ)庫中。
在圖 4 中,坐標(biāo)為 com.ibm.devworks/OpsImp/1.0-SNAPSHOT 的工件顯示在本地存儲(chǔ)庫中。該工件和 POM 文件一起存放在存儲(chǔ)庫。在本例中,該工件在本地安裝。
com.ibm.devworks/OpsImp/1.0-SNAPSHOT
Maven 2 生命周期、階段、插件和 mojo
Maven 通過插件動(dòng)作完成大多數(shù)構(gòu)建任務(wù)。可以把 Maven 引擎認(rèn)為是插件動(dòng)作的協(xié)調(diào)器。
插件中的 Mojo
插件是適應(yīng) Maven 的插件框架的軟件模塊。現(xiàn)在,可以使用 Java、Ant 或 Beanshell 來創(chuàng)建自定義插件。插件中的每個(gè)任務(wù)稱作一個(gè) mojo。有時(shí),插件也被視為一套相關(guān)的 mojo。創(chuàng)建自定義的 Maven 2 插件超出了本教程的范圍;參見 參考資料,以獲取更多信息。
Maven 2 是預(yù)先打包好的,以便于下載,它和許多常用插件一起使用。大多數(shù)典型開發(fā)任務(wù)不需要使用額外插件。
在開始編寫自己的插件前,您應(yīng)該先參考一下列出流行 Maven 2 插件的 Web 站點(diǎn)(參見 參考資料),看一下您需要的插件是不是已經(jīng)有了。圖 5 顯示了 Maven Plugin Matrix(參見 參考資料),它提供了許多可用插件的兼容性信息:
將 mojo 綁定到生命周期各階段
Maven 引擎在執(zhí)行構(gòu)建生命周期中相應(yīng)的階段時(shí),執(zhí)行插件中的 mojo(構(gòu)建任務(wù))。插件的 mojo 和生命周期中的階段間的關(guān)聯(lián)叫做綁定 。插件開發(fā)人員能夠靈活地將一個(gè)或多個(gè)生命周期階段和一個(gè)插件關(guān)聯(lián)起來。
默認(rèn)的生命周期的各階段
Maven 對(duì)構(gòu)建生命周期的固定理解包含了許多不同的階段。表 1 簡短地描述了各個(gè)階段:
Maven 從開源社區(qū)中汲取了十多年的項(xiàng)目構(gòu)建管理經(jīng)驗(yàn)。很難找到一個(gè)構(gòu)建周期不符合表 1 中的生命周期階段的軟件項(xiàng)目。
啟動(dòng) Maven 2 引擎后,它會(huì)按順序經(jīng)歷表 1 中的各階段,執(zhí)行可能與該階段綁定的 mojo。每個(gè) mojo 則可以使用 Maven 2 豐富的 POM 支持、依賴項(xiàng)管理,也可以訪問執(zhí)行這一專門任務(wù)時(shí)的構(gòu)建狀態(tài)信息。
調(diào)用 Maven 2 引擎時(shí),可以將一個(gè)生命周期階段指定為命令行參數(shù)。該引擎一直執(zhí)行到指定的階段(包括該指定的階段)。包含的階段中所有的 mojo 都會(huì)被觸發(fā)。
簡短地說,這就是 Maven 2 的操作原理。在下一部分里,您將直接面對(duì)操作。對(duì) Maven 操作、它的依賴項(xiàng)管理模型和它的 POM 有了基本的理解,您會(huì)發(fā)現(xiàn)實(shí)踐 Maven 2 是一項(xiàng)十分簡單的練習(xí)。
下載和安裝 Maven 2
根據(jù)下列步驟下載和安裝 Maven 2:
PATH
為檢驗(yàn)安裝,鍵入 mvn -help 命令。您將看到清單 3 中的幫助頁面。
mvn -help
C:\>mvn -help usage: mvn [options] [<goal(s)>] [<phase(s)>] Options: -C,--strict-checksums Fail the build if checksums don't match -c,--lax-checksums Warn if checksums don't match -P,--activate-profiles Comma-delimited list of profiles to activate -ff,--fail-fast Stop at first failure in reactorized builds -fae,--fail-at-end Only fail the build afterwards; allow all non-impacted builds to continue -B,--batch-mode Run in non-interactive (batch) mode -fn,--fail-never NEVER fail the build, regardless of project result -up,--update-plugins Synonym for cpu -N,--non-recursive Do not recurse into sub-projects -npr,--no-plugin-registry Don't use ~/.m2/plugin-registry.xml for plugin versions -U,--update-snapshots Update all snapshots regardless of repository policies -cpu,--check-plugin-updates Force upToDate check for any relevant registered plugins -npu,--no-plugin-updates Suppress upToDate check for any relevant registered plugins -D,--define Define a system property -X,--debug Produce execution debug output -e,--errors Produce execution error messages -f,--file Force the use of an alternate POM file. -h,--help Display help information -o,--offline Work offline -r,--reactor Execute goals for project found in the reactor -s,--settings Alternate path for the user settings file -v,--version Display version information
實(shí)踐 Maven 2:您的第一個(gè) Maven 2 項(xiàng)目
在第一個(gè)實(shí)踐的例子里,您將看到如何用最小的努力使用 Maven 2 構(gòu)建簡單項(xiàng)目。Maven 2 內(nèi)置的關(guān)于 Java 項(xiàng)目的知識(shí)消除了其他構(gòu)建工具也許必須要經(jīng)歷的冗長的配置過程。
處理數(shù)值操作的類
該例使用了一個(gè)處理數(shù)值操作的類。這個(gè)主體類的源代碼(叫做 NumOps)如清單 4 所示。
NumOps
package com.ibm.devworks; import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class NumOps { private List <Operation> ops = new ArrayList <Operation>(); public NumOps() { ops.add( new AddOps()); } public Operation getOp(int i) { Operation retval; if (i > ops.size()) { retval = null; } else { retval = (Operation) ops.get(i); } return retval; } public int size() { return ops.size(); } public static void main( String[] args ) { NumOps nop = new NumOps(); for (int i=0; i < nop.size(); i++) { System.out.println( "2 " + nop.getOp(i).getDesc() + " 1 is " + nop.getOp(i).op(2,1) ); } } }
NumOps 類管理一個(gè)能夠在兩個(gè)整數(shù)上執(zhí)行數(shù)值操作的對(duì)象集。main 方法創(chuàng)建一個(gè) NumOps 實(shí)例,然后調(diào)用由 NumOps 管理的每個(gè)對(duì)象,同時(shí)分別調(diào)用它的 getDesc() 方法和 op() 方法。由 NumOps 管理的所有對(duì)象都實(shí)現(xiàn)了在 Operation.java 中定義的 Operation 接口,接口代碼如清單 5 所示:
getDesc()
op()
Operation
package com.ibm.devworks; public interface Operation { int op(int a, int b); String getDesc(); }
在這個(gè)初始的例子里定義的惟一操作是一個(gè) AddOps 類,如清單 6 所示:
AddOps
package com.ibm.devworks; public class AddOps implements Operation { public int op(int a, int b) { return a+b; } public String getDesc() { return "plus"; } }
執(zhí)行 NumOps 類時(shí),會(huì)打印出下列輸出:
2 plus 1 is 3
使用 Archetype 來創(chuàng)建初始項(xiàng)目
要?jiǎng)?chuàng)建能用 Maven 構(gòu)建的簡單 Java 項(xiàng)目所需的一切,可以使用 Archetype 插件,它是 Maven 2 中的標(biāo)準(zhǔn)插件。不同于構(gòu)建階段插件,Archetype 在 Maven 項(xiàng)目構(gòu)建生命周期之外運(yùn)行,用于創(chuàng)建 Maven 項(xiàng)目。在您想要包含 NumOps 項(xiàng)目的目錄中執(zhí)行下列命令(將所有命令敲在一行):
mvn archetype:create -DarchetypeGroupId=org.apache.maven.archetypes -DgroupId=com.ibm.devworks -DartifactId=NumOps
該命令為 Archetype 插件提供您模塊的坐標(biāo):com.ibm.devworks/NumOps/1.0-SNAPSHOT。在此情況下,不需要指定版本,因?yàn)?Archetype 插件常默認(rèn)為 1.0-SNAPSHOT。此命令為項(xiàng)目創(chuàng)建了一個(gè)起始的 pom.xml 文件,也創(chuàng)建了規(guī)范的 Maven 2 目錄結(jié)構(gòu)。您將在本教程源代碼下載處的 example1 目錄下找到該代碼(參見 下載)。
com.ibm.devworks/NumOps/1.0-SNAPSHOT
1.0-SNAPSHOT
該輸出應(yīng)該與清單 7 類似:
[INFO] Scanning for projects... [INFO] Searching repository for plugin with prefix: 'archetype'. [INFO] ------------------------------------------------------------------------- --- [INFO] Building Maven Default Project [INFO] task-segment: [archetype:create] (aggregator-style) [INFO] ------------------------------------------------------------------------- --- [INFO] Setting property: classpath.resource.loader.class => 'org.codehaus.plexus ... [INFO] [archetype:create] [INFO] Defaulting package to group ID: com.ibm.devworks [INFO] ------------------------------------------------------------------------- --- [INFO] Using following parameters for creating Archetype: maven-archetype-quicks tart:RELEASE [INFO] ------------------------------------------------------------------------- --- [INFO] Parameter: groupId, Value: com.ibm.devworks [INFO] Parameter: packageName, Value: com.ibm.devworks [INFO] Parameter: basedir, Value: C:\temp\maven [INFO] Parameter: package, Value: com.ibm.devworks [INFO] Parameter: version, Value: 1.0-SNAPSHOT [INFO] Parameter: artifactId, Value: NumOps [INFO] ********************* End of debug info from resources from generated POM *********************** [INFO] Archetype created in dir: C:\temp\maven\NumOps [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESSFUL [INFO] ------------------------------------------------------------------------ [INFO] Total time: 1 second [INFO] Finished at: Sat Dec 02 22:04:02 EST 2006 [INFO] Final Memory: 4M/8M [INFO] ------------------------------------------------------------------------
Archetype 插件創(chuàng)建了一個(gè)目錄樹、一個(gè) pom.xml 文件和一個(gè)占位符 App.java 應(yīng)用程序文件。它也為單元測(cè)試源碼創(chuàng)建了一棵目錄樹,還創(chuàng)建了一個(gè)占位符 AppTest.java 單元測(cè)試文件。這個(gè)項(xiàng)目已經(jīng)準(zhǔn)備就緒。圖 6 顯示了該 Archetype 插件創(chuàng)建的目錄和文件。
您只需將 NumOps.java、Operation.java 和 AddOps.java 文件移到 App.java 的位置,并移除 App.java。在下一部分中,將做出一些改變來定制生成的 pom.xm。
定制 POM
Maven 2 通過該 pom.xml 文件了解您的項(xiàng)目。該文件由 Archetype 按照 NumOps 生成,如清單 8 所示:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.ibm.devworks</groupId> <artifactId>NumOps</artifactId> <packaging>jar</packaging> <version>1.0-SNAPSHOT</version> <name>Maven Quick Start Archetype</name> <url>http://maven.apache.org</url> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> </dependencies> </project>
請(qǐng)注意:在測(cè)試階段中(通過 <scope> 標(biāo)記),Archetype 如何定義模塊的坐標(biāo)、如何將類型定義為 JAR 歸檔以及如何將 JUnit 指定為一個(gè)依賴項(xiàng)。要為您的新項(xiàng)目定制這個(gè) pom.xml 文件,請(qǐng)參照清單 9 中突出顯示的部分,稍作改動(dòng)。
<scope>
<project xmlns=http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.ibm.devworks</groupId> <artifactId>NumOps</artifactId> <packaging>jar</packaging> <version>1.0-SNAPSHOT</version> <name>Intro to Maven 2 Example 1</name> <url>http://www.ibm.com/java</url> <build> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.5</source> <target>1.5</target> </configuration> </plugin> </plugins> </build> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> </dependencies> </project>
額外的 <build> 標(biāo)記是必要的,用來覆蓋源代碼,以達(dá)到 Java 代碼的水平。默認(rèn)情況下,采用的是 JDK 1.4,但您的代碼使用了泛型,因而需要 JDK 5.0 編譯。
<build>
編譯定制的項(xiàng)目
現(xiàn)在可以使用 mvn compile 命令編譯 NumOps 項(xiàng)目。 這個(gè)命令使 Maven 2 引擎從構(gòu)建生命周期一直運(yùn)行到編譯階段,并執(zhí)行相應(yīng)的 mojo。您應(yīng)該看到構(gòu)建成功的報(bào)告,報(bào)告中在目標(biāo)目錄樹里創(chuàng)建了三個(gè)類文件(如清單 10 所示)。如果這是第一次運(yùn)行,那會(huì)花點(diǎn)時(shí)間,因?yàn)橐恍┮蕾図?xiàng)需要經(jīng)過 Internet 從中央存儲(chǔ)庫下載。
mvn compile
[INFO] Scanning for projects... [INFO] ------------------------------------------------------------------------- --- [INFO] Building Intro to Maven 2 Example 1 [INFO] task-segment: [compile] [INFO] ------------------------------------------------------------------------- --- [INFO] [resources:resources] [INFO] Using default encoding to copy filtered resources. [INFO] [compiler:compile] Compiling 3 source files to C:\temp\maven\NumOps\target\classes [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESSFUL [INFO] ------------------------------------------------------------------------ [INFO] Total time: 1 second [INFO] Finished at: Sat Dec 02 22:52:16 EST 2006 [INFO] Final Memory: 3M/7M [INFO] ------------------------------------------------------------------------
添加單元測(cè)試
項(xiàng)目開發(fā)的最佳實(shí)踐通常要求對(duì)所有代碼模塊進(jìn)行單元測(cè)試。Maven 2 為您創(chuàng)建了一個(gè)占位符 AppTest.java 單元測(cè)試文件。現(xiàn)在,將文件名改為 NumOpsTest.java,請(qǐng)參照清單 11 中突出顯示的部分,對(duì)生成的單元測(cè)試做出改動(dòng)。也可以從源代碼下載處復(fù)制單元測(cè)試的源代碼(參見 下載)。
package com.ibm.devworks; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; /** * Unit test for simple App. */ public class NumOpsTest extends TestCase { /** * Create the test case * * @param testName name of the test case */ public NumOpsTest( String testName ) { super( testName ); } ... public void testNumOps() { NumOps nops = new NumOps(); assertTrue( nops.size() == 1); assertTrue( nops.getOp(0).getDesc().equals("plus")); assertTrue( nops.getOp(0).op(2,1) == 3); } }
現(xiàn)在可以使用 mvn test 命令運(yùn)行所有的 mojo 一直到測(cè)試階段。
mvn test
Maven 2 編譯源碼和單元測(cè)試。然后運(yùn)行測(cè)試,同時(shí)報(bào)告成功、失敗和錯(cuò)誤的數(shù)目,如清單 12 所示:
[INFO] Scanning for projects... [INFO] ------------------------------------------------------------------------- --- [INFO] Building Intro to Maven 2 Example 1 [INFO] task-segment: [test] [INFO] ------------------------------------------------------------------------- --- [INFO] [resources:resources] [INFO] Using default encoding to copy filtered resources. [INFO] [compiler:compile] [INFO] Nothing to compile - all classes are up to date [INFO] [resources:testResources] [INFO] Using default encoding to copy filtered resources. [INFO] [compiler:testCompile] Compiling 1 source file to C:\temp\maven\NumOps\target\test-classes [INFO] [surefire:test] [INFO] Surefire report directory: C:\temp\maven\NumOps\target\surefire-reports ------------------------------------------------------- T E S T S ------------------------------------------------------- Running com.ibm.devworks.NumOpsTest Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.031 sec Results : Tests run: 1, Failures: 0, Errors: 0, Skipped: 0 [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESSFUL [INFO] ------------------------------------------------------------------------ [INFO] Total time: 2 seconds [INFO] Finished at: Sat Dec 02 23:04:27 EST 2006 [INFO] Final Memory: 3M/6M [INFO] ------------------------------------------------------------------------
實(shí)踐 Maven 2:參與多個(gè)項(xiàng)目構(gòu)建
使用 Maven 2 構(gòu)建和測(cè)試簡單項(xiàng)目是很直接的。本部分用第二個(gè)例子來展示更現(xiàn)實(shí)也更為通用的多模塊項(xiàng)目。
擴(kuò)展 NumOps 樣例
NumOps 的例子將在此處的第二個(gè)例子里得到擴(kuò)展。添加一個(gè)新 SubOps 類來支持減法,添加一個(gè)新 MulOps 類來支持乘法。
SubOps
MulOps
但 Operation 接口和 AddOps 類現(xiàn)在己從 NumOps 項(xiàng)目中移走。相反,它們和新的 SubOps 和 MulOps 類一起放到了一個(gè)叫做 OpsImp 的新項(xiàng)目中。圖 7 顯示了 NumOps 和 OpsImp 項(xiàng)目間的這種關(guān)系:
在大型軟件開發(fā)項(xiàng)目中,子項(xiàng)目和子模塊之間常存在依賴性。您可以將這里的這項(xiàng)技術(shù)應(yīng)用到有著多個(gè)相互依賴項(xiàng)的任何多模塊 Maven 項(xiàng)目中。
清單 13 里的 SubOps 在編碼上和 AddOps 類似。這里沒有顯示的 MulOps 也類似;您可以看一下隨附的代碼來了解詳情(參見 下載)。
package com.ibm.devworks; public class SubOps implements Operation { public int op(int a, int b) { return a-b; } public String getDesc() { return "minus"; } }
現(xiàn)在修改了 NumOps 的構(gòu)造函數(shù)來創(chuàng)建一個(gè) SubOps 實(shí)例和一個(gè) MulOps 實(shí)例。參見隨附的源代碼獲取詳情。
創(chuàng)建主項(xiàng)目
為和這兩個(gè)項(xiàng)目一起運(yùn)行,主項(xiàng)目創(chuàng)建在比 NumOps 和 OpsImp 的項(xiàng)目目錄高一級(jí)的目錄。NumOps 和 OpsImp 項(xiàng)目都使用標(biāo)準(zhǔn) Maven 項(xiàng)目目錄布局。在最頂部,項(xiàng)目目錄只包含一個(gè) pom.xml 文件。圖 8 顯示了新的子目錄結(jié)構(gòu),緊跟在主目錄之下:
可以在分發(fā)代碼的 example2 子目錄中找到這個(gè)多模塊項(xiàng)目的代碼(參見 下載)。清單 14 顯示了頂層的 pom.xml 文件:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.ibm.devworks</groupId> <artifactId>mavenex2</artifactId> <packaging>pom</packaging> <version>1.0-SNAPSHOT</version> <name>Maven Example 2</name> <url>http://maven.apache.org</url> <modules> <module>NumOps</module> <module>OpsImp</module> </modules> <build> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.5</source> <target>1.5</target> </configuration> </plugin> </plugins> </build> <dependencyManagement> <dependencies> <dependency> <groupId>com.ibm.devworks</groupId> <artifactId>OpsImp</artifactId> <version>${project.version}</version> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> </dependencies> </project>
新代碼以粗體突出顯示。首先,這個(gè)主項(xiàng)目的工件 ID 是 mavenex2,其打包類型是 pom。這向 Maven 2 傳遞出這樣的信號(hào):這是一個(gè)多模塊項(xiàng)目。
mavenex2
pom
隨后,<modules> 標(biāo)記指定組成此項(xiàng)目的兩個(gè)模塊:NumOps 和 OpsImp。
<modules>
這個(gè)主項(xiàng)目的子模塊能從這個(gè) pom.xml 文件中繼承屬性。說得更具體一點(diǎn),這些子模塊都不需要將 JUnit 聲明為一個(gè)依賴項(xiàng),即使它們都包含單元測(cè)試。這是因?yàn)樗鼈兝^承了頂層定義的 JUnit 依賴項(xiàng)。
<dependencyManagement> 標(biāo)記不指定此模塊依賴的依賴項(xiàng)。相反,它主要由子模塊使用。子模塊能指定 <dependencyManagement> 標(biāo)記中任何條目的依賴項(xiàng),而無需指定具體的版本號(hào)。當(dāng)項(xiàng)目樹更改依賴項(xiàng)的版本號(hào)時(shí),這很有用,可以使需編輯的條目數(shù)目最小化。在本例中,OpsImp 項(xiàng)目的版本號(hào)是使用 ${project.version} 指定的。在執(zhí)行 Maven 時(shí),這是一個(gè)會(huì)被相應(yīng)值所填充的參數(shù)。
<dependencyManagement>
${project.version}
從主 POM 中繼承
前進(jìn)到下一層的 OpsImp 目錄,可以找到該模塊的 pom.xml 文件,如清單 15 所示:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <parent> <groupId>com.ibm.devworks</groupId> <artifactId>mavenex2</artifactId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>OpsImp</artifactId> <packaging>jar</packaging> </project>
<parent> 元素指定此模塊所繼承自的主 POM。從父模塊中繼承極大地簡化了這個(gè) pom.xml。只需要覆蓋該工件 ID 并打包。此模塊繼承了其父模塊的依賴項(xiàng):JUnit 模塊。
<parent>
NumOps pom.xml 也從其父模塊中繼承,也相當(dāng)簡單。這個(gè) pom.xml 顯示在清單 16 中:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <parent> <groupId>com.ibm.devworks</groupId> <artifactId>mavenex2</artifactId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>NumOps</artifactId> <packaging>jar</packaging> <dependencies> <dependency> <groupId>com.ibm.devworks</groupId> <artifactId>OpsImp</artifactId> </dependency> </dependencies> </project>
mvn help:effective-pom
NumOps POM 中有意思的一處是將 OpsImp 項(xiàng)目指定為一個(gè)依賴項(xiàng)。請(qǐng)注意,在這個(gè)依賴項(xiàng)中沒有指定任何版本號(hào)。在它的父項(xiàng)目的 <dependencyManagement> 元素里已經(jīng)指定了首選的版本號(hào)。
在項(xiàng)目頂層,現(xiàn)在可以用 mvn compile 命令編譯全部兩個(gè)模塊,或用 mvn test 命令為兩個(gè)模塊運(yùn)行單元測(cè)試。也可以運(yùn)行 mvn install 將打包的模塊安裝到本地目錄中。這使得任何依賴于它的模塊可以不訪問源代碼就能解析依賴項(xiàng)。
mvn install
在 Eclipse 3.2 中安裝 Maven 2.x Plug-in
如果您在日常開發(fā)中使用 Eclipse IDE,那么您應(yīng)該在 Eclipse 中下載和安裝 Maven 2.x Plug-in。這個(gè)插件有助于在 Eclipse IDE 中操作 Maven 項(xiàng)目。參見 參考資料 獲取有關(guān)此插件的項(xiàng)目信息。
可以使用 Eclipse 的軟件更新向?qū)г?Eclipse 下安裝 Maven 2.X Plug-in:
在 Eclipse 3.2 中使用 Maven 2 Plug-in
本節(jié)涵蓋了 Eclipse 中 Maven 2.x Plug-in 的一些常用功能。
在您的項(xiàng)目可以使用 Maven 2 插件功能前,需要在一個(gè) Eclipse 項(xiàng)目中啟用 Maven 屬性。右鍵單擊您想要向其中添加 Maven 支持的項(xiàng)目,選擇 Maven2>Enable。
為確保項(xiàng)目的目錄結(jié)構(gòu)符合 Maven 的預(yù)期,您應(yīng)先創(chuàng)建 Maven 目錄結(jié)構(gòu)(或者手動(dòng)或者使用 Archetype),然后將該項(xiàng)目添加到 Eclipse 中。
在存儲(chǔ)庫中實(shí)時(shí)搜索依賴項(xiàng)
使用插件將依賴項(xiàng)添加到 pom.xml 很簡單。右鍵單擊該項(xiàng)目的 pom.xml,然后選擇 Maven2>Add Dependency。這會(huì)啟動(dòng) Repository Search 向?qū)АfI入您要尋找的依賴項(xiàng)名稱開頭的一些字母,該向?qū)?huì)在中央存儲(chǔ)庫中搜索任何相匹配的工件。相匹配的工件的所有詳情都會(huì)呈現(xiàn)給您,以幫助您選擇依賴項(xiàng)。圖 11 顯示了搜索 JUnit 工件的結(jié)果:
一旦選定了想要的工件的版本,單擊 OK,插件就會(huì)自動(dòng)地將一個(gè)新的 <dependency> 元素添加到 pom.xml 中。
<dependency>
調(diào)用 Maven 構(gòu)建
一個(gè)構(gòu)建涉及到能從 Eclipse 中啟動(dòng)的生命周期的任何階段。首先,確保啟用了 Maven 的項(xiàng)目當(dāng)前是打開的。然后,從 Eclipse 菜單中選擇 Run>External Tools>External Tools...。這將顯示 External Tools 向?qū)В鐖D 12 所示:
給這個(gè)配置文件起名,然后通過單擊 Goals... 按鈕選擇一個(gè)生命周期階段。單擊 Run 運(yùn)行 Maven。
Maven 輸出顯示在 Eclipse 的 Console 選項(xiàng)卡中。
結(jié)束語
在本教程中,您:
隨著軟件開發(fā)協(xié)作的發(fā)展,Maven 也會(huì)不斷發(fā)展和改變來適應(yīng)這種需求。作為大多數(shù)大型開源項(xiàng)目的骨干構(gòu)建工具,它一定能從開發(fā)者社區(qū)不斷提供的改進(jìn)意見中獲益。
一旦理解了 Maven 2 的動(dòng)機(jī)和它想要戰(zhàn)勝的挑戰(zhàn),了解 Maven 2 就不困難了。作為構(gòu)建工具,Maven 2 高效可用,即使是一個(gè)十足的新手在一個(gè)與世隔絕的地方開發(fā)項(xiàng)目,他也會(huì)深感 Maven 2 的這一特性。開始在您自己的開發(fā)項(xiàng)目中使用 Maven 2 并參加一個(gè)開源開發(fā)社區(qū)吧,很快,您也將能夠?qū)?Maven 2 的發(fā)展方向和進(jìn)程有所影響。
下載
參考資料
關(guān)于作者
Sing Li 是一位顧問和多產(chǎn)的作家,具有二十多年的行業(yè)工作經(jīng)驗(yàn)。他編寫了 Professional Apache Geronimo、Beginning JavaServer Pages、Professional Apache Tomcat 5、Pro JSP - Third Edition、Early Adopter JXTA、Professional Jini、Beginning J2ME: From Novice to Professional, Third Edition、Professional Apache Geronimo 和許多其他書籍。Sing 也為技術(shù)雜志供稿,還參加開源社區(qū)。他是開源、VOIP 和 P2P 運(yùn)動(dòng)的積極倡導(dǎo)者。可以通過 westmakaha@yahoo.com 和 Sing 聯(lián)系。
posts - 5, comments - 10, trackbacks - 0, articles - 23
Copyright © 李春生