1. 程式人生 > >Maven 做專案構建

Maven 做專案構建

本文將介紹基於 Apache Maven 3 的專案構建的基本概念和方法。Maven 是一套標準的專案構建和管理工具,使用統一規範的指令碼進行專案構建,簡單易用,摒棄了 Ant 中繁瑣的構建元素,並具有較高的可重用性。讀完本文,您將瞭解 Maven 的基本概念和使用它進行專案構建的基本方法。

簡介

本文將介紹基於 Apache Maven 3 的專案構建的基本概念和方法。Maven 是一套標準的專案構建和管理工具,使用統一規範的指令碼進行專案構建,簡單易用,摒棄了 Ant 中繁瑣的構建元素,並具有較高的可重用性。讀完本文,你將瞭解 Maven 的基本概念和使用它進行專案構建的基本方法。

什麼是 Maven

Maven 是 Apache 組織下的一個跨平臺的專案管理工具,它主要用來幫助實現專案的構建、測試、打包和部署。Maven 提供了標準的軟體生命週期模型和構建模型,通過配置就能對專案進行全面的管理。它的跨平臺性保證了在不同的作業系統上可以使用相同的命令來完成相應的任務。Maven 將構建的過程抽象成一個個的生命週期過程,在不同的階段使用不同的已實現外掛來完成相應的實際工作,這種設計方法極大的避免了設計和指令碼編碼的重複,極大的實現了複用。

Maven vs Ant

Ant 也是 Apache 組織下的一個跨平臺的專案構建工具,它是一個基於任務和依賴的構建系統,是過程式的。開發者需要顯示的指定每一個任務,每個任務包含一組由 XML 編碼的指令,必須在指令中明確告訴 Ant 原始碼在哪裡,結果位元組碼儲存在哪裡,如何將這些位元組碼打包成 JAR 檔案。Ant 沒有生命週期,你必須定義任務和任務之間的依賴,還需要手工定義任務的執行序列和邏輯關係。這就無形中造成了大量的程式碼重複。

Maven 不僅是一個專案構建工具還是一個專案管理工具。它有約定的目錄結構(表 1)和生命週期,專案構建的各階段各任務都由外掛實現,開發者只需遵照約定的目錄結構建立專案,再配置檔案中生命專案的基本元素,Maven 就會按照順序完成整個構建過程。Maven 的這些特性在一定程度上大大減少了程式碼的重複。

表 1. Maven 目錄結構
src/main/java Application/Library sources
src/main/resources Application/Library resources
src/main/filters Resource filter files
src/main/assembly Assembly descriptors
src/main/config Configuration files
src/main/scripts Application/Library scripts
src/main/webapp Web application sources
src/test/java Test sources
src/test/resources Test resources
src/test/filters Test resource filter files
src/site Site
LICENSE.txt Project's license
README.txt Project's readme

Maven 的安裝和基本設定

本文介紹 Windows 平臺上 Maven 的安裝。Maven 3 需要執行在 JDK1.4 以上的版本上。

  1. 首先確保系統中已經安裝 JDK 並正確設定了 Java 環境變數。
  2. 設定環境變數。開啟我的電腦 > 屬性 > 高階 > 環境變數,新建系統變數 Maven_HOME,設定變數值為 Maven 安裝目錄:C:\Maven-3.0。選擇“系統變數”中變數名為“Path”的環境變數,雙擊該變數,把 Maven 安裝路徑中 bin 目錄的絕對路徑,新增到 Path 變數的值中,並使用半形的分號和已有的路徑進行分隔:C:\Maven-3.0\bin。
  3. 配置完成後,在 Windows DOS 視窗中輸入 mvn -v 驗證 Maven 安裝成功與否,如果安裝成功,執行結果會正確顯示版本號等安裝資訊,如圖 1。
圖 1. 安裝資訊
圖 1. 安裝資訊

在 Eclipse3.6 中建立 maven 專案

  • 安裝 M2eclipse 外掛

