Apache Maven 2 簡介


https://www.ibm.com/developerworks/cn/education/java/j-mavenv2/j-mavenv2.html


Comments
 
2

開始之前

現代軟件開發基於健壯的企業級開源技術,它需要一類新的構建工具和項目協作工具。Apache Maven 2 的核心引擎旨在簡化往往十分複雜的大型協作性軟件項目的構建和管理。在大型項目團隊環境中工作是種挑戰,而 Maven 2 以友好爲設計目的,即便對那些不熟悉這種挑戰的開發人員而言,Maven 2 也足夠友好。本教程首先圍繞單個的新手開發展開,然後逐漸介紹 Maven 2 中的一些協作性的概念和功能。我們鼓勵您在本教程提供的介紹之上,探究本文範圍之外的 Maven 2 的一些高級功能。

關於本教程

本教程將循序漸進地向您介紹有關 Maven 2 的基礎概念和實踐練習,具體內容包括:

  • Maven 2 概覽
  • 理解 Maven 2 依賴項管理模型
  • Maven 2 存儲庫和 Maven 2 座標
  • Maven 2 生命週期、階段、插件和 mojo
  • 下載和安裝 Maven 2
  • 實踐 Maven 2 —— 您的第一個 Maven 2 項目
  • 定製項目對象模型(POM)
  • 參與多個項目
  • 實踐 Maven 2 —— 參與多個項目構建
  • 在 Eclipse 3.2 中安裝 Maven 2.x Plug-in
  • 在 Eclipse 3.2 中使用 Maven 2.x Plug-in

讀完本教程,您會理解並欣賞 Maven 2 背後的設計理念。也將進一步熟悉在使用 Maven 2 構建的項目中工作所需的基本技能。這是一張通往 Apache 和 Codehaus 社區中大多數大型項目的通行證。最爲重要的是,您將可以把 Maven 2 應用到日常的項目構建和管理活動中。

先決條件

您應當大體上熟悉 Java™ 開發。本教程假設您理解構建工具的價值和基本操作,包括依賴項管理和輸出打包。要求您能將 Eclipse 3.2 用作 IDE,從而在 Eclipse 中使用 Maven 2.x 插件。如果您熟悉大型開源項目(如 Apache 軟件基金會管理下的項目),那就太好了。理解 Java 5 編碼(包括泛型)會很有幫助。如果有各種項目構建技術的經驗(如 Ant、autoconfmake 和 nmake)也很好,但不做強制性要求。

系統需求

爲繼續學習和試驗本教程中的代碼,您需要有效安裝 Sun's JDK 1.5.0_09 (或其後的版本)或 IBM JDK 1.5.0 SR3

針對教程中關於 Eclipse 的 Maven 2.x 的插件部分,需要有效安裝 Eclipse 3.2.1 或其後版本。

本教程的推薦系統配置如下:

  • 支持上述 JDK/JRE 的系統,有至少 1GB 的主存
  • 有至少 20MB 的磁盤空間來安裝軟件組件和樣例

本教程中的說明基於 Microsoft Windows 操作系統。教程中涵蓋的所有工具也可以在 Linux® 和 UNIX® 系統上運行。

Maven 2 概覽

Maven 是一個頂級的 Apache Software Foundation 開源項目,創建它最初是爲了管理 Jakarta Turbine 項目複雜的構建過程。從那以後,不論是開源開發項目還是私有開發項目都選擇 Maven 作爲項目構建系統。Maven 快速地發展着,如今已是第二版,Maven 已經從針對單個複雜項目的定製構建工具成長爲廣泛使用的構建管理系統,其豐富的功能可以應用於大多數的軟件開發場景。

概括來講,Maven 2 能夠:

  • 理解項目通常是如何構建的。
  • 利用其內嵌的項目知識簡化和便利項目構建。
  • 利用其內嵌的項目知識來幫助用戶理解複雜的項目結構和構建過程中潛在的變數。
  • 設計並實現一個經證實的依賴項管理系統,該系統順應了當今全球化和相互聯繫的項目團隊的需求。
  • 利用其內部知識,針對簡單項目提供簡單的用戶體驗。
  • 對於高級用戶來說相當靈活;針對特別的應用場景,可以覆蓋其內嵌模型,也可以通過配置、部署元數據或創建自定義插件對其內嵌模型進行改寫。
  • 全面擴展現有行爲之外的場景細節。
  • 捕獲新出現的最佳實踐和各個用戶社區間的經確認的共同性,並將它們納入到 Maven 的內嵌項目知識中,從而不斷地得到改進。

