Maven實戰:Maven生命週期

五月的倉頡發表於2016-05-25

前言

之前有寫過一篇文章Maven實戰,介紹了Maven的一些基本概念,以及對於一個初學者而言的Maven基礎知識,當時在我看來掌握了這些基本是夠用的。

隨著工作的深入,越來越感覺對於Maven的理解不夠,很多時候使用Maven出了問題都無法很快地解決,因此打算深入地從搭建Maven工程開始學習一下Maven,這篇文章就將自己的學習歷程記錄下來和網友朋友們分享。

 

從搭建最簡單的Maven專案開始

LZ使用的是MyEclipse,那麼就是用MyEclipse搭建一個簡單的Maven專案。第一步,new一個Maven Project:

點選next:

這裡不推薦勾選"Create a simple project(skip archetype selection)",這樣就可以使用很多Maven Archetype,也就是Maven原型,點選next:

看到這裡出現了很多Maven推薦給開發者的原型,"org.apache.maven"開頭的都是maven官方推薦的,選擇一個簡單的"maven-archetype-quickstart",接著就是填入一些基本資訊:

這些資訊填寫比較自由,當然之後也會對Group Id、Artifact Id、Version、Package等再做解釋,點選finish,MyEclipse就給我們生成好了一個Maven工程:

這就是"maven-archetype-quickstart"原型生成的標準目錄結構,"src/main/java"用於編寫Java程式碼、"src/test/java"用於編寫測試類,看一下其中最重要的pom.xml:

<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/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>org.xrq.mvnstart</groupId>
  <artifactId>hello-world</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>hello-world</name>
  <url>http://maven.apache.org</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.10</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>

看到"maven-archetype-quickstart"原型給我們匯入了一個junit的jar包,用於單元測試。注意一下,預設生成JUnit版本是3.8.1的,該版本的JUnit不支援註解,後面的單元測試是用的註解,因此把這裡改成4.10的版本。

另外,還有個重點就是,最終打包出來的檔案型別是一個jar包(packaging)。

OK,那接下來我們就開始編寫Java程式碼以及測試類的程式碼。

 

編寫主程式碼及編譯

專案主程式碼和測試程式碼不同,專案的主程式碼會被打包到最終的構件中,而測試程式碼只在執行測試時用到,不會被打包。

生成的工程下,預設有一個App類,稍作修改:

public class App 
{
    public String sayHelloWorld()
    {
        return "Say Hello World!";
    }
    
    public static void main( String[] args )
    {
        System.out.println(new App().sayHelloWorld());
    }
}

程式碼寫完,接著編譯,由於Maven使用命令進行操作,因此進入控制檯,進入工程目錄,輸入"mvn clean compile":

此時工程的變化是,在target下有編譯好的App.java對應的App.class:

這條命令的意思是:

1、clean告訴Maven清理輸出目錄target/

2、compile告訴Maven編譯專案主程式碼

從輸出中可以看到Maven首先執行了clean:clean任務,刪除target/目錄;緊接著執行resources:resources任務(因為未定義專案資源,因此此項略過);最後執行compiler:compile任務,將專案主程式碼編譯至target/classes目錄。

要說一點,clean:clean、resources:resources、compiler:compile對應了一些Maven外掛及外掛目標,比如clean:clean是clean外掛的clean目標,compiler:compile是compiler外掛的compile目標,這在後面的文章中會說明。

 

編寫測試程式碼及編譯

為了保證專案結構清晰,主程式碼與測試程式碼應當分別位於獨立的目錄中。Maven專案中預設的主程式碼目錄是src/main/java,相應地Maven專案中預設的測試程式碼目錄是src/test/java,因此如果在編寫測試用例之前沒有該目錄,應當首先建立該目錄。

測試程式碼為:

public class AppTest 
{
    @Test
    public void testSayHelloWorld()
    {
        App app = new App();
        String result = app.sayHelloWorld();
        Assert.assertEquals(result, "Say Hello World!");
    }
}

用了JUnit,上面的pom.xml中依賴項,JUnit的scope是test,這表示JUnit的包只對測試目錄是有效的,換句話說,在主目錄下使用JUnit將會導致編譯報錯。

這裡還有一個問題要特別說明,由於歷史原因,Maven的核心外掛之一compiler外掛預設只支援編譯Java1.3,這個版本並不支援JUnit,因此需要配置該外掛使其支援Java5才可以使用JUnit。因此在pom.xml中加入一段:

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

這樣就使得外掛支援Java5編譯。此時命令列中輸入mvn clean test:

測試通過,沒有問題。

 

打包

在專案編譯、測試通過之後,就會進行一項操作,就是打包(package)。

同樣,使用命令列操作,輸入"mvn clean package",打包前會先編譯、測試,這些資訊就不擷取了,只看打包的部分:

打包的名字是按照artifactId+version來的,如有需要也可以自定義,這個這裡也不講了。

打包完畢,再看一下工程目錄:

多出了一個jar檔案,這樣就可以在別的地方使用這個jar檔案了。

 

安裝

上面生成的jar檔案,可以直接複製到別的專案的CLASSPATH下供其他專案使用,那麼如何讓其他專案直接使用這個jar包呢?可以使用install。

在命令列中輸入"mvn clean install",同樣我只擷取關鍵的部分:

看到生成的jar和pom被安裝到本地倉庫中了,只有當構建被下載到本地倉庫之後,才能供其他Maven專案使用。這裡也是同樣道理,只有將Hello World的構件安裝到本地倉庫之後,其他Maven專案才能使用它。

 

Maven生命週期

上面的內容,包含了Mavan最主要的命令:mvn clean compile、mvn clean test、mvn clean package、mvn clean install。執行test之前,先執行compile、執行package之前先執行test、執行install之前先執行package,這就涉及到一個概念:Maven生命週期

在Maven中有三套獨立的生命週期:

  • Clean Lifecycle:在進行真正的構建之前進行一些清理工作
  • Default Lifecycle:構建的核心部分,編譯、測試、打包、部署
  • Site Lifecycle:生成專案報告、生成站點、釋出站點

第一個和第三個比較簡單也比較好理解,看一下Maven的最重要的Default生命週期,絕大部分工作都發生在這個生命週期中,在這個階段中,比較重要和常用的階段有:

  • validate
  • generate-sources
  • process-sources
  • generate-resources
  • process-resources:複製並處理資原始檔至目標目錄,準備打包
  • compile:編譯專案原始碼
  • process-clases
  • generate-test-sources
  • procss-test-sources
  • generate-test-resources
  • process-test-resources:複製並處理資原始檔至目標測試目錄
  • test-compile:編譯測試原始碼
  • process-test-classes
  • test:使用合適的單元測試框架測試執行,這些測試程式碼將不會被打包或部署
  • prepare-package
  • package:接受編譯好的程式碼,打包成可釋出的格式,如jar
  • pre-integration-test
  • integration-test
  • post-integration-test
  • verify
  • install:將包安裝至本地倉庫,以便讓其它專案依賴
  • deploy:將最終的包複製到遠端倉庫,以便讓其它開發人員與專案共享

基本上,只要根據名稱我們就可以猜測出每個階段的用途。要記住的是,任何一個階段的時候,它前面的所有階段都會被執行,這也就是為什麼我們執行mvn clean install的時候,程式碼會被編譯、測試、打包。

此外,Maven的外掛機制是完全依賴Maven生命週期的,因此理解生命週期至關重要。

相關文章