Maven入門--概念與實例
最近由于工作原因在研究、應用Maven,有了一些體會就寫成了此文。本文雖然是Maven2的入門文章,但并不涉及Maven的歷史、下載與安裝,這些內容可以到Maven的官方網站上了解。本文主要是關注Maven中的重要概念,并以一個實例來闡述使用Maven的基本方法。文末有例子代碼下載的鏈接。(2007.01.02最后更新) 注:轉載時請注明原作者(jiangshachina)及出處(http://www.aygfsteel.com/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下會出現如下目錄布局:
app
|-- pom.xml
`-- src
|-- main
| `-- java
| `-- ce
| `-- demo
| `-- mvn
| `-- App.java
`-- test
`-- java
`-- ce
`-- demo
`-- mvn
`-- AppTest.java
因本文暫時不涉及JUnit測試,故請將目錄app\src\test目錄刪除(不刪除也沒關系 ^_^)。然后再修改App.java文件,其完全內容如下:|-- pom.xml
`-- src
|-- main
| `-- java
| `-- ce
| `-- demo
| `-- mvn
| `-- App.java
`-- test
`-- java
`-- ce
`-- demo
`-- mvn
`-- AppTest.java






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
|-- 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,其完整代碼如下:|-- pom.xml
`-- src
`-- main
`-- webapp
|-- index.jsp
|-- WEB-INF
`-- web.xml























大家可以發現,在前面新建工程時,我們并沒有提到各個工程中的pom.xml文件。現在將要討論這個問題。我們先看看app工程中的POM文件,其完整內容如下:






























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] ------------------------------------------------------------------------
[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必不存在于倉庫中,所以執行只能失敗。……
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)
……
解決這個問題有兩種方法:[1]將app-1.0.jar安裝到倉庫中,使它成為一個artifact;[2]構建一個更高層次的工程,使app和webapp成為這個工程的子工程,然后從這個更高層次工程中執行命令。
第一種方法比較簡單(見http://www.aygfsteel.com/jiangshachina/admin/EditPosts.aspx中的第一個主題),此處將詳細討論第2種方法(見5.6節)。
5.6 更高層次工程
我們可以將app和webapp的上一級目錄demo作為這兩個工程的 一個 更高層次工程,即使用app和webapp成為這個工程的子工程。為了使demo目錄成為一個demo工程,只需要在這個目錄下添加一個pom.xml文件,該文件內容如下:













有了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.aygfsteel.com/files/jiangshachina/maven.rar
請關注本人的另一篇關于Maven的文章--Maven入門--較復雜的實例