Maven 2 —— 概念性的概覽

爲捕捉項目構建知識,Maven 2 依賴於一套發展中的有關事物如何運轉的概念性模型。部分模型被硬編碼爲 Maven 代碼庫的一部分,這些模型通過新的 Maven 發佈版本不斷得到精煉。圖 1 解釋了 Maven 2 的關鍵模型:

圖 1. Maven 2 對象和操作模型
Maven 2 對象和操作模型

圖 1 中的關鍵組件爲:

  • 項目對象模型(POM): POM 是 Maven 2 中的里程碑式的模型。該模型的一部分已經構建到 Maven 引擎(被親切地稱爲反應堆 )中,其餘部分則通過一個名叫 pom.xml 的基於 XML 的元數據文件來聲明。
  • 依賴項管理模型: Maven 對如何管理項目的依賴項很在行。依賴項管理是一片灰色地帶,典型的構建-管理工具和系統都未明確涉及。Maven 2 構建了 Maven 依賴項管理模型,該模型能夠適應大多數需求。這個模型被證明是有效而高產的模型,目前,主要的開源項目都部署了該模型。
  • 構建生命週期和階段:和 POM 相關的概念是構建生命週期 和階段。這是 Maven 2 的內嵌概念模型和現實物理世界之間的接口。使用 Maven 時,工作主要是通過插件來執行的。在構建週期中,Maven 2 通過遵循一系列定義好的階段,將這些插件協調起來。

如果您對其中一些概念還有點模糊,請不要擔心。接下來的部分會用具體的例子來鞏固這些模型背後的概念。

Maven 2 —— 物理概覽

圖 2 揭示了 Maven 2 的操作方式和與之交互的方式,同時顯示了它的物理構成。圖 2 提供了有關如何與 Maven 進行交互的概覽:

圖 2. Maven 2 操作和交互模型
Maven 2 操作和交互模型

圖 2 中,POM 是 Maven 對您的特定項目的理解。這個模型由包含在一系列 pom.xml 文件中的聲明性描述構成。這些 pom.xml 文件構成一棵樹,每個文件能從其父文件中繼承屬性。Maven 2 提供一個 Super POM。這個 Super POM 位於層級樹的頂端,它包含所有項目的默認通用屬性;每個項目的 POM 都從這個 Super POM 處繼承。

依賴項被指定爲 pom.xml 文件的一部分。Maven 根據其依賴項管理模型解析項目依賴項。Maven 2 在本地存儲庫和全球存儲庫尋找依賴性組件(在 Maven 術語裏稱作工件 )。在遠程存儲庫中解析的工件被下載到本地存儲庫中,以便使接下來的訪問可以有效進行。Maven 2 中的這個依賴項解析器可以處理可遞依賴項。即,它能有效地解析您的依賴項所依賴的那些依賴項。

Maven 引擎通過插件 親自執行幾乎所有的文件處理任務。插件被配置和描述在 pom.xml 文件中。依賴項管理系統將插件當作工件來處理,並根據構建任務的需要來下載插件。每個插件都能和生命週期中的不同階段聯繫起來。Maven 引擎有一個狀態機,它運行在生命週期的各個階段,在必要的時候調用插件。

理解 Maven 2 依賴項管理模型

在有效利用 Maven 2 之前,您需要理解 Maven 2 依賴項管理模型是如何運行的。

依賴項開發適應於這樣的項目,其軟件組件(稱作模塊 )是由不同的項目團隊開發的。它支持持續獨立開發,也支持對所有依賴模塊進行精煉。

這個團隊協作場景在通過 Internet 建立和維護的開源項目中十分常見,由於內部開發大受開源或外包世界的衝擊和影響,這種場景在合作開發的圈子裏日益盛行。

解析項目依賴項

Maven 2 依賴項管理引擎幫助解析構建過程中的項目依賴項。

實踐中,依賴項在 pom.xml 文件內的 <dependencies> 元素中指定,並作爲 POM 的一部分注入到 Maven 中。

