maven 使用入門

3.1 編寫POM 
3.2 編寫主代碼 
3.3 編寫測試代碼 
3.4 打包和運行 
3.5 使用Archetype生成項目骨架 
3.6 m2eclipse簡單使用 
3.7 NetBeans Maven插件簡單使用 
3.8 小結 

3.1 編寫POM

首先創建一個名爲hello-world的文件夾(本書中各章的代碼都會對應一個以ch開頭的項目),打開該文件夾,新建一個名爲pom.xml的文件,輸入其內容如代碼清單3-1:

代碼清單3-1:Hello World的POM


Java代碼  

<?xml version="1.0" encoding="UTF-8"?>

<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>

  <groupId>com.juvenxu.mvnbook</groupId>

  <artifactId>hello-world</artifactId>

  <version>1.0-SNAPSHOT</version>

  <name>Maven Hello World Project</name>

</project> 

 1. 代碼的第一行是XML頭,指定了該xml文檔的版本和編碼方式
 2.project元素,project是所有pom.xml的根元素,它還聲明瞭一些POM相關的命名空間及xsd元素,雖然這些屬性不是必須的,但使用這些屬性能夠讓第三方工具(如IDE中           的XML編輯器)幫助我們快速編輯POM。

 3. 根元素下的第一個子元素modelVersion指定了當前POM模型的版本,對於Maven2及Maven 3來說,它只能是4.0.0這段代碼中最重要的是groupId,artifactId和version三行。這三個元素定義了一個項目基本的座標,在Maven的世界,任何的jar、pom或者war都是以基於這些基   本的座標進行區分的。

groupId定義了項目屬於哪個組,這個組往往和項目所在的組織或公司存在關聯,譬如你在googlecode上建立了一個名爲myapp的項目,那麼groupId就應該是     com.googlecode.myapp,如果你的公司是mycom,有一個項目爲myapp,那麼groupId就應該是com.mycom.myapp。本書中所有的代碼都基於groupId com.juvenxu.mvnbook。

artifactId定義了當前Maven項目在組中唯一的ID,我們爲這個Hello World項目定義artifactId爲hello-world,本書其他章節代碼會被分配其他的artifactId。而在前面的groupId爲com.googlecode.myapp的例子中,你可能會爲不同的子項目(模塊)分配artifactId,如:myapp-util、myapp-domain、myapp-web等等。

version指定了Hello World項目當前的版本——1.0-SNAPSHOT。SNAPSHOT意爲快照,說明該項目還處於開發中,是不穩定的版本。隨着項目的發展,version會不斷更新,如升級爲1.0、1.1-SNAPSHOT、1.1、2.0等等。本書的6.5小節會詳細介紹SNAPSHOT,第13章介紹如何使用Maven管理項目版本的升級發佈。

name元素聲明瞭一個對於用戶更爲友好的項目名稱,雖然這不是必須的,但我還是推薦爲每個POM聲明name,以方便信息交流。

沒有任何實際的Java代碼,我們就能夠定義一個Maven項目的POM,這體現了Maven的一大優點,它能讓項目對象模型最大程度地與實際代碼相獨立,我們可以稱之爲解耦,或者正交性,這在很大程度上避免了Java代碼和POM代碼的相互影響。比如當項目需要升級版本時,只需要修改POM,而不需要更改Java代碼;而在POM穩定之後,日常的Java代碼開發工作基本不涉及POM的修改。

3.2 編寫主代碼 

 

項目主代碼和測試代碼不同,項目的主代碼會被打包到最終的構件中(比如jar),而測試代碼只在運行測試時用到,不會被打包。默認情況下,Maven假設項目主代碼位於src/main/java目錄,我們遵循Maven的約定,創建該目錄,然後在該目錄下創建文件com/juvenxu/mvnbook/helloworld/HelloWorld.java,其內容如代碼清單3-2:

代碼清單3-2:Hello World的主代碼

Java代碼  

package com.juvenxu.mvnbook.helloworld;

public class HelloWorld

{

   public String sayHello()

   {

     return "Hello Maven";

   }