在 Eclipse 中整合 Maven 外掛能極大的方便建立 Maven project 並對其進行操作。使用以下的步驟來完成 M2eclipse 外掛的安裝:

  1. 在 Eclipse 選單欄中選擇 Help > Install New Software,如圖 2。
    圖 2. 安裝步驟 1
    圖 2. 安裝步驟 1
  2. 在彈出的對話方塊中單擊 add 按鈕,在彈出的 Add Repository 對話方塊的 Name 和 Location 欄位中分別輸入 maven 和 http://m2eclipse.sonatype.org/sites/m2e,如圖 3,點選 OK。
    圖 3. 安裝步驟 2
    圖 3. 安裝步驟 2
  3. 在彈出的資源列表中選擇 M2eclipse,點選 next 按照提示進行安裝,如圖 4。
    圖 4. 安裝步驟 3
    圖 4. 安裝步驟 3
  4. 安裝完成後重啟 Eclipse,開啟 Window > Preferences 檢查 maven 外掛是否安裝成功,如圖 5。
    圖 5. 驗證 Maven 外掛
    圖 5. 驗證 Maven 外掛
  • 建立 Maven Project
  1. 點選 Eclipse 選單欄 File->New->Ohter->Maven,在彈出的對話方塊中選擇 Maven Project,點選下一步,如圖 6。
    圖 6. 建立步驟 1
    圖 6. 建立步驟 1
  2. 選擇 maven-archetype-quickstart,點選下一步,如圖 7。
    圖 7. 建立步驟 2
    圖 7. 建立步驟 2
  3. 在彈出的對話方塊中,填寫 GroupId, ArtfactId,Version 等資訊,他們的具體含義將在後文中介紹,點選 Finish,如圖 8。
    圖 8. 建立步驟 3
    圖 8. 建立步驟 3

通過以上步驟我們生成了一個基本的 Maven Project,開啟 eclipse 中的 Package Explorer,看到它的結構如圖 9 所示。

圖 9. Maven Project 例項
圖 9. Maven Project 例項

專案物件模型 POM-Maven 的靈魂

POM 即 Project Object Module,專案物件模型,在 pom.xml 檔案中定義了專案的基本資訊、原始碼、配置檔案、開發者的資訊和角色、問題追蹤系統、組織資訊、專案授權、專案的 url、以及構建專案所用的外掛,依賴繼承關係。開發人員需按照 maven 定義的規則進行 POM 檔案的編寫,清單 1 為一個 POM 檔案示例。

清單 1. POM 檔案示例
 <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> 
  <! – The Basics – > 
  <groupId> … </groupId> 
  <artifactId> … </artifactId> 
  <version> … </version> 
  <packaging> … </packaging> 
  <dependencies> … </dependencies> 
  <parent> … </parent> 
  <dependencyManagement> … </dependencyManagement> 
  <modules> … </modules> 
  <properties> … </properties> 
  <! – Build Settings – > 
  <build> … </build> 
  <reporting> … </reporting> 
  <! – More Project Information – > 
  <name> … </name> 
  <description> … </description> 
  <url> … </url> 
  <inceptionYear> … </inceptionYear> 
  <licenses> … </licenses> 
  <organization> … </organization> 
  <developers> … </developers> 
  <contributors> … </contributors> 
  <! – Environment Settings – > 
  <issueManagement> … </issueManagement> 
  <ciManagement> … </ciManagement> 
  <mailingLists> … </mailingLists> 
  <scm> … </scm> 
  <prerequisites> … </prerequisites> 
  <repositories> … </repositories> 
  <pluginRepositories> … </pluginRepositories> 
  <distributionManagement> … </distributionManagement> 
  <profiles> … </profiles> 
 </project>

在每個 POM 檔案中都含有的元素是該 project 的座標,它包含三個基本元素。