項目依賴項存儲在存儲庫服務器(在 Maven 術語中簡單地稱之爲存儲庫)上。要成功的解析依賴項,需要從包含該工件的存儲庫裏找到所需的依賴性工件。

基於 POM 中的項目依賴項信息,該依賴項解析器試圖以下列方式解析依賴項:

  1. 檢查本地存儲庫中的依賴項。
  2. 檢查遠程存儲庫列表中的依賴項。
  3. 如果前兩步失敗,則報告一個錯誤。

默認情況下,第二步中所涉及的第一個遠程存儲庫是一個能在全球訪問的集中式 Maven 2 存儲庫,它包含了最流行的開源項目的一些工件。在內部開發中,可以設置額外的遠程存儲庫來包含從內部開發模塊中發佈的工件。可以使用 settings.xml 中的 <repositories> 元素來配置這些額外的遠程存儲庫。

確保單個的工件

將 Maven 2 用於項目構建時,依賴項解析通過一個集中的存儲庫確保只存在一個依賴性工件,而不考慮有多少項目或子項目引用該工件。這是多模塊項目構建中一個重要的屬性,因爲包含多個工件會導致一些項目一致性和集成方面的問題。

存儲庫和座標

Maven 2 存儲庫存儲 Maven 在一個項目的依賴項解析中使用過的工件集。在本地磁盤上訪問本地存儲庫,通過網絡訪問遠程存儲庫。

工件通常被打包成包含二進制庫或可執行庫的 JAR 文件。這被認爲是工件的一個類型。但在實踐中,工件也可以是 WAR、EAR 或其他代碼捆綁類型。

Maven 2 利用操作系統的目錄結構對存儲在存儲庫中的工件集進行快速索引。這個存儲庫索引系統依賴於這種能力來通過工件的座標 惟一標識工件。

Maven 座標

Maven 座標是一組可以惟一標識工件的三元組值。座標包含了下列三條信息:

  • 組 ID:代表製造該工件的實體或組織。例如,com.ibm.devworks 就是一個組 ID。
  • 工件 ID:實際的工件的名稱。例如,主體類名爲 OpsImp 的項目也許會用 OpsImp 作爲其工件 ID。
  • 版本:該工件的版本號。支持的格式爲 mmm.nnn.bbb-qqqqqqq-dd,其中, mmm 是主版本號,nnn 是次版本號,bbb 代表其 bug 修復水平。qqqqq(限定詞)或 dd(構建號)也能添加到版本號中,這兩項是可選項。

對 Maven 座標的使用貫穿於 Maven 配置文件和 POM 文件中。例如,要在命名爲 OpsImp 的模塊上指定項目依賴項(在 1.0-SNAPSHOT 級別),pom.xml 文件應包含清單 1 所示的部分:

清單 1. OpsImp 樣例模塊的 Maven 座標
1
2
3
4
5
6
7
< dependencies >
    < dependency >
       < groupId >com.ibm.devworks</ groupId >
       < artifactId >OpsImp</ artifactId >
       < version >1.0-SNAPSHOT</ version >
    </ dependency >
</ dependencies >

特別限定詞 SNAPSHOT 告訴 Maven 2:該項目或模塊還處於開發狀態中,它應該獲取最新版的可用工件。

要將該項目指定爲依賴於 JUnit 進行單元測試,可以將 JUnit 3.8.1 的座標作爲一個依賴項添加到該項目的 pom.xml 文件中,如清單 2 所示:

清單 2. JUnit 依賴項的 Maven 座標
1
2
3
4
5
6
7
< dependencies >
    < dependency >
       < groupId >junit</ groupId >
       < artifactId >junit</ artifactId >
       < version >3.8.1</ version >
    </ dependency >
</ dependencies >

深入 Maven 存儲庫

由於 Maven 存儲庫是普通的目錄樹,所以可以很容易地看到工件是如何存儲到磁盤上的。圖 3 是本地存儲庫的一部分,顯示了 JUnit 3.8.1 工件的位置:

圖 3. Maven 2 存儲庫內幕
Maven 2 存儲庫內幕