  public static void main(String[] args)

   {

     System.out.print( new HelloWorld().sayHello() );

   }

}

 這是一個簡單的Java類,它有一個sayHello()方法,返回一個String。同時這個類還帶有一個main方法,創建一個HelloWorld實例,調用sayHello()方法,並將結果輸出到控制檯。

關於該Java代碼有兩點需要注意。首先,在95%以上的情況下,我們應該把項目主代碼放到src/main/java/目錄下(遵循Maven的約定),而無須額外的配置,Maven會自動搜尋該目錄找到項目主代碼。其次,該Java類的包名是com.juvenxu.mvnbook.helloworld,這與我們之前在POM中定義的groupId和artifactId相吻合。一般來說,項目中Java類的包都應該基於項目的groupId和artifactId,這樣更加清晰,更加符合邏輯,也方便搜索構件或者Java類。

代碼編寫完畢後,我們使用Maven進行編譯,在項目根目錄下運行命令 mvn clean compile ,我們會得到如下輸出:

Java代碼  

1. [INFO] Scanning for projects...   

2. [INFO] ------------------------------------------------------------------------   

3. [INFO] Building Maven Hello World Project   

4. [INFO]    task-segment: [clean, compile]   

5. [INFO] ------------------------------------------------------------------------   

6. [INFO] [clean:clean {execution: default-clean}]          //從輸出中我們看到Maven首先執行了clean:clean任務

7. [INFO] Deleting directory D:\code\hello-world\target   //刪除target/目錄,默認情況下Maven構建的所有輸出都在target/目錄中

8. [INFO] [resources:resources {execution: default-resources}]  //接着執行resources:resources任務(未定義項目資源,暫且略過)

9. [INFO] skip non existing resourceDirectory D: \code\hello-world\src\main\resources   

10. [INFO] [compiler:compile {execution: default-compile}]   //compiler:compile任務,將項目主代碼編譯至target/classes目錄(編譯好的類com/juvenxu/mvnbook/helloworld/HelloWorld.Class)。

11. [INFO] Compiling 1 source file to D: \code\hello-world\target\classes   

12. [INFO] ------------------------------------------------------------------------   

13. [INFO] BUILD SUCCESSFUL   

14. [INFO] ------------------------------------------------------------------------   

15. [INFO] Total time: 1 second   

16. [INFO] Finished at: Fri Oct 09 02:08:09 CST 2009  

17. [INFO] Final Memory: 9M/16M   

18. [INFO] ------------------------------------------------------------------------   

19. </SPAN>  

[INFO] Scanning for projects...

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

[INFO] Building Maven Hello World Project

[INFO]    task-segment: [clean, compile]

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

[INFO] [clean:clean {execution: default-clean}]

[INFO] Deleting directory D:\code\hello-world\target

[INFO] [resources:resources {execution: default-resources}]

[INFO] skip non existing resourceDirectory D: \code\hello-world\src\main\resources

[INFO] [compiler:compile {execution: default-compile}]

[INFO] Compiling 1 source file to D: \code\hello-world\target\classes

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

[INFO] BUILD SUCCESSFUL

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

[INFO] Total time: 1 second

[INFO] Finished at: Fri Oct 09 02:08:09 CST 2009

[INFO] Final Memory: 9M/16M

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

 

clean告訴Maven清理輸出目錄targetcompile告訴Maven編譯項目主代碼

上文提到的clean:clean、resources:resources,以及compiler:compile對應了一些Maven插件及插件目標,比如clean:clean是clean插件的clean目標,compiler:compile是compiler插件的compile目標後文會詳細講述Maven插件及其編寫方法

至此,Maven在沒有任何額外的配置的情況下就執行了項目的清理和編譯任務接下來,我們編寫一些單元測試代碼並讓Maven執行自動化測試。




3.3 編寫測試代碼 

 

爲了使項目結構保持清晰,主代碼與測試代碼應該分別位於獨立的目錄中。3.2節講過Maven項目中默認的主代碼目錄是src/main/java,對應地,Maven項目中默認的測試代碼目錄是src/test/java。因此,在編寫測試用例之前,我們先創建該目錄。

