天天看點

Maven入門--概念與執行個體

關鍵名詞

    Project:任何您想build的事物,Maven都可以認為它們是工程。這些工程被定義為工程對象模型(POM,Poject Object Model)。一個工程可以依賴其它的工程;一個工程也可以由多個子工程構成。

    POM:POM(pom.xml)是Maven的核心檔案,它是訓示Maven如何工作的中繼資料檔案,類似于Ant中的build.xml檔案。POM檔案位于每個工程的根目錄中。

    GroupId:groupId是一個工程的在全局中唯一的辨別符,一般地,它就是工程名。groupId有利于使用一個完全的包名,将一個工程從其它有類似名稱的工程裡差別出來。

    Artifact:artifact是工程将要産生或需要使用的檔案,它可以是jar檔案,源檔案,二進制檔案,war檔案,甚至是pom檔案。每個artifact都由groupId和artifactId組合的辨別符唯一識别。需要被使用(依賴)的artifact都要放在倉庫(見Repository)中,否則Maven無法找到(識别)它們。

    Dependency:為了能夠build或運作,一個典型的Java工程會依賴其它的包。在Maven中,這些被依賴的包就被稱為dependency。dependency一般是其它工程的artifact。

    Plug-in:Maven是由插件組織的,它的每一個功能都是由插件提供的。插件提供goal(類似于Ant中的target),并根據在POM中找到的中繼資料去完成工作。主要的Maven插件要是由Java寫成的,但它也支援用Beanshell或Ant腳本寫成的插件。

    Repository:倉庫用于存放artifact,它可以是本地倉庫,也可以是遠端倉庫。Maven有一個預設的遠端倉庫--central,可以從http://www.ibiblio.org/maven2/下載下傳其中的artifact。在Windows平台上,本地倉庫的預設位址是User_Home\.m2\repository。

    Snapshot:工程中可以(也應該)有一個特殊版本,它的版本号包括SNAPSHOT字樣。該版本可以告訴Maven,該工程正處于開發階段,會經常更新(但還未釋出)。當其它工程使用此類型版本的artifact時,Maven會在倉庫中尋找該artifact的最新版本,并自動下載下傳、使用該最新版。

2 Maven Build Life Cycle

    軟體項目一般都有相似的開發過程:準備,編譯,測試,打包和部署,Maven将上述過程稱為Build Life Cycle。在Maven中,這些生命周期由一系列的短語組成,每個短語對應着一個(或多個)操作;或對應着一個(或多個)goal(類似于Ant中的target)。

    如編譯源檔案的指令mvn compile中的compile是一個生命周期短語。同時該指令也可以等價于mvn compiler:compile,其中的compiler是一個插件,它提供了compile(此compile與mvn compile中的compile意義不同)goal;compiler還可提供另一個goal--testCompile,該goal用于編譯junit測試類。

    在執行某一個生命周期時,Maven會首先執行該生命周期之前的其它周期。如要執行compile,那麼将首先執行validate,generate-source,process-source和generate-resources,最後再執行compile本身。關于Maven中預設的生命周期短語,請見參考資源[6]中的附錄B.3。

3 标準目錄布局

    Maven為工程中的源檔案,資源檔案,配置檔案,生成的輸出和文檔都制定了一個标準的目錄結構。Maven鼓勵使用标準目錄布局,這樣就不需要進行額外的配置,而且有助于各個不同工程之間的聯接。當然,Maven也允許定制個性的目錄布局,這就需要進行更多的配置。關于Maven的标準目錄布局,請見參考資源[6]中的附錄B.1。

4 Maven的優點

    [1]build邏輯可以被重用。在Ant中可能需要多次重複地寫相同的語句,但由于POM的繼承性,可以複用其它的POM檔案中的語句。這樣既可以寫出清晰的build語句,又可以構造出層次關系良好的build工程。

    [2]不必關注build工作的實作細節。我們隻需要使用一些build生命周期短語就可以達到我們的目标,而不必管Maven是如何做到這些的。如,隻需要告訴Maven要安裝(install),那麼它自然就會驗證,編譯,打包,及安裝。

    [3]Maven會自動加載工程依賴的artifact所依賴的其它artifact(Transitive Dependency),而不用顯示的将這些artifact全部寫到dependency中。

    [4]如果完全使用Maven的标準目錄布局,那麼可以極大地減少配置細節。

5 執行個體