從圖 3 中可以看出,Maven 維護了一個工件的 POM 文件,同時也爲該工件和其存儲庫中的 POM 維護了檢驗和散列。當工件在存儲庫間轉移時,這些文件幫助確保工件的完整性。該工件已由 Maven 的依賴項管理引擎從中央存儲庫下載並放置到本地存儲庫中。

在圖 4 中,座標爲 com.ibm.devworks/OpsImp/1.0-SNAPSHOT 的工件顯示在本地存儲庫中。該工件和 POM 文件一起存放在存儲庫。在本例中,該工件在本地安裝。

圖 4. 本地存儲庫中的 OpsImp 工件
本地存儲庫中的 OpsImp 工件

Maven 2 生命週期、階段、插件和 mojo

Maven 通過插件動作完成大多數構建任務。可以把 Maven 引擎認爲是插件動作的協調器。

插件中的 Mojo

插件是適應 Maven 的插件框架的軟件模塊。現在,可以使用 Java、Ant 或 Beanshell 來創建自定義插件。插件中的每個任務稱作一個 mojo。有時,插件也被視爲一套相關的 mojo。創建自定義的 Maven 2 插件超出了本教程的範圍;參見 參考資料,以獲取更多信息。

Maven 2 是預先打包好的,以便於下載,它和許多常用插件一起使用。大多數典型開發任務不需要使用額外插件。

在開始編寫自己的插件前,您應該先參考一下列出流行 Maven 2 插件的 Web 站點(參見 參考資料),看一下您需要的插件是不是已經有了。圖 5 顯示了 Maven Plugin Matrix(參見 參考資料),它提供了許多可用插件的兼容性信息:

圖 5. Maven Plugin Matrix
 Maven Plugin Matrix

將 mojo 綁定到生命週期各階段

Maven 引擎在執行構建生命週期中相應的階段時,執行插件中的 mojo(構建任務)。插件的 mojo 和生命週期中的階段間的關聯叫做綁定 。插件開發人員能夠靈活地將一個或多個生命週期階段和一個插件關聯起來。

默認的生命週期的各階段

Maven 對構建生命週期的固定理解包含了許多不同的階段。表 1 簡短地描述了各個階段:

表 1. Maven 2 默認生命週期的各階段

Maven 從開源社區中汲取了十多年的項目構建管理經驗。很難找到一個構建週期不符合表 1 中的生命週期階段的軟件項目。

啓動 Maven 2 引擎後,它會按順序經歷表 1 中的各階段,執行可能與該階段綁定的 mojo。每個 mojo 則可以使用 Maven 2 豐富的 POM 支持、依賴項管理,也可以訪問執行這一專門任務時的構建狀態信息。

調用 Maven 2 引擎時,可以將一個生命週期階段指定爲命令行參數。該引擎一直執行到指定的階段(包括該指定的階段)。包含的階段中所有的 mojo 都會被觸發。

簡短地說,這就是 Maven 2 的操作原理。在下一部分裏,您將直接面對操作。對 Maven 操作、它的依賴項管理模型和它的 POM 有了基本的理解,您會發現實踐 Maven 2 是一項十分簡單的練習。

下載和安裝 Maven 2

根據下列步驟下載和安裝 Maven 2:

  1. 從 Maven 項目的官方站點下載 Maven 2 二進制文件(參見 參考資料)。
  2. 將二進制發佈文件解壓到選定的目錄。
  3. 將 InstallationDirectory\bin 目錄添加到 PATH 環境變量。

爲檢驗安裝,鍵入 mvn -help 命令。您將看到清單 3 中的幫助頁面。

清單 3. 使用 mvn -help 命令檢驗安裝
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
28
29
30
31
32
33
34
35
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

實踐 Maven 2:您的第一個 Maven 2 項目

在第一個實踐的例子裏,您將看到如何用最小的努力使用 Maven 2 構建簡單項目。Maven 2 內置的關於 Java 項目的知識消除了其他構建工具也許必須要經歷的冗長的配置過程。

處理數值操作的類

該例使用了一個處理數值操作的類。這個主體類的源代碼(叫做 NumOps)如清單 4 所示。

清單 4.NumOps 類
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
28
29
30
31
32
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 類管理一個能夠在兩個整數上執行數值操作的對象集。main 方法創建一個 NumOps 實例,然後調用由 NumOps 管理的每個對象,同時分別調用它的 getDesc() 方法和 op() 方法。由 NumOps 管理的所有對象都實現了在 Operation.java 中定義的 Operation 接口,接口代碼如清單 5 所示:

清單 5. Operation 接口
1
2
3
4
5
6
package com.ibm.devworks;
 
public interface Operation {
    int op(int a, int b);
    String getDesc();
}

在這個初始的例子裏定義的惟一操作是一個 AddOps 類,如清單 6 所示:

清單 6. AddOps 類
1
2
3
4
5
6
7
8
9
10
package com.ibm.devworks;
 
public class AddOps implements Operation {
    public int op(int a, int b) {
       return a+b;
    }
    public String getDesc() {
       return "plus";
    }
}

執行 NumOps 類時,會打印出下列輸出:

1
2 plus 1 is 3

使用 Archetype 來創建初始項目

要創建能用 Maven 構建的簡單 Java 項目所需的一切,可以使用 Archetype 插件,它是 Maven 2 中的標準插件。不同於構建階段插件,Archetype 在 Maven 項目構建生命週期之外運行,用於創建 Maven 項目。在您想要包含 NumOps 項目的目錄中執行下列命令(將所有命令敲在一行):

1
2
mvn archetype:create -DarchetypeGroupId=org.apache.maven.archetypes
   -DgroupId=com.ibm.devworks -DartifactId=NumOps

該命令爲 Archetype 插件提供您模塊的座標:com.ibm.devworks/NumOps/1.0-SNAPSHOT。在此情況下,不需要指定版本,因爲 Archetype 插件常默認爲 1.0-SNAPSHOT。此命令爲項目創建了一個起始的 pom.xml 文件,也創建了規範的 Maven 2 目錄結構。您將在本教程源代碼下載處的 example1 目錄下找到該代碼(參見 下載)。

該輸出應該與清單 7 類似:

清單 7. 使用 Maven Archetype 創建 NumOps 項目
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
28
29
30
31
32
33
34
35
36
[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 插件創建了一個目錄樹、一個 pom.xml 文件和一個佔位符 App.java 應用程序文件。它也爲單元測試源碼創建了一棵目錄樹,還創建了一個佔位符 AppTest.java 單元測試文件。這個項目已經準備就緒。圖 6 顯示了該 Archetype 插件創建的目錄和文件。

圖 6. Archetype 生成的目錄和文件
Archetype 生成的目錄和文件

您只需將 NumOps.java、Operation.java 和 AddOps.java 文件移到 App.java 的位置,並移除 App.java。在下一部分中,將做出一些改變來定製生成的 pom.xm。

定製 POM

Maven 2 通過該 pom.xml 文件瞭解您的項目。該文件由 Archetype 按照 NumOps 生成,如清單 8 所示:

清單 8. Archetype 生成的 POM - pom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
< 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 >

請注意:在測試階段中(通過 <scope> 標記),Archetype 如何定義模塊的座標、如何將類型定義爲 JAR 歸檔以及如何將 JUnit 指定爲一個依賴項。要爲您的新項目定製這個 pom.xml 文件,請參照清單 9 中突出顯示的部分,稍作改動。

清單 9. 爲 NumOps 項目定製生成的 pom.xml 文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
< 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> 標記是必要的,用來覆蓋源代碼,以達到 Java 代碼的水平。默認情況下,採用的是 JDK 1.4,但您的代碼使用了泛型,因而需要 JDK 5.0 編譯。

編譯定製的項目

現在可以使用 mvn compile 命令編譯 NumOps 項目。 這個命令使 Maven 2 引擎從構建生命週期一直運行到編譯階段,並執行相應的 mojo。您應該看到構建成功的報告,報告中在目標目錄樹裏創建了三個類文件(如清單 10 所示)。如果這是第一次運行,那會花點時間,因爲一些依賴項需要經過 Internet 從中央存儲庫下載。

清單 10. NumOps 項目中 mvn 編譯的輸出
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[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] ------------------------------------------------------------------------

添加單元測試

項目開發的最佳實踐通常要求對所有代碼模塊進行單元測試。Maven 2 爲您創建了一個佔位符 AppTest.java 單元測試文件。現在,將文件名改爲 NumOpsTest.java,請參照清單 11 中突出顯示的部分,對生成的單元測試做出改動。也可以從源代碼下載處複製單元測試的源代碼(參見 下載)。

清單 11. 爲項目添加 NumOpsTest 單元測試
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
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);    }
}