在Java世界中,由Kent Beck和Erich Gamma建立的JUnit是事實上的單元測試標準。要使用JUnit,我們首先需要爲Hello World項目添加一個JUnit依賴,修改項目的POM如代碼清單3-3:

代碼清單3-3:爲Hello World的POM添加依賴

Java代碼  

<?xml version="1.0" encoding="UTF-8"?>

<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>

  <groupId>com.juvenxu.mvnbook</groupId>

  <artifactId>hello-world</artifactId>

  <version>1.0-SNAPSHOT</version>

  <name>Maven Hello World Project</name>

  <dependencies>

    <dependency>

       <groupId>junit</groupId>

       <artifactId>junit</artifactId>

       <version>4.7</version>

       <scope>test</scope>

    </dependency>

  </dependencies>

</project>  

 1.添加了dependencies元素,該元素下可以包含多個dependency元素以聲明項目的依賴這裏我們添加了一個依賴——groupId是junit,artifactId是junit,version是4.7。前面我們提到groupId、artifactId和version是任何一個Maven項目最基本的座標,JUnit也不例外,有了這段聲明,Maven就能夠自動下載junit-4.7.jar。也許你會問,Maven從哪裏下載這個jar呢?在Maven之前,我們可以去JUnit的官網下載分發包。而現在有了Maven,它會自動訪問中央倉庫(http://repo1.maven.org/maven2/),下載需要的文件。讀者也可以自己訪問該倉庫,打開路徑junit/junit/4.7/,就能看到junit-4.7.pom和junit-4.7.jar。本書第6章會詳細介紹Maven倉庫及中央倉庫。

2.POM代碼中還有一個值爲test的元素scope,scope爲依賴範圍,若依賴範圍爲test則表示該依賴只對測試有效,換句話說,測試代碼中的import JUnit代碼是沒有問題的,但是如果我們在主代碼中用import JUnit代碼,就會造成編譯錯誤。如果不聲明依賴範圍,那麼默認值就是compile,表示該依賴對主代碼和測試代碼都有效

3.配置了測試依賴,接着就可以編寫測試類,回顧一下前面的HelloWorld類,現在我們要測試該類的sayHello()方法,檢查其返回值是否爲“Hello Maven”。在src/test/java目錄下創建文件,其內容如代碼清單3-4:

代碼清單3-4:Hello World的測試代碼

 

Java代碼  

package com.juvenxu.mvnbook.helloworld;

import static org.junit.Assert.assertEquals;

import org.junit.Test;

public class HelloWorldTest

{

    @Test

    public void testSayHello()

    {

        HelloWorld helloWorld = new HelloWorld();

        String result = helloWorld.sayHello();

        assertEquals( "Hello Maven", result );

    }

一個典型的單元測試包含三個步驟:一,準備測試類及數據;二,執行要測試的行爲;三,檢查結果上述樣例中,我們首先初始化了一個要測試的HelloWorld實例,接着執行該實例的sayHello()方法並保存結果到result變量中,最後使用JUnit框架的Assert類檢查結果是否爲我們期望的”Hello Maven”。在JUnit 3中,約定所有需要執行測試的方法都以test開頭,這裏我們使用了JUnit 4,但我們仍然遵循這一約定,在JUnit 4中,需要執行的測試方法都應該以@Test進行標註。

測試用例編寫完畢之後就可以調用Maven執行測試,運行 mvn clean test :

 

Java代碼  

[INFO] Scanning for projects...

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

[INFO] Building Maven Hello World Project

[INFO]    task-segment: [clean, test]

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

[INFO] [clean:clean {execution: default-clean}]

[INFO] Deleting directory D:\git-juven\mvnbook\code\hello-world\target

[INFO] [resources:resources {execution: default-resources}]

Downloading: http://repo1.maven.org/maven2/junit/junit/4.7/junit-4.7.pom     //Maven從中央倉庫下載了junit-4.7.pom到本地倉庫(~/.m2/repository)中,供所有Maven項目使用

1K downloaded  (junit-4.7.pom)

[INFO] [compiler:compile {execution: default-compile}]

[INFO] Compiling 1 source file to D: \code\hello-world\target\classes

[INFO] [resources:testResources {execution: default-testResources}]

Downloading: http://repo1.maven.org/maven2/junit/junit/4.7/junit-4.7.jar    /Maven從中央倉庫下載junit-4.7.jar到本地倉庫(~/.m2/repository)中,供所     有Maven項目使用

226K downloaded  (junit-4.7.jar)

[INFO] [compiler:testCompile {execution: default-testCompile}]

[INFO] Compiling 1 source file to D:\ code\hello-world\target\test-classes

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

[ERROR] BUILD FAILURE

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

[INFO] Compilation failure

D:\code\hello-world\src\test\java\com\juvenxu\mvnbook\helloworld\HelloWorldTest.java:[8,5] -source 1.3 中不支持註釋

(請使用 -source 5 或更高版本以啓用註釋)

    @Test

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

[INFO] For more information, run Maven with the -e switch

  …

  

不幸的是構建失敗了,不過我們先耐心分析一下這段輸出。命令行輸入的是mvn clean test,而Maven實際執行的可不止這兩個任務,還有clean:clean,resources:resources、compiler:compile、resources:testResources以及compiler:testCompile。暫時我們需要了解的是,在Maven執行測試(test)之前,它會先自動執行項目主資源處理,主代碼編譯,測試資源處理,測試代碼編譯等工作,這是Maven生命週期的一個特性,本書後續章節會詳細解釋Maven的生命週期。

構建在執行compiler:testCompile任務的時候失敗了,Maven輸出提示我們 的JUnit 4的@Test註解。這是Maven初學者常常會遇到的一個問題。由於歷史原因,Maven的核心插件之一compiler插件默認只支持編譯Java 1.3,因此我們需要配置該插件使其支持Java 5,見代碼清單3-5:

代碼清單3-5:配置maven-compiler-plugin支持Java 5

 

Java代碼   

<project>

  <build>

    <plugins>

       <plugin>

         <groupId>org.apache.maven.plugins</groupId>

         <artifactId>maven-compiler-plugin</artifactId>

         <configuration>

           <source>1.5</source>

           <target>1.5</target>

         </configuration>

       </plugin>

    </plugins>

  </build>

</project>  

該POM省略了除插件配置以外的其他部分,我們暫且不去關心插件配置的細節,只需要知道compiler插件支持Java 5的編譯。現在再執行mvn clean test,輸出如下:

 

Java代碼  

[INFO] [compiler:testCompile {execution: default-testCompile}]       //compiler:testCompile任務執行成功

[INFO] Compiling 1 source file to D: \code\hello-world\target\test-classes   //測試代碼通過編譯之後在target/test-classes下生成了二進制文件

[INFO] [surefire:test {execution: default-test}]

[INFO] Surefire report directory: D:\code\hello-world\target\surefire-reports   // surefire:test任務運行測試,surefire是Maven世界中負責執行測試的插件,這裏它運行測試用例HelloWorldTest,並且輸出測試報告,顯示一共運行了多少測試,失敗了多少,出錯了多少,跳過了多少

-------------------------------------------------------

 T E S T S

-------------------------------------------------------

Running com.juvenxu.mvnbook.helloworld.HelloWorldTest

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.055 sec

Results :

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

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

[INFO] BUILD SUCCESSFUL

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

3.4 打包和運行 

 

將項目進行編譯、測試之後,下一個重要步驟就是打包(package)。Hello World的POM中沒有指定打包類型,使用默認打包類型jar,我們可以簡單地執行命令 mvn clean package 進行打包,可以看到如下輸出:

 

Java代碼  

    …

    Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

    [INFO] [jar:jar {execution: default-jar}]    //我們看到jar:jar任務負責打包,實際上就是jar插件的jar目標將項目主代碼打包成一個名爲hello-world-1.0-SNAPSHOT.jar的文件,該文件也位於target/輸出目錄中,它是根據artifact-version.jar規則進行命名的如有需要,我們還可以使用finalName來自定義該文件的名稱

    [INFO] Building jar: D:\code\hello-world\target\hello-world-1.0-SNAPSHOT.jar

    [INFO] 

------------------------------------------------------------------------

    [INFO] BUILD SUCCESSFUL

    …

  

至此,我們得到了項目的輸出,如果有需要的話,就可以複製這個jar文件到其他項目的Classpath中從而使用HelloWorld類。但是,如何才能讓其他的Maven項目直接引用這個jar呢?我們還需要一個安裝的步驟,執行 mvn clean install

 

Java代碼  

    [INFO] [jar:jar {execution: default-jar}]

    [INFO] Building jar: D: \code\hello-world\target\hello-world-1.0-SNAPSHOT.jar

    [INFO] [install:install {execution: default-install}]    //打包之後,執行了安裝任務install:install

    [INFO] Installing D:\code\hello-world\target\hello-world-1.0-SNAPSHOT.jar to C:\Users\juven\.m2\repository\com\juvenxu\mvnbook\hello-world\1.0-SNAPSHOT\hello-world-1.0-SNAPSHOT.jar      //我們看到該任務將項目輸出的jar安裝到了Maven本地倉庫中我們可以打開相應的文件夾看到Hello World項目的pom和jar

    [INFO]

 ------------------------------------------------------------------------

    [INFO] BUILD SUCCESSFUL

    …

之前講述JUnit的POM及jar的下載的時候,我們說只有構件被下載到本地倉庫後,才能由所有Maven項目使用,這裏是同樣的道理,只有將Hello World的構件安裝到本地倉庫之後,其他Maven項目才能使用它。


總結

我們已經將體驗了Maven最主要的命令:mvn clean compile、mvn clean test、mvn clean package、mvn clean install。執行test之前是會先執行compile的,執行package之前是會先執行test的,而類似地,install之前會執行package。我們可以在任何一個Maven項目中執行這些命令,而且我們已經清楚它們是用來做什麼的。

到目前爲止,我們還沒有運行Hello World項目,不要忘了HelloWorld類可是有一個main方法的。默認打包生成的jar是不能夠直接運行的,因爲帶有main方法的類信息不會添加到manifest中我們可以打開jar文件中的META-INF/MANIFEST.MF文件,將無法看到Main-Class一行)。爲了生成可執行的jar文件,我們需要藉助maven-shade-plugin配置該插件如下:

 

Java代碼  

<plugin>

<groupId>org.apache.maven.plugins</groupId>

  <artifactId>maven-shade-plugin</artifactId>

  <version>1.2.1</version>

  <executions>

    <execution>

      <phase>package</phase>

      <goals>

        <goal>shade</goal>

      </goals>

      <configuration>

        <transformers>

          <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">      
        <mainClass>com.juvenxu.mvnbook.helloworld.HelloWorld</mainClass>  //我們配置了mainClass爲com.juvenxu.mvnbook.helloworld.HelloWorld,項目在打包時會將該信息放到MANIFEST中

         </transformer>

       </transformers>

     </configuration>

     </execution>

  </executions>

</plugin>

  

plugin元素在POM中的相對位置應該在<project><build><plugins>下面
現在執行 mvn clean install ,待構建完成之後打開target/目錄,我們可以看到hello-world-1.0-SNAPSHOT.jar和original-hello-world-1.0-SNAPSHOT.jar,
 前者是帶有Main-Class信息的可運行jar後者是原始的jar.
打開hello-world-1.0-SNAPSHOT.jar的META-INF/MANIFEST.MF,可以看到它包含這樣一行信息:

Main-Class: com.juvenxu.mvnbook.helloworld.HelloWorld

現在,我們在項目根目錄中執行該jar文件:

java -cp target/simple-1.0-SNAPSHOT.jar org.sonatype.mavenbook.App 

Hello Maven

控制檯輸出爲Hello Maven,這正是我們所期望的。


本小節介紹了Hello World項目,側重點是Maven而非Java代碼本身,介紹了POM、Maven項目結構、以及如何編譯、測試、打包,等等。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章