groupId 定義了專案屬於哪個組,這有助於在大的範圍上區別專案。artifactId 定義了這個專案在組中唯一的 ID。name 是一個使用者友好的專案名稱。

除了專案座標外,modelVersion 指定 POM 模型的版本,version 指明當前專案的版本,packaging 指定了專案釋出時的打包型別。

在下文中提及的外掛,依賴,生命週期等也都有相應的 POM 元素在檔案中有所體現。

Maven 外掛和倉庫

Maven 本質上是一個外掛框架,它的核心並不執行任何具體的構建任務,僅僅定義了抽象的生命週期,所有這些任務都交給外掛來完成的。每個外掛都能完成至少一個任務,每個任務即是一個功能,將這些功能應用在構建過程的不同生命週期中。這樣既能保證拿來即用,又能保證 maven 本身的繁雜和冗餘。

將生命週期的階段與外掛目標相互繫結,就可以在特定的階段完成具體的構建任務。例如清單 2 中的程式碼就是要在 validate 這個階段執行 maven-antrun-plugin 的 run 目標,具體的任務在 <target></target> 元素中定義。

清單 2. 外掛
 <plugins> 
	 <plugin> 
		 <groupId>org.apache.maven.plugins</groupId> 
		 <artifactId>maven-antrun-plugin</artifactId> 
		 <version>1.6</version> 
 <executions> 
 <execution> 
	 <id>version</id> 
    <phase>validate</phase> 
    <configuration> 
 <target> 
    具體任務
 </target> 
 </configuration> 
 <goals> 
 <goal>  run  </goal> 
	 </goals> 
 </execution> 
	 </executions> 
	 </plugin> 
 </plugins>

Maven 專案中的外掛,依賴和專案構建的輸出都可以由 Maven 的座標進行唯一的區分,基於這種機制,Maven 將所有專案的構件檔案放置在一個統一的位置,也就是 Maven 倉庫。所有 Maven 專案可以從同一個 Maven 倉庫中獲取自己所需要的依賴 JAR,這節省了磁碟資源。實際的 Maven 專案中不需要儲存依賴的檔案,只需要在 POM 檔案中生成依賴關係,在構建的時候 Maven 就會自動去倉庫中下載。

在安裝了 Maven 的機器上,會生成一個 ~\.m2\repository 目錄,這個目錄被稱為本地倉庫,當 Maven 查詢需要的依賴時,首先會在本地查詢,如果本地倉庫中存在,則直接使用,否則 Maven 回去遠端倉庫查詢,查詢到後下載到本地進行使用。遠端中央倉庫的地址為 http://repo1.maven.org/。當然還有一些映象倉庫可供使用,有興趣的讀者可以參考 Maven 官方網站的相關介紹

當個人所在的網路無法訪問公共的 Maven 倉庫時,可以在 settings.xml 中設定代理伺服器。開啟 ~\.m2\settings.xml,如果沒有則複製 $Maven_HOME/conf/settings.xml 到此路徑下,加入清單 3 中的程式碼:

清單 3. 代理
 <proxies> 
   <proxy> 
      <active>true</active> 
      <protocol>http</protocol> 
      <host> 代理地址 </host> 
      <port>8080</port> 
      <username> 使用者名稱 </username> 
      <password> 密碼 </password> 
    </proxy> 
 </proxies>

依賴、聚合和繼承

  • 依賴

我們專案中依賴的 Jar 包可以通過依賴的方式引入,通過在 dependencies 元素下新增 dependency 子元素,可以宣告一個或多個依賴。通過控制依賴的範圍,可以指定該依賴在什麼階段有效。Maven 的幾種依賴範圍:

表 2. 依賴範圍
名稱 有效範圍
compile 編譯,測試,執行。預設的依賴範圍。
test 測試,如 Junit。
runtime 執行,如 JDBC。
provided 編譯,測試,如 ServletAPI。
system 編譯,測試,依賴於系統變數。

清單 4 中表示引入對 Junit 的依賴 , 這個依賴關係產生作用的階段是 <scope>test</scope>。