現在可以使用 mvn test 命令運行所有的 mojo 一直到測試階段。

Maven 2 編譯源碼和單元測試。然後運行測試,同時報告成功、失敗和錯誤的數目,如清單 12 所示:

清單 12. 執行 mvn 測試來編譯項目和運行單元測試
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
28
29
30
31
32
33
34
[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] ------------------------------------------------------------------------

實踐 Maven 2:參與多個項目構建

使用 Maven 2 構建和測試簡單項目是很直接的。本部分用第二個例子來展示更現實也更爲通用的多模塊項目。

擴展 NumOps 樣例

NumOps 的例子將在此處的第二個例子裏得到擴展。添加一個新 SubOps 類來支持減法,添加一個新 MulOps 類來支持乘法。

但 Operation 接口和 AddOps 類現在己從 NumOps 項目中移走。相反,它們和新的 SubOps 和 MulOps 類一起放到了一個叫做 OpsImp 的新項目中。圖 7 顯示了 NumOps 和 OpsImp 項目間的這種關係:

圖 7. NumOps 和 OpsImp 間的關係
NumOps 和 OpsImp 間的關係

在大型軟件開發項目中,子項目和子模塊之間常存在依賴性。您可以將這裏的這項技術應用到有着多個相互依賴項的任何多模塊 Maven 項目中。

清單 13 裏的 SubOps 在編碼上和 AddOps 類似。這裏沒有顯示的 MulOps 也類似;您可以看一下隨附的代碼來了解詳情(參見 下載)。

清單 13. 實現 Operation 接口的新 SubOps 類
1
2
3
4
5
6
7
8
9
10
package com.ibm.devworks;
 
public class SubOps implements Operation {
    public int op(int a, int b) {
       return a-b;
    }
    public String getDesc() {
       return "minus";
    }
}

現在修改了 NumOps 的構造函數來創建一個 SubOps 實例和一個 MulOps 實例。參見隨附的源代碼獲取詳情。

創建主項目

爲和這兩個項目一起運行,主項目創建在比 NumOps 和 OpsImp 的項目目錄高一級的目錄。NumOps 和 OpsImp 項目都使用標準 Maven 項目目錄佈局。在最頂部,項目目錄只包含一個 pom.xml 文件。圖 8 顯示了新的子目錄結構,緊跟在主目錄之下:

圖 8. 多模塊項目的目錄結構
多模塊項目的目錄結構

可以在分發代碼的 example2 子目錄中找到這個多模塊項目的代碼(參見 下載)。清單 14 顯示了頂層的 pom.xml 文件:

清單 14. 多模塊項目頂層的 pom.xml 文件
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
28
29
30
31
32
33
34
35
36
< 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 >

新代碼以粗體突出顯示。首先,這個主項目的工件 ID 是 mavenex2,其打包類型是 pom。這向 Maven 2 傳遞出這樣的信號:這是一個多模塊項目。

隨後,<modules> 標記指定組成此項目的兩個模塊:NumOps 和 OpsImp

這個主項目的子模塊能從這個 pom.xml 文件中繼承屬性。說得更具體一點,這些子模塊都不需要將 JUnit 聲明爲一個依賴項,即使它們都包含單元測試。這是因爲它們繼承了頂層定義的 JUnit 依賴項。

<dependencyManagement> 標記不指定此模塊依賴的依賴項。相反,它主要由子模塊使用。子模塊能指定 <dependencyManagement> 標記中任何條目的依賴項,而無需指定具體的版本號。當項目樹更改依賴項的版本號時,這很有用,可以使需編輯的條目數目最小化。在本例中,OpsImp 項目的版本號是使用 ${project.version} 指定的。在執行 Maven 時,這是一個會被相應值所填充的參數。

從主 POM 中繼承

前進到下一層的 OpsImp 目錄,可以找到該模塊的 pom.xml 文件,如清單 15 所示:

清單 15. 新 OpsImp 項目的 pom.xml 文件
1
2
3
4
5
6
7
8
9
10
11
12
13
< project xmlns = "http://maven.apache.org/POM/4.0.0"
    xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
相關文章
相關標籤/搜索