5.1 構想

    由于隻是闡述Maven的基本使用方法,是以本文将要設計的執行個體,隻是一個簡單的Maven demo。該執行個體包含兩個工程:普通應用程式工程(app)和Web應用工程(webapp)。app工程提供一個簡單的Java類;webapp工程隻包含一個Servlet,并将使用app中的Java類。

    該Demo的目标是能夠正确地将webapp制成war包,以供部署時使用。要能夠正确制作war,自然首先就必須要能夠正确的編譯源代碼,且要将App子產品制成jar包。本文建立的工程所在的目錄是D:\maven\demo。

5.2 App工程

    可以使用Maven的archetype插件來建立新工程,指令如下:

    D:\maven\demo>mvn archetype:create -DgroupId=ce.demo.mvn -DartifactId=app

該工程的groupId是ce.demo.mvn,那麼該工程的源檔案将放在Java包ce.demo.mvn中。artifactId是app,那麼該工程根目錄的名稱将為app。

    當第一次執行該指令時,Maven會從central倉庫中下載下傳一些檔案。這些檔案包含插件archetype,以及它所依賴的其它包。該指令執行完畢後,在目錄D:\maven\demo下會出現如下目錄布局:

app

|-- pom.xml

`-- src

    |-- main

    |   `-- java

    |       `-- ce

    |           `-- demo

    |               `-- mvn

    |                   `-- App.java

    `-- test

        `-- java

            `-- ce

                `-- demo

                    `-- mvn

                        `-- AppTest.java

因本文暫時不涉及JUnit測試,故請将目錄app\src\test目錄删除(不删除也沒關系 ^_^)。然後再修改App.java檔案,其完全内容如下:

Maven入門--概念與執行個體

package ce.demo.mvn;

Maven入門--概念與執行個體
Maven入門--概念與執行個體

public class App 

Maven入門--概念與執行個體

{

Maven入門--概念與執行個體
Maven入門--概念與執行個體

    public String getStr(String str) 

Maven入門--概念與執行個體
Maven入門--概念與執行個體

        return str;

Maven入門--概念與執行個體

    }

Maven入門--概念與執行個體

}

其實,如果我們能夠清楚地知道Maven的标準目錄布局,就可以不使用archetype插件來建立工程原型;如果我們要定制個性的目錄布局,那麼就更沒有必要使用archetype插件了。

5.3 WebApp工程

    我們仍然如建立app工程一樣使用archetype插件來建立webapp工程,指令如下:

    D:\maven\demo>mvn archetype:create -DgroupId=ce.demo.mvn -DartifactId=webapp -DarchetypeArtifactId=maven-archetype-webapp

    第一次運作此指令時,也會從central倉庫中下載下傳一些與Web應用相關的artifact(如javax.servlet)。此指令與建立app的指令的不同之處是,多設定了一個屬性archetypeArtifacttId,該屬性的值為maven-archetype-webapp。即告訴Maven,将要建立的工程是一個Web應用工程。建立app工程時沒有使用該屬性值,是由于archetype預設建立的是應用程式工程。同樣的,執行完該指令之後,會出現如下标準目錄布局:

webapp

    `-- main

        `-- webapp

            |-- index.jsp

            |-- WEB-INF

                `-- web.xml

    根據5.1節的構想,webapp工程将隻包含一個Servlet,是以我們不需要index.jsp檔案,請将其删除。此時大家可以發現,目前的目錄布局中并沒有放Servlet,即Java源檔案的地方。根據參考資源[6]中的附錄B.1,以及app工程中Java源檔案的布局,可以知道Servlet(它仍然是一個Java類檔案)仍然是放在webapp\src\main\java目錄中,請建立該目錄。此處的Servlet是一個簡單HelloServlet,其完整代碼如下:

Maven入門--概念與執行個體

package hello;

Maven入門--概念與執行個體
Maven入門--概念與執行個體

import java.io.IOException;

Maven入門--概念與執行個體

import java.io.PrintWriter;

Maven入門--概念與執行個體

import javax.servlet.ServletException;

Maven入門--概念與執行個體

import javax.servlet.http.HttpServlet;

Maven入門--概念與執行個體

import javax.servlet.http.HttpServletRequest;

Maven入門--概念與執行個體

import javax.servlet.http.HttpServletResponse;

Maven入門--概念與執行個體
Maven入門--概念與執行個體

import ce.demo.mvn.App;  // 引用app工程中的App類

Maven入門--概念與執行個體
Maven入門--概念與執行個體
Maven入門--概念與執行個體

public class HelloServlet extends HttpServlet 

Maven入門--概念與執行個體
Maven入門--概念與執行個體

    private static final long serialVersionUID = -3696470690560528247L;

Maven入門--概念與執行個體

    public void doGet(HttpServletRequest request, HttpServletResponse response)

Maven入門--概念與執行個體
Maven入門--概念與執行個體

            throws ServletException, IOException 

Maven入門--概念與執行個體
Maven入門--概念與執行個體

        App app = new App();

Maven入門--概念與執行個體

        String str = app.getStr("CE Maven Demo");

Maven入門--概念與執行個體

        PrintWriter out = response.getWriter();

Maven入門--概念與執行個體

        out.print("<html><body>");

Maven入門--概念與執行個體

        out.print("<h1>" + str);

Maven入門--概念與執行個體

        out.print("</body></html>");

Maven入門--概念與執行個體
Maven入門--概念與執行個體

5.4 POM檔案

    大家可以發現,在前面建立工程時,我們并沒有提到各個工程中的pom.xml檔案。現在将要讨論這個問題。我們先看看app工程中的POM檔案,其完整内容如下:

Maven入門--概念與執行個體

<project>

Maven入門--概念與執行個體

  <modelVersion>4.0.0</modelVersion>

Maven入門--概念與執行個體

  <groupId>ce.demo.mvn</groupId>

Maven入門--概念與執行個體

  <artifactId>app</artifactId>

Maven入門--概念與執行個體

  <packaging>jar</packaging>

Maven入門--概念與執行個體

  <version>1.0</version>

Maven入門--概念與執行個體

  <name>CE Maven Demo -- App</name>

Maven入門--概念與執行個體

</project>

    大家可以發現此我帖出來的内容與實際由archetype插件生成的POM檔案的内容有些不同,但基本上是一緻的。隻是為了使檔案中的語句更清晰,此處删除了一些備援的内容,并修改了該工程的version和name的值,以與此例子的背景來符合。在目前情況下modelVersion值将被固定為4.0.0,這也是Maven2唯一能夠識别的model版本。groupId,artifactId的值與建立工程時使用的指令中的相關屬性值是一緻的。packaging的值由工程的類型決定,如應用程式工程的packaging值為jar,Web應用工程的packaging值為war。上述情況也可以從webapp的POM檔案中看出,下面将看看這個pom的完整内容。

Maven入門--概念與執行個體
Maven入門--概念與執行個體
Maven入門--概念與執行個體
Maven入門--概念與執行個體

  <artifactId>webapp</artifactId>

Maven入門--概念與執行個體

  <packaging>war</packaging>

Maven入門--概念與執行個體
Maven入門--概念與執行個體

  <name>CE Maven Demo -- WebApp</name>

Maven入門--概念與執行個體
Maven入門--概念與執行個體

  <dependencies>

Maven入門--概念與執行個體

      <dependency>

Maven入門--概念與執行個體

          <groupId>ce.demo.mvn</groupId>

Maven入門--概念與執行個體

          <artifactId>app</artifactId>

Maven入門--概念與執行個體

          <version>1.0</version>

Maven入門--概念與執行個體

      </dependency>

Maven入門--概念與執行個體

    <dependency>

Maven入門--概念與執行個體

        <groupId>javax.servlet</groupId>

Maven入門--概念與執行個體

        <artifactId>servlet-api</artifactId>

Maven入門--概念與執行個體

        <version>2.4</version>

Maven入門--概念與執行個體

        <scope>provided</scope>

Maven入門--概念與執行個體

    </dependency> 

Maven入門--概念與執行個體

  </dependencies>

Maven入門--概念與執行個體

    比較app與webapp中的POM,除前面已經提過的packaging的差别外,我們還可以發現webapp中的POM多了dependencies項。由于webapp需要用到app工程中的類(見HelloServlet源代碼),它還需要javax.servlet包(因為該包并不預設存在于jsdk中)。故,我們必須要将它們聲明到依賴關系中。

5.5 執行

    上述兩個工程建立完畢後,就需要執行一些指令來看看會有什麼結果出現。我們首先進入app目錄,并執行指令mvn compile,然後會在該目錄下發現新生成的目錄target\classes,即編譯後的class檔案(包括它的包目錄)就放在了這裡。再執行指令mvn package,在目錄target中就會生成app-1.0.jar檔案。該檔案的全名由如下形式确定:artifactId-version.packaging。根據第2章的叙述可以知道,執行指令mvn package時,将首先将産生執行指令mvn compile之後的結果,故如果要打包,那麼隻需要執行mvn package即可。

    在app工程中執行完之後,就需要進入webapp工程了。進入webapp目錄,此次将隻執行mvn package指令(隐示地執行了compile過程)。此次指令的執行并不成功,會出現如下問題:

D:\maven\demo\webapp>mvn package

……

Downloading: http://repo1.maven.org/maven2/ce/demo/mvn/app/1.0/app-1.0.pom

[INFO] ------------------------------------------------------------------------

[ERROR] BUILD ERROR

[INFO] Error building POM (may not be this project's POM).

Project ID: ce.demo.mvn:app

Reason: Error getting POM for 'ce.demo.mvn:app' from the repository: Error transferring file

  ce.demo.mvn:app:pom:1.0

from the specified remote repositories:

  central (http://repo1.maven.org/maven2)

    由粗體内容可知,Maven正試圖從central倉庫下載下傳app工程的artifact,但central倉庫肯定不會有這個artifact,其結果隻能是執行失敗!由第1章artifact名詞的解釋可知,被依賴的artifact必須存在于倉庫(遠端或本地)中,但目前webapp所依賴的app必不存在于倉庫中,是以執行隻能失敗。

    解決這個問題有兩種方法:[1]将app-1.0.jar安裝到倉庫中,使它成為一個artifact;[2]建構一個更高層次的工程,使app和webapp成為這個工程的子工程,然後從這個更高層次工程中執行指令。

    第一種方法比較簡單(見),此處将詳細讨論第2種方法(見5.6節)。

5.6 更高層次工程

    我們可以将app和webapp的上一級目錄demo作為這兩個工程的 一個 更高層次工程,即使用app和webapp成為這個工程的子工程。為了使demo目錄成為一個demo工程,隻需要在這個目錄下添加一個pom.xml檔案,該檔案内容如下:

Maven入門--概念與執行個體
Maven入門--概念與執行個體

    <modelVersion>4.0.0</modelVersion>

Maven入門--概念與執行個體

    <groupId>ce.demo</groupId>

Maven入門--概念與執行個體

    <artifactId>mvn-demo</artifactId>

Maven入門--概念與執行個體

    <packaging>pom</packaging>

Maven入門--概念與執行個體

    <version>1.0</version>

Maven入門--概念與執行個體

    <name>CE Maven Demo</name>

Maven入門--概念與執行個體
Maven入門--概念與執行個體

    <modules>

Maven入門--概念與執行個體

        <module>app</module>

Maven入門--概念與執行個體

        <module>webapp</module>

Maven入門--概念與執行個體

    </modules>

Maven入門--概念與執行個體

    與app和webapp中的POM相比,demo的POM使用了modules項,modules用于聲明本工程的子工程,module中的值對應于子工程的artifact名。而且該POM的packaging類型必須為pom。

    有了demo工程後,我們隻需要在demo目錄下執行相關指令就可以了。通過如下指令即可驗證:

    [1]mvn clean – 消除工程(包括所有子工程)中産生的所有輸出。這本文的執行個體中,實際上是删除target目錄。由于之前的操作隻有app工程産生了target目錄,而webapp并沒有,是以将隻會删除app工程中的target目錄。

    [2]mvn package – 将工程制作成相應的包,app工程是作成jar包(app-1.0.jar),webapp工程是作成war包(webapp-1.0.war)。打開webapp-1.0.war包,可以發現app-1.0.jar被放到了WEB-INF的lib目錄中。

6 小結

    通過以上的叙述與執行個體,應該可以對Maven有一個粗略的認識了。使用Maven關鍵是要弄清楚如何寫pom.xml檔案,就如同使用Ant要會寫build.xml檔案一樣。在POM中可以直接寫入Ant的task腳本,也可以調用Ant的build.xml檔案(推薦),是以Maven也可以完成Ant的絕大多數工作(但不必安裝Ant)。注意:使用Maven就不要再過多的使用Ant腳本。

    利用好Maven的繼承特性及子工程的關系,可以很好地簡化POM檔案,并能夠建構層次結構良好的工程,有利于工程的維護。

7 參考資源

[1]Maven官方網站. http://maven.apache.org

[2]Maven POM檔案參考結構. http://maven.apache.org/ref/current/maven-model/maven.html

[3]Super POM. http://maven.apache.org/guides/introduction/introduction-to-the-pom.html

[4]Maven主要插件的清單. http://maven.apache.org/plugins

[5]Maven基本使用指南. http://maven.apache.org/guides/index.html

[6]Better Build with Maven. http://www.mergere.com/m2book_download.jsp -- 強烈推薦

[7]介紹Maven2. http://www.javaworld.com/javaworld/jw-12-2005 /jw-1205-maven_p.html

[8]揭秘Maven2 POM. http://www.javaworld.com/javaworld/jw-05-2006/jw-0529-maven.html

[9]Maven讓事情變得簡單. http://www-128.ibm.com/developerworks/cn/java/j-maven

[10]Maven文檔集. http://docs.codehaus.org/display/MAVENUSER/Home

[11]有效利用Maven2的站點生成功能. http://www.matrix.org.cn/resource/article/44/44491_Maven2.html