清單 4. 依賴
    <dependency>  
      <groupId> </groupId>   
      <artifactId> </artifactId>   
      <version> </version>   
      <optional>true<optional>  
    </dependency>

依賴是具有傳遞性的,例如 Project A 依賴於 Project B,B 依賴於 C,那麼 B 對 C 的依賴關係也會傳遞給 A,如果我們不需要這種傳遞性依賴,也可以用 <optional> 去除這種依賴的傳遞,如清單 5。

清單 5. 選擇性依賴
 <dependency> 
 <groupId>commons-logging</groupId> 
 <artifactId>commons-logging</artifactId> 
 <version>1.1.1</version> 
 <optional>true<optional> 
 </dependency>

假設第三方的 jar 包中沒有使用 <optional> 來去除某些依賴的傳遞性,那麼可以在當前的 POM 檔案中使用 <exclusions> 元素宣告排除依賴,exclusions 可以包含一個或者多個 exclusion 子元素,因此可以排除一個或者多個傳遞性依賴。如清單 6。

清單 6. 排除依賴
    <dependency>    
         <groupId>org.springframework</groupId>  
         <artifactId>spring-core</artifactId>  
         <exclusions>  
               <exclusion>      
                    <groupId>commons-logging</groupId>          
                    <artifactId>commons-logging</artifactId>  
               </exclusion>  
         </exclusions>  
    </dependency>
  • 聚合

現實中一個專案往往是由多個 project 構成的,在進行構建時,我們當然不想針對多個 project 分別執行多次構建命令,這樣極容易產生遺漏也會大大降低效率。Maven 的聚合功能可以通過一個父模組將所有的要構建模組整合起來,將父模組的打包型別宣告為 POM,通過 <modules> 將各模組集中到父 POM 中。如清單 7,其中 <module></module> 中間的內容為子模組工程名的相對路徑。

清單 7. 聚合
  <modules>    
 <module>../com.dugeng.project1</module> 
 <module>../com.dugeng.project2</module> 
  </modules>

父型別的模組,不需要有原始碼和資原始檔,也就是說,沒有 src/main/java 和 src/test/java 目錄。Maven 會首先解析聚合模組的 POM 檔案,分析要構建的模組,並通過各模組的依賴關係計算出模組的執行順序,根據這個潛在的關係依次構建模組。將各子模組聚合到父模組中後,我們就可以對父模組進行一次構建命令來完成全部模組的構建。

  • 繼承

在面向物件的程式設計中我們學會了繼承的概念,繼承是可重用行即消除重複編碼的行為。Maven 中繼承的用意和麵向物件程式設計中是一致的。與聚合的實現類似,我們通過構建父模組將子模組共用的依賴,外掛等進行統一宣告,在聚合和繼承同時使用時,我們可以用同一個父模組來完成這兩個功能。

例如將 com.dugeng.parent 這個模組宣告為 project1 和 project2 的父模組,那麼我們在 project1 和 2 中用如下程式碼宣告父子關係,如清單 8:

清單 8. 繼承
 <parent> 
  <groupId>com.dugeng.mavenproject</groupId> 
  <artifactId>com.dugeng.parent</artifactId> 
  <version>0.0.1-SNAPSHOT</version> 
  <relativePath>../com.dugeng.parent/pom.xml</relativePath> 
 </parent>

由於父模組只是用來宣告一些可共用的配置和外掛資訊,所以它也像聚合模組一樣只需要包括一個 POM 檔案,其它的專案檔案如 src/main/java 是不需要的。

聚合和繼承存在一些共性和潛在的聯絡,在實際的應用中,經常將聚合模組的父模組和繼承的父模組定義為同一個。

並不是所有的 POM 元素都可以被繼承,表 3 是一個可繼承的元素列表。

