隨筆-314  評(píng)論-209  文章-0  trackbacks-0
          最近由于工作原因在研究、應(yīng)用Maven,有了一些體會(huì)就寫(xiě)成了此文。本文雖然是Maven2的入門(mén)文章,但并不涉及Maven的歷史、下載與安裝,這些內(nèi)容可以到Maven的官方網(wǎng)站上了解。本文主要是關(guān)注Maven中的重要概念,并以一個(gè)實(shí)例來(lái)闡述使用Maven的基本方法。文末有例子代碼下載的鏈接。(2006.10.29最后更新)
          1 關(guān)鍵名詞
              Project:任何您想build的事物,Maven都可以認(rèn)為它們是工程。這些工程被定義為工程對(duì)象模型(POM,Poject Object Model)。一個(gè)工程可以依賴其它的工程;一個(gè)工程也可以由多個(gè)子工程構(gòu)成。
              POM:POM(pom.xml)是Maven的核心文件,它是指示Maven如何工作的元數(shù)據(jù)文件,類似于Ant中的build.xml文件。POM文件位于每個(gè)工程的根目錄中。
              GroupId:groupId是一個(gè)工程的在全局中唯一的標(biāo)識(shí)符,一般地,它就是工程名。groupId有利于使用一個(gè)完全的包名,將一個(gè)工程從其它有類似名稱的工程里區(qū)別出來(lái)。
              Artifact:artifact是工程將要產(chǎn)生或需要使用的文件,它可以是jar文件,源文件,二進(jìn)制文件,war文件,甚至是pom文件。每個(gè)artifact都由groupId和artifactId組合的標(biāo)識(shí)符唯一識(shí)別。需要被使用(依賴)的artifact都要放在倉(cāng)庫(kù)(見(jiàn)Repository)中,否則Maven無(wú)法找到(識(shí)別)它們。
              Dependency:為了能夠build或運(yùn)行,一個(gè)典型的Java工程會(huì)依賴其它的包。在Maven中,這些被依賴的包就被稱為dependency。dependency一般是其它工程的artifact。
              Plug-in:Maven是由插件組織的,它的每一個(gè)功能都是由插件提供的。插件提供goal(類似于Ant中的target),并根據(jù)在POM中找到的元數(shù)據(jù)去完成工作。主要的Maven插件要是由Java寫(xiě)成的,但它也支持用Beanshell或Ant腳本寫(xiě)成的插件。
              Repository:倉(cāng)庫(kù)用于存放artifact,它可以是本地倉(cāng)庫(kù),也可以是遠(yuǎn)程倉(cāng)庫(kù)。Maven有一個(gè)默認(rèn)的遠(yuǎn)程倉(cāng)庫(kù)--central,可以從http://www.ibiblio.org/maven2/下載其中的artifact。在Windows平臺(tái)上,本地倉(cāng)庫(kù)的默認(rèn)地址是User_Home\.m2\repository。
              Snapshot:工程中可以(也應(yīng)該)有一個(gè)特殊版本,它的版本號(hào)包括SNAPSHOT字樣。該版本可以告訴Maven,該工程正處于開(kāi)發(fā)階段,會(huì)經(jīng)常更新(但還未發(fā)布)。當(dāng)其它工程使用此類型版本的artifact時(shí),Maven會(huì)在倉(cāng)庫(kù)中尋找該artifact的最新版本,并自動(dòng)下載、使用該最新版。
          2 Maven Build Life Cycle
              軟件項(xiàng)目一般都有相似的開(kāi)發(fā)過(guò)程:準(zhǔn)備,編譯,測(cè)試,打包和部署,Maven將上述過(guò)程稱為Build Life Cycle。在Maven中,這些生命周期由一系列的短語(yǔ)組成,每個(gè)短語(yǔ)對(duì)應(yīng)著一個(gè)(或多個(gè))操作;或?qū)?yīng)著一個(gè)(或多個(gè))goal(類似于Ant中的target)。
              如編譯源文件的命令mvn compile中的compile是一個(gè)生命周期短語(yǔ)。同時(shí)該命令也可以等價(jià)于mvn compiler:compile,其中的compiler是一個(gè)插件,它提供了compile(此compile與mvn compile中的compile意義不同)goal;compiler還可提供另一個(gè)goal--testCompile,該goal用于編譯junit測(cè)試類。
              在執(zhí)行某一個(gè)生命周期時(shí),Maven會(huì)首先執(zhí)行該生命周期之前的其它周期。如要執(zhí)行compile,那么將首先執(zhí)行validate,generate-source,process-source和generate-resources,最后再執(zhí)行compile本身。關(guān)于Maven中默認(rèn)的生命周期短語(yǔ),請(qǐng)見(jiàn)參考資源[6]中的附錄B.3。
          3 標(biāo)準(zhǔn)目錄布局
              Maven為工程中的源文件,資源文件,配置文件,生成的輸出和文檔都制定了一個(gè)標(biāo)準(zhǔn)的目錄結(jié)構(gòu)。Maven鼓勵(lì)使用標(biāo)準(zhǔn)目錄布局,這樣就不需要進(jìn)行額外的配置,而且有助于各個(gè)不同工程之間的聯(lián)接。當(dāng)然,Maven也允許定制個(gè)性的目錄布局,這就需要進(jìn)行更多的配置。關(guān)于Maven的標(biāo)準(zhǔn)目錄布局,請(qǐng)見(jiàn)參考資源[6]中的附錄B.1。
          4 Maven的優(yōu)點(diǎn)
              [1]build邏輯可以被重用。在Ant中可能需要多次重復(fù)地寫(xiě)相同的語(yǔ)句,但由于POM的繼承性,可以復(fù)用其它的POM文件中的語(yǔ)句。這樣既可以寫(xiě)出清晰的build語(yǔ)句,又可以構(gòu)造出層次關(guān)系良好的build工程。
              [2]不必關(guān)注build工作的實(shí)現(xiàn)細(xì)節(jié)。我們只需要使用一些build生命周期短語(yǔ)就可以達(dá)到我們的目標(biāo),而不必管Maven是如何做到這些的。如,只需要告訴Maven要安裝(install),那么它自然就會(huì)驗(yàn)證,編譯,打包,及安裝。
              [3]Maven會(huì)遞歸加載工程依賴的artifact所依賴的其它artifact,而不用顯示的將這些artifact全部寫(xiě)到dependency中。
              [4]如果完全使用Maven的標(biāo)準(zhǔn)目錄布局,那么可以極大地減少配置細(xì)節(jié)。
          5 實(shí)例
          5.1 構(gòu)想
              由于只是闡述Maven的基本使用方法,所以本文將要設(shè)計(jì)的實(shí)例,只是一個(gè)簡(jiǎn)單的Maven demo。該實(shí)例包含兩個(gè)工程:普通應(yīng)用程序工程(app)和Web應(yīng)用工程(webapp)。app工程提供一個(gè)簡(jiǎn)單的Java類;webapp工程只包含一個(gè)Servlet,并將使用app中的Java類。
              該Demo的目標(biāo)是能夠正確地將webapp制成war包,以供部署時(shí)使用。要能夠正確制作war,自然首先就必須要能夠正確的編譯源代碼,且要將App模塊制成jar包。本文創(chuàng)建的工程所在的目錄是D:\maven\demo。
          5.2 App工程
              可以使用Maven的archetype插件來(lái)創(chuàng)建新工程,命令如下:
              D:\maven\demo>mvn archetype:create -DgroupId=ce.demo.mvn -DartifactId=app
          該工程的groupId是ce.demo.mvn,那么該工程的源文件將放在Java包c(diǎn)e.demo.mvn中。artifactId是app,那么該工程根目錄的名稱將為app。
              當(dāng)?shù)谝淮螆?zhí)行該命令時(shí),Maven會(huì)從central倉(cāng)庫(kù)中下載一些文件。這些文件包含插件archetype,以及它所依賴的其它包。該命令執(zhí)行完畢后,在目錄D:\maven\demo下會(huì)出現(xiàn)如下目錄布局:
          app
          |-- pom.xml
          `-- src
              |-- main
              |   `-- java
              |       `-- ce
              |           `-- demo
              |               `-- mvn
              |                   `-- App.java
              `-- test
                  `-- java
                      `-- ce
                          `-- demo
                              `-- mvn
                                  `-- AppTest.java
          因本文暫時(shí)不涉及JUnit測(cè)試,故請(qǐng)將目錄app\src\test目錄刪除(不刪除也沒(méi)關(guān)系 ^_^)。然后再修改App.java文件,其完全內(nèi)容如下:
          package ce.demo.mvn;
          public class App {
              
          public String getStr(String str) {
                  
          return str;
              }

          }
          其實(shí),如果我們能夠清楚地知道Maven的標(biāo)準(zhǔn)目錄布局,就可以不使用archetype插件來(lái)創(chuàng)建工程原型;如果我們要定制個(gè)性的目錄布局,那么就更沒(méi)有必要使用archetype插件了。
          5.3 WebApp工程
              我們?nèi)匀蝗鐒?chuàng)建app工程一樣使用archetype插件來(lái)創(chuàng)建webapp工程,命令如下:
             
          D:\maven\demo>mvn archetype:create -DgroupId=ce.demo.mvn -DartifactId=webapp -DarchetypeArtifactId=maven-archetype-webapp
              第一次運(yùn)行此命令時(shí),也會(huì)從central倉(cāng)庫(kù)中下載一些與Web應(yīng)用相關(guān)的artifact(如javax.servlet)。此命令與創(chuàng)建app的命令的不同之處是,多設(shè)置了一個(gè)屬性archetypeArtifacttId,該屬性的值為maven-archetype-webapp。即告訴Maven,將要?jiǎng)?chuàng)建的工程是一個(gè)Web應(yīng)用工程。創(chuàng)建app工程時(shí)沒(méi)有使用該屬性值,是由于archetype默認(rèn)創(chuàng)建的是應(yīng)用程序工程。同樣的,執(zhí)行完該命令之后,會(huì)出現(xiàn)如下標(biāo)準(zhǔn)目錄布局:
          webapp
          |-- pom.xml
          `-- src
              `-- main
                  `-- webapp
                     
          |-- index.jsp
                      |-- WEB-INF
                          `-- web.xml
              根據(jù)5.1節(jié)的構(gòu)想,webapp工程將只包含一個(gè)Servlet,所以我們不需要index.jsp文件,請(qǐng)將其刪除。此時(shí)大家可以發(fā)現(xiàn),目前的目錄布局中并沒(méi)有放Servlet,即Java源文件的地方。根據(jù)參考資源[6]中的附錄B.1,以及app工程中Java源文件的布局,可以知道Servlet(它仍然是一個(gè)Java類文件)仍然是放在webapp\src\main\java目錄中,請(qǐng)新建該目錄。此處的Servlet是一個(gè)簡(jiǎn)單HelloServlet,其完整代碼如下:
          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文件
              大家可以發(fā)現(xiàn),在前面新建工程時(shí),我們并沒(méi)有提到各個(gè)工程中的pom.xml文件?,F(xiàn)在將要討論這個(gè)問(wèn)題。我們先看看app工程中的POM文件,其完整內(nèi)容如下:
          <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>
              大家可以發(fā)現(xiàn)此我帖出來(lái)的內(nèi)容與實(shí)際由archetype插件生成的POM文件的內(nèi)容有些不同,但基本上是一致的。只是為了使文件中的語(yǔ)句更清晰,此處刪除了一些冗余的內(nèi)容,并修改了該工程的version和name的值,以與此例子的背景來(lái)符合。在目前情況下modelVersion值將被固定為4.0.0,這也是Maven2唯一能夠識(shí)別的model版本。groupId,artifactId的值與創(chuàng)建工程時(shí)使用的命令中的相關(guān)屬性值是一致的。packaging的值由工程的類型決定,如應(yīng)用程序工程的packaging值為jar,Web應(yīng)用工程的packaging值為war。上述情況也可以從webapp的POM文件中看出,下面將看看這個(gè)pom的完整內(nèi)容。
          <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,除前面已經(jīng)提過(guò)的packaging的差別外,我們還可以發(fā)現(xiàn)webapp中的POM多了dependencies項(xiàng)。由于webapp需要用到app工程中的類(見(jiàn)HelloServlet源代碼),它還需要javax.servlet包(因?yàn)樵摪⒉荒J(rèn)存在于jsdk中)。故,我們必須要將它們聲明到依賴關(guān)系中。
          5.5 執(zhí)行
              上述兩個(gè)工程創(chuàng)建完畢后,就需要執(zhí)行一些命令來(lái)看看會(huì)有什么結(jié)果出現(xiàn)。我們首先進(jìn)入app目錄,并執(zhí)行命令mvn compile,然后會(huì)在該目錄下發(fā)現(xiàn)新生成的目錄target\classes,即編譯后的class文件(包括它的包目錄)就放在了這里。再執(zhí)行命令mvn package,在目錄target中就會(huì)生成app-1.0.jar文件。該文件的全名由如下形式確定:artifactId-version.packaging。根據(jù)第2章的敘述可以知道,執(zhí)行命令mvn package時(shí),將首先將產(chǎn)生執(zhí)行命令mvn compile之后的結(jié)果,故如果要打包,那么只需要執(zhí)行mvn package即可。
              在app工程中執(zhí)行完之后,就需要進(jìn)入webapp工程了。進(jìn)入webapp目錄,此次將只執(zhí)行mvn package命令(隱示地執(zhí)行了compile過(guò)程)。此次命令的執(zhí)行并不成功,會(huì)出現(xiàn)如下問(wèn)題:
          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)
          ……
              由粗體內(nèi)容可知,Maven正試圖從central倉(cāng)庫(kù)下載app工程的artifact,但central倉(cāng)庫(kù)肯定不會(huì)有這個(gè)artifact,其結(jié)果只能是執(zhí)行失敗!由第1章artifact名詞的解釋可知,被依賴的artifact必須存在于倉(cāng)庫(kù)(遠(yuǎn)程或本地)中,但目前webapp所依賴的app必不存在于倉(cāng)庫(kù)中,所以執(zhí)行只能失敗。
              解決這個(gè)問(wèn)題有兩種方法:[1]將app-1.0.jar安裝到倉(cāng)庫(kù)中,使它成為一個(gè)artifact;[2]構(gòu)建一個(gè)更高層次的工程,使app和webapp成為這個(gè)工程的子工程,然后從這個(gè)更高層次工程中執(zhí)行命令。
              第一種方法比較簡(jiǎn)單(
          見(jiàn)http://www.aygfsteel.com/jiangshachina/admin/EditPosts.aspx中的第一個(gè)主題),此處將詳細(xì)討論第2種方法(見(jiàn)5.6節(jié))。
          5.6 更高層次工程
              我們可以將app和webapp的上一級(jí)目錄demo作為這兩個(gè)工程的 一個(gè)
          更高層次工程,即使用app和webapp成為這個(gè)工程的子工程。為了使demo目錄成為一個(gè)demo工程,只需要在這個(gè)目錄下添加一個(gè)pom.xml文件,該文件內(nèi)容如下:
          <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項(xiàng),modules用于聲明本工程的子工程,module中的值對(duì)應(yīng)于子工程的artifact名。而且該P(yáng)OM的packaging類型必須為pom。
              有了demo工程后,我們只需要在demo目錄下執(zhí)行相關(guān)命令就可以了。通過(guò)如下命令即可驗(yàn)證:
              [1]mvn clean – 消除工程(包括所有子工程)中產(chǎn)生的所有輸出。這本文的實(shí)例中,實(shí)際上是刪除target目錄。由于之前的操作只有app工程產(chǎn)生了target目錄,而webapp并沒(méi)有,所以將只會(huì)刪除app工程中的target目錄。
              [2]mvn package – 將工程制作成相應(yīng)的包,app工程是作成jar包(app-1.0.jar),webapp工程是作成war包(webapp-1.0.war)。打開(kāi)webapp-1.0.war包,可以發(fā)現(xiàn)app-1.0.jar被放到了WEB-INF的lib目錄中。
          6 小結(jié)
              通過(guò)以上的敘述與實(shí)例,應(yīng)該可以對(duì)Maven有一個(gè)粗略的認(rèn)識(shí)了。使用Maven關(guān)鍵是要弄清楚如何寫(xiě)pom.xml文件,就如同使用Ant要會(huì)寫(xiě)build.xml文件一樣。在POM中可以直接寫(xiě)入Ant的task腳本,也可以調(diào)用Ant的build.xml文件(推薦),所以Maven也可以完成Ant的絕大多數(shù)工作(但不必安裝Ant)。注意:使用Maven就不要再過(guò)多的使用Ant腳本。
              利用好Maven的繼承特性及子工程的關(guān)系,可以很好地簡(jiǎn)化POM文件,并能夠構(gòu)建層次結(jié)構(gòu)良好的工程,有利于工程的維護(hù)。
          7 參考資源
          [1]Maven官方網(wǎng)站. http://maven.apache.org
          [2]Maven POM文件參考結(jié)構(gòu). 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 -- 強(qiáng)烈推薦
          [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讓事情變得簡(jiǎn)單. http://www-128.ibm.com/developerworks/cn/java/j-maven
          [10]Maven文檔集. http://docs.codehaus.org/display/MAVENUSER/Home
          [11]有效利用Maven2的站點(diǎn)生成功能. http://www.matrix.org.cn/resource/article/44/44491_Maven2.html
          文中例子程序下載:http://www.aygfsteel.com/files/jiangshachina/maven.rar
          posted on 2006-10-29 16:29 xzc 閱讀(1253) 評(píng)論(0)  編輯  收藏 所屬分類: Maven
          主站蜘蛛池模板: 志丹县| 施甸县| 双鸭山市| 乳源| 武强县| 林芝县| 城步| 大化| 泾源县| 六盘水市| 康乐县| 尼勒克县| 遂平县| 巩义市| 临澧县| 潍坊市| 霞浦县| 大厂| 改则县| 山东省| 兴和县| 镇远县| 嫩江县| 永仁县| 洪洞县| 开江县| 交口县| 辉县市| 通州区| 商河县| 仲巴县| 怀来县| 即墨市| 汉源县| 读书| 内丘县| 乐清市| 泗水县| 西充县| 揭东县| 拉孜县|