【轉】Maven 入門

 
Maven入門--概念與實例 

    最近因爲工做緣由在研究、應用Maven,有了一些體會就寫成了此文。本文雖然是Maven2的入門文章,但並不涉及Maven的歷史、下載與安裝,這些內容能夠到Maven的官方網站上了解。本文主要是關注Maven中的重要概念,並以一個實例來闡述使用Maven的基本方法。文末有例子代碼下載的連接。(2007.01.02最後更新) 
注:轉載時請註明原做者(jiangshachina)及出處(http://www.blogjava.net/jiangshachina)!

1 關鍵名詞 

    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下會出現以下目錄佈局:
html

app
|-- pom.xml
`-- src
    |-- main
    |   `-- java
    |       `-- ce
    |           `-- demo
    |               `-- mvn
    |                   `-- App.java
    `-- test
        `-- java
            `-- ce
                `-- demo
                    `-- mvn
                        `-- AppTest.java

因本文暫時不涉及JUnit測試,故請將目錄app\src\test目錄刪除(不刪除也不要緊 ^_^)。而後再修改App.java文件,其徹底內容以下:
java

package  ce.demo.mvn;
public   class  App  {
    
public String getStr(String str) {
        
return str;
    }

}

其實,若是咱們可以清楚地知道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默認建立的是應用程序工程。一樣的,執行完該命令以後,會出現以下標準目錄佈局:
web

webapp
|-- pom.xml
`-- src
    `-- 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,其完整代碼以下:
shell

package  hello;

import  java.io.IOException;
import  java.io.PrintWriter;
import  javax.servlet.ServletException;
import  javax.servlet.http.HttpServlet;
import  javax.servlet.http.HttpServletRequest;
import  javax.servlet.http.HttpServletResponse;

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

public   class  HelloServlet  extends  HttpServlet  {
    
private static final long serialVersionUID = -3696470690560528247L;
    
public void doGet(HttpServletRequest request, HttpServletResponse response)
            
throws ServletException, IOException {
        App app 
= new App();
        String str 
= app.getStr("CE Maven Demo");
        PrintWriter out 
= response.getWriter();
        out.print(
"<html><body>");
        out.print(
"<h1>" + str);
        out.print(
"</body></html>");
    }

}

5.4 POM文件
    你們能夠發現,在前面新建工程時,咱們並無提到各個工程中的pom.xml文件。如今將要討論這個問題。咱們先看看app工程中的POM文件,其完整內容以下:
apache

< project >
  
< modelVersion > 4.0.0 </ modelVersion >
  
< groupId > ce.demo.mvn </ groupId >
  
< artifactId > app </ artifactId >
  
< packaging > jar </ packaging >
  
< version > 1.0 </ version >
  
< name > CE Maven Demo -- App </ name >
</ project >

    你們能夠發現此我帖出來的內容與實際由archetype插件生成的POM文件的內容有些不一樣,但基本上是一致的。只是爲了使文件中的語句更清晰,此處刪除了一些冗餘的內容,並修改了該工程的version和name的值,以與此例子的背景來符合。在目前狀況下modelVersion值將被固定爲4.0.0,這也是Maven2惟一可以識別的model版本。groupId,artifactId的值與建立工程時使用的命令中的相關屬性值是一致的。packaging的值由工程的類型決定,如應用程序工程的packaging值爲jar,Web應用工程的packaging值爲war。上述狀況也能夠從webapp的POM文件中看出,下面將看看這個pom的完整內容。
api

< project >
  
< modelVersion > 4.0.0 </ modelVersion >
  
< groupId > ce.demo.mvn </ groupId >
  
< artifactId > webapp </ artifactId >
  
< packaging > war </ packaging >
  
< version > 1.0 </ version >
  
< name > CE Maven Demo -- WebApp </ name >
  
  
< dependencies >
      
< dependency >
          
< groupId > ce.demo.mvn </ groupId >
          
< artifactId > app </ artifactId >
          
< version > 1.0 </ version >
      
</ dependency >
    
< dependency >
        
< groupId > javax.servlet </ groupId >
        
< artifactId > servlet-api </ artifactId >
        
< version > 2.4 </ version >
        
< scope > provided </ scope >
    
</ dependency >  
  
</ dependencies >
</ project >

    比較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過程)。這次命令的執行並不成功,會出現以下問題:
app

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 ]  ------------------------------------------------------------------------
[ 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成爲這個工程的子工程,而後從這個更高層次工程中執行命令。
    第一種方法比較簡單(http://www.blogjava.net/jiangshachina/admin/EditPosts.aspx中的第一個主題),此處將詳細討論第2種方法(見5.6節)。
5.6 更高層次工程
    咱們能夠將app和webapp的上一級目錄demo做爲這兩個工程的
 一個 更高層次工程,即便用app和webapp成爲這個工程的子工程。爲了使demo目錄成爲一個demo工程,只須要在這個目錄下添加一個pom.xml文件,該文件內容以下:
webapp

< project >
    
< modelVersion > 4.0.0 </ modelVersion >
    
< groupId > ce.demo </ groupId >
    
< artifactId > mvn-demo </ artifactId >
    
< packaging > pom </ packaging >
    
< version > 1.0 </ version >
    
< name > CE Maven Demo </ name >
    
    
< modules >
        
< module > app </ module >
        
< module > webapp </ module >
    
</ modules >
</ project >

    與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
文中例子程序下載:http://www.blogjava.net/files/jiangshachina/maven.rar
jsp

相關文章
相關標籤/搜索