表 3. 可繼承元素列表
名稱 描述
groupId 專案組 ID
version 專案版本
description 描述資訊
organization 組織資訊
inceptionYear 創始年份
url 專案的 url 地址
developers 開發者
contributors 貢獻者資訊
distributionManagerment 部署資訊
issueManagement 缺陷跟蹤系統
ciManagement 持續繼承資訊
scm 版本控制資訊
mailingList 郵件列表資訊
properties 自定義的屬性
dependencies 依賴配置
dependencyManagement 依賴管理配置
repositories 倉庫配置
build 原始碼目錄,外掛管理等配置
reporting 報告配置

Maven 屬性

在 POM 檔案中常常需要引用已定義的屬性以降低程式碼的冗餘,提高程式碼的可重用性,這樣不僅能降低程式碼升級的工作量也能提高程式碼的正確率。有些屬性是使用者自定義的,有些屬性是可以直接引用的已定義變數。

Maven 的可用屬性型別可分為 5 種,它們分別是:

  1. 內建屬性。這種屬性跟 Maven Project 自身有關,比如要引入當前 Project 的版本信 息,那麼只需要在使用的位置引用 ${version} 就行了。
  2. Setting 屬性。上文中已經提到 Maven 自身有一個 settings.xml 配置檔案,它裡面含有包括倉庫,代理伺服器等一些配置資訊,利用 ${settings.somename} 就可以得到檔案裡相應元素的值。
  3. POM 屬性。這種屬性對應 POM 檔案中對應元素的值,例如 ${project.groupId} 對應了 <groupId></groupId> 中的值,${project.artifactId} 對應了 <artifactId> </ artifactId > 中的值。
  4. 系統環境變數。可以使用 env.${name} 來獲得相應 name 對應的環境變數的值,例如 ${env.JAVA_HOME} 得到的就是 JAVA_HOME 的環境變數值。
  5. 使用者自定義變數。這種型別的變數是使用最頻繁和廣泛的變數,完全由使用者自己定義。在 POM 檔案中加入 <properties> 元素並將自定義屬性作為其子元素。格式如清單 9。
清單 9. 自定義屬性
<properties>
  <path>../../sourcecode</path>
</properties>

Maven 3 的新特性

Maven 3 在效能和靈活性方面都比 Maven2 有了很大提升,它的新特性總結起來有以下幾點:

1. 相容低版本 Maven,也就是向後相容,因此使用者可以將 Maven2 的專案移植到 Maven3 上來。

2. 效能優化。CPU 利用率更高,記憶體消耗更小,經過優化的 Maven3 比 Maven2 構建速度快出 50% 以上,這對於構建大型專案的開發者來說無疑會節省大量的時間。

3. 在早先的版本中,開發者必須在子模組中指定父版本,當進行程式碼的遷移或升級時,這會帶來額外的維護工作,Maven3.1 將會消除在子模組上指定父版本的需要。

4.Maven3 改善了錯誤報告,它會在錯誤報告中提供指向 Maven Wiki 頁面的連結,這樣開發者可以方便的檢視更全面的錯誤描述和可能的原因。

5. 增加了 Maven Shell,通常我們可以在系統自帶的 console 裡執行 Maven 命令,但是通過自安裝的 Maven Shell 可以提高生成速度,它是一個是 Maven 的命令列介面工具,可以快取解析過的 POM,避免了重複呼叫 Maven 的啟動成本。Maven Shell 不屬於 Maven 發行包的一部分,需要單獨下載。

6. M2Eclipse 實現了 Maven 和 Eclipse 的整合,與一個使用更廣泛的 IDE 進行整合從而為開發者帶來的便利是不言而喻的。

結束語

Maven 有著許多實用的特點,它使用了標準的目錄結構和部署。這就使得開發人員能夠適應不同的專案,並且不用學習任何結構方面新的東西,也不用掌握特殊的指令來構建結構。當然,Maven 的使用還不夠普及,相信隨著時間的推移,它的功能會更完善,使用的人群也會越來越廣泛。