讀古今文學網 > Netty實戰 > 附錄 Maven介紹 >

附錄 Maven介紹

本附錄提供了對Apache Maven(http://maven.apache.org/what-is-maven.html)的基本介紹。在讀過之後,你應該能夠通過復用本書示例中的配置來啟動你自己的項目。

Maven是一個強大的工具,學習的回報很大。如果希望瞭解更多,你可以在http://maven. apache.org找到官方文檔,在www.sonatype.com/resources/books找到一套極好的免費的PDF格式的書。

第一節將介紹Maven的基本概念。在第二節中,我們將使用本書示例項目中的示例來說明這些基本概念。

A.1 什麼是Maven

Maven是一種用來管理Java項目的工具,但不是那種用來管理資源規劃和調度的工具。相反,它處理的是管理一個具體的項目所涉及的各種任務,如編譯、測試、打包、文檔以及分發。

Maven包括以下的幾個部分。

  • 一組用於處理依賴管理、目錄結構以及構建工作流的約定。基於這些約定實現的標準化可以極大地簡化開發過程。例如,一個常用的目錄結構使得開發者可以更加容易地跟上不熟悉的項目的節奏。
  • 一個用於項目配置的XML Schema:項目對像模型(Project Object Model),簡稱POM[1]。每一個Maven項目都擁有一個POM文件[2],默認命名為pom.xml,包含了Maven用於管理該項目的所有的配置信息。
  • 一個委託外部組件來執行項目任務的插件架構。這簡化了更新以及擴展Maven能力的過程。

構建和測試我們的示例項目只需要用到Maven多種特性的一個子集。這些也是我們將在本附錄中所討論的內容,其中不包括那些在生產部署中肯定需要用到的特性。我們將會涵蓋的主題包括以下內容。

  • 基本概念:構件、坐標以及依賴。
  • 關鍵元素以及Maven項目描述符(pom.xml)的用法。
  • Maven構建的生命週期以及插件。

A.1.1 安裝和配置Maven

可以從http://maven.apache.org/download.cgi下載適合於你的系統的Maven tar.gz或者zip文件。安裝非常簡單:將該歸檔內容解壓到你選擇的任意文件夾(我們稱之為<安裝目錄>)中。這將創建目錄<安裝目錄>\apache-maven-3.3.9[3]。

然後,

  • 將環境變量M2_HOME設置為指向<安裝目錄>\apache-maven-3.3.9,這個環境變量將會告訴Maven在哪裡能找到它的配置文件,conf\settings.xml
  • %M2_HOME%\bin(在Linux上是${M2_HOME}/bin)添加到你的執行路徑,在這之後,在命令行執行mvn就能運行Maven了。

在編譯和運行示例項目時,不需要修改默認配置。在首次執行mvn時,Maven會為你創建本地存儲庫[4],並從Maven中央存儲庫下載基本操作所需的大量JAR文件。最後,它會下載構建當前項目所需要的依賴項(包括Netty的JAR包)。關於自定義settings.xml的詳細信息可以在http://maven.apache.org/settings.html找到。

A.1.2 Maven的基本概念

在下面的章節中,我們將解釋Maven的幾個最重要的概念。熟悉這些概念將有助於你理解POM文件的各個主要元素。

1.標準的目錄結構

Maven定義了一個標準的項目目錄結構[5]。並不是每種類型的項目都需要Maven的所有元素,很多都可以在必要的時候在POM文件中重寫。表A-1展示了一個基本的WAR項目,有別於JAR項目,它擁有src/main/webapp文件夾。當Maven構建該項目時,該目錄(其中包含WEB-INF目錄)的內容將會被放置在WAR文件的根路徑上。位於該文件樹根部的${project.basedir}是一個標準的Maven屬性,標識了當前項目的根目錄。

表A-1 基本的項目目錄結構

文 件 夾

描  述

\${project.basedir}

項目根路徑

|---\src

源代碼根路徑

   |---\main

程序代碼

      |---\java

Java源代碼

      |---\resources

屬性文件、XML schema等

      |---\webapp

Web應用程序資源

    |---\test

測試源代碼根路徑

       |---\java

Java源代碼,如JUnit測試類

       |---\resources

屬性文件、XML schema等

|---\target

由構建過程所創建的文件

2.POM大綱

代碼清單A-1是我們的一個示例項目的POM文件的大綱。只顯示了頂層的schema元素。其中的一些也是其他元素的容器。

代碼清單A-1 POM文件的大綱

<project>  ← --  根元素
  <groupId/> 
  <artifactId/>  ← --  唯一地定義一個Maven 項目的值
  <version/> 
  <packaging/>  ← --   該項目所產生的構件的類型;默認值是jar
  <properties/>  ← -- 在POM 中所引用的符號
  <dependencies/>  ← -- 構建當前項目所需要的其他Maven 項目  
  <build/>  ← -- 構建該項目所需要執行的任務的配置  
  <profiles/>  ← -- 為不同的用例自定義POM的命名的配置  
</project>  

我們將在本節剩下的部分中更加詳細地討論這些元素。

3.構件

任何可以被Maven的坐標系統(參見接下來的關於GAV坐標的討論)唯一標識的對象都是一個Maven構件。大多數情況下,構件是構建Maven項目所生成的文件,如JAR。但是,只包含其他POM(該文件本身並不產生構件)使用的定義的POM文件也是Maven構件。

Maven構件的類型由其POM文件的<packaging>元素指定。最常用的值是pomjarearwar以及maven-plugin

4.POM文件的用例

可以通過以下的方式來使用POM文件。

  • 默認的——用於構建一個構件。
  • 父POM——提供一個由子項目繼承的單個配置信息源——聲明這個POM文件作為它們的<parent>元素的值。
  • 聚合器——用於構建一組聲明為<modules>的項目,這些子項目位於其當前聚合器項目的文件夾中,每個都包含有它自己的POM文件。

作為父POM或者聚合器的POM文件的<packaging>元素的值將是pom。注意,一個POM文件可能同時提供兩項功能。

5.GAV坐標

POM定義了5種稱為坐標的元素,用於標識Maven構件。首字母縮寫GAV指的是必須始終指定的3個坐標<groupId><artifactId>以及<version>的首字母。

下面的坐標是按照它們在坐標表達式中出現的順序列出的。

(1)<groupId>是項目或者項目組的全局的唯一標識符。這通常是Java源代碼中使用的全限定的Java包名。例如,io.netty、com.google。

(2)<artifactId>用於標識和某個<groupId>相關的不同的構件。例如,netty-allnetty-handler

(3)<type>是指和項目相關的主要構件的類型(對應於構件的POM文件中的<packaging>值)。它的默認值是jar。例如,pomwarear

(4)<version>標識了構件的版本。例如,1.1、2.0-SNAPSHOT[6]、4.1.9.Final。

(5)<classifier>用於區分屬於相同的POM但是卻被以不同的方式構建的構件。例如,javadoc、sources、jdk16、jdk17。

一個完整的坐標表達式具有如下格式:

artifactId:groupId:packaging:version:classifier  

下面的GAV坐標標識了包含所有Netty組件的JAR:

io.netty:netty-all:4.1.9.Final  

POM文件必須聲明它所管理的構件的坐標。一個具有如下坐標的項目:

<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.9.Final</version>
<packaging>jar</packaging>  

將會產生一個具有以下格式的名稱的構件:

<artifactId>-<version>.<packaging>  

在這種情況下,它將產生這個構件:

netty-all-4.1.9.Final.jar  

6.依賴

項目的依賴是指編譯和執行它所需要的外部構件。在大多數情況下,你的項目的依賴項也會有它自己的依賴。我們稱這些依賴為你的項目的傳遞依賴。一個複雜的項目可能會有一個深層級的依賴樹;Maven提供了各種用於幫助理解和管理它的工具。[7]

Maven的<dependency>[8]聲明在POM的<dependencies>元素中:

<dependencies>
  <dependency>
    <groupId/>
    <artifactId/>
    <version/>
    <type/>
    <scope/>
    <systemPath/>
  </dependency>
  ...
</dependencies>  

<dependency>聲明中,GAV坐標總是必不可少的[9]。type以及scope元素對於那些值不分別是默認值jarcompile的依賴來說也是必需的。

下面的代碼示例是從我們示例項目的頂級POM中摘錄的。注意第一個條目,它聲明了對我們先前提到的Netty JAR的依賴。

<dependencies>
  <dependency>
    <groupId>io.netty<groupId>
    <artifactId>netty-all</artifactId>
     <version>4.1.9.Final</version>
  </dependency>
  <dependency>
    <groupId>nia</groupId>
    <artifactId>util</artifactId>
    <version>1.0-SNAPSHOT</version>
  </dependency>
  <dependency>
    <groupId>com.google.protobuf</groupId>
    <artifactId>protobuf-java</artifactId>
    <version>2.5.0</version>
  </dependency>
  <dependency>
    <groupId>org.eclipse.jetty.npn</groupId>
    <artifactId>npn-api</artifactId>=
    <version>1.1.0.v20120525</version>
  </dependency>
  <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.11</version>
    <scope>test</scope>
  </dependency>
</dependencies>  

<scope>元素可以具有以下值。

  • compile——編譯和執行需要的(默認值)。
  • runtime——只有執行需要。
  • optional——不被引用了這個項目所產生的構件的其他項目,視為傳遞依賴。
  • provided——不會被包含在由這個POM產生的WAR文件的WEB_INF/lib目錄中。
  • test——只有編譯和測試的執行需要。
  • import——這將在後面的「依賴管理」一節進行討論。

<systemPath>元素用來指定文件系統中的絕對位置。

Maven用來管理項目依賴的方式,包括了一個用來存儲和獲取這些依賴的存儲庫協議,已經徹底地改變了在項目之間共享JAR文件的方式,從而有效地消除了項目的中每個開發人員都維護一個私有lib目錄時經常會出現的問題。

7.依賴管理

POM的<dependencyManagement>元素包含可以被其他項目使用的<dependency>聲明。這樣的POM的子項目將會自動繼承這些聲明。其他項目可以通過使用<scope>元素的import值來導入它們(將在稍後討論)。

引用了<dependencyManagement>元素的項目可以使用它所聲明的依賴,而不需要指定它們的<version>坐標。如果<dependencyManagement>中的<version>在稍後有所改變,則它將被所有引用它的POM拾起。

在下面的示例中,所使用的Netty版本是在POM的<properties>部分中定義,在<dependencyManagement>中引用的。

<properties>
  <netty.version>4.1.9</netty.version>
  ...
  ...
</properties>
<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>io.netty</groupId>
      <artifactId>netty-all</artifactId>
      <version>${netty.version}</version>
    </dependency>
  </dependencies>
  ...
</dependencyManagement>  

對於這種使用場景,依賴的<scope>元素有一個特殊的import值:它將把外部POM(沒有被聲明為<parent>)的<dependencyManagement>元素的內容導入到當前POM的<dependency Management>元素中。

8.構建的生命週期

Maven構建的生命週期是一個明確定義的用於構建和分發構件的過程。有3個內置的構建生命週期:cleandefaultsite。我們將只討論其中的前兩個,分別用於清理和分發項目。

一個構建的生命週期由一系列的階段所組成。下面是默認的構建生命週期的各個階段的一個部分清單。

  • validate——檢查項目是否正確,所有必需的信息是否已經就緒。
  • process-sources——處理源代碼,如過濾任何值。
  • compile——編譯項目的源代碼。
  • process-test-resources——複製並處理資源到測試目標目錄中。
  • test-compile——將測試源代碼編譯到測試目標目錄中。
  • test——使用合適的單元測試框架測試編譯的源代碼。
  • package——將編譯的代碼打包為它的可分發格式,如JAR。
  • integration-test——處理並將軟件包部署到一個可以運行集成測試的環境中。
  • verify——運行任何的檢查以驗證軟件包是否有效,並且符合質量標準。
  • install——將軟件包安裝到本地存儲庫中,在那裡其他本地構建項目可以將它引用為依賴。
  • deploy——將最終的構件上傳到遠程存儲庫,以與其他開發人員和項目共享。

執行這些階段中的一個階段將會調用所有前面的階段。例如:

mvn package  

將會執行validatecompile以及test,並隨後將該構件組裝到該項目的目標目錄中。

執行

mvn clean install  

將會首先移除所有先前的構建所創建的結果。然後,它將會運行所有到該階段的默認階段,並且包括將該構件放置到你的本地存儲庫的文件系統中。

雖然我們的示例項目可以通過這些簡單的命令來構建,但是任何使用Maven的重要工作都需要詳細瞭解構建生命週期的各個階段。[10]

9.插件

雖然Maven協調了所有構建生命週期階段的執行,但是它並沒有直接實現它們,相反,它將它們委託給了插件[11]**,這些插件是maven-plugin類型的構件(打包為JAR文件)。Apache Maven項目為標準構建生命週期所定義的所有任務都提供了插件,更多的是由第三方生產的,用於處理各種自定義的任務。

插件可能擁有多個內部步驟,或者目標,其也可以被單獨調用。例如,在一個JAR項目中,默認的構建生命週期由maven-jar-plugin處理,其將構建的各個階段映射到了它自己的以及其他插件的目標中,如表A-2所示。

表A-2 階段、插件以及目標

階  段

插件:目標

process-resources

resources:resources

compile

compiler:compiler

process-test-resources

resources:testResources

test-compile

compiler:testCompile

test

surefire:test

package

jar:jar

install

install:install

deploy

deploy:deploy

在我們的示例項目中,我們使用了下面的第三方插件來從命令行執行我們的項目。注意插件的聲明,它被打包為JAR包,使用了和<dependency>的GAV坐標相同的GAV坐標。

<plugin>
  <groupId>org.codehaus.mojo</groupId>
  <artifactId>exec-maven-plugin</artifactId>
  <version>1.2.1</version>
</plugin>  

10.插件管理

如同<dependencyManagement><pluginManagement>聲明了其他POM可以使用的信息,如代碼清單A-2所示。但是這只適用於子POM,因為對於插件來說,沒有導入聲明。和依賴一樣,<version>坐標是繼承的。

代碼清單A-2 pluginManagement

<build>
  <pluginManagement>
    <plugins>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.2</version>
        <configuration>
          <source>1.7</source>
          <target>1.7</target>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>exec-maven-plugin</artifactId>
        <version>1.2.1</version>
      </plugin>
    </plugins>
  </pluginManagement>
</build> 

代碼清單A-3展示了代碼清單A-2中的POM片段的子POM是如何使用其父POM的<pluginManagement>配置的,它只引用了其構建所需的插件。子POM還可以重寫它需要自定義的任何插件配置。

關於Maven插件

在聲明由Maven項目生成的插件時,可以省略groupId(org.apache.maven.plugins),如代碼清單A-2中的maven-compiler-plugin的聲明中所示。此外,保留了以「maven」開頭的artifactId,僅供Maven項目使用。例如,第三方可以提供一個artifactIdexec-maven-plugin的插件,但是不能為maven-exec-plugin

POM定義了一個大多數插件都需要遵從的插件配置格式。

更多的信息參見Maven的「插件配置指南」(http://maven.apache.org/guides/mini/guide-configuring- plugins.html)。這將幫助你設置你想要在自己的項目中使用的任何插件。

代碼清單A-3 插件繼承

<build>
  <plugins>
    <plugin>
      <artifactId>maven-compiler-plugin</artifactId>
    </plugin>
    <plugin>
      <groupId>org.codehaus.mojo</groupId>
      <artifactId>exec-maven-plugin</artifactId>
    </plugin>
  </plugins>
</build>  

11.配置文件

配置文件(在<profiles>中定義)是一組自定義的POM元素,可以通過自動或者手動啟用(激活)來改變POM的行為。例如,你可以定義一個配置文件,它將根據JDK版本、操作系統或者目標部署環境(如開發、測試或者生產環境)來設置構建參數。

可以通過命令行的-P標誌來顯式地引用配置文件。下面的例子將激活一個將POM自定義為使用JDK1.6的配置文件。

mvn -P jdk16 clean install  

12.存儲庫

Maven的構件存儲庫[12]可能是遠程的,也可能是本地的。

  • 遠程存儲庫是一個Maven從其下載POM文件中所引用的依賴的服務。如果你有上傳權限,那麼這些依賴中可能也會包含由你自己的項目所產生的構件。大量開放源代碼的Maven項目(包含Netty)都將它們的構件發佈到可以公開訪問的Maven存儲庫。
  • 本地存儲庫是一個本地的目錄,其包含從遠程存儲庫下載的構件,以及你在本地機器上構建並安裝的構件。它通常放在你的主目錄下,如:
  C:\Users\maw\.m2\repository  

Maven存儲庫的物理目錄結構使用GAV坐標,如同Java編譯器使用包名一樣。例如,在Maven下載了下面的依賴之後:

<dependency>
  <groupId>io.netty</groupId>
  <artifactId>netty-all</artifactId>
  <version>4.1.9.Final</version>
</dependency>  

將會在本地存儲庫中找到以下內容:

.m2\repository
|---\io
  |---\netty
    |---\netty-all
      |---\4.1.9.Final
          netty-all-4.1.9.Final.jar
          netty-all-4.1.9.Final.jar.sha1
          netty-all-4.1.9.Final.pom
          netty-all-4.1.9.Final.pom.sha1
          _maven.repositories  

13.快照和發佈

遠程存儲庫通常會為正在開發的構件,以及那些穩定發佈或者生產發佈的構件,定義不同的區域。這些區域被分別稱為快照存儲庫和發佈存儲庫。

一個<version>值由-SNAPSHOT結尾的構件將被認為是還沒有發佈的。這種構件可以重複地使用相同的<version>值被上傳到存儲庫。每次它都會被分配一個唯一的時間戳。當項目檢索構件時,下載的是最新實例。

一個<version>值不具有-SNAPSHOT後綴的構件將會被認為是一個發佈版本。通常,存儲庫策略只允某一特定的發佈版本上傳一次。

當構建一個具有SNAPSHOT依賴的項目時,Maven將檢查本地存儲庫中是否有對應的副本。如果沒有,它將嘗試從指定的遠程存儲庫中檢索,在這種情況下,它將接收到具有最新時間戳的構件。如果本地的確有這個構件,並且當前構建也是這一天中的第一個,那麼Maven將默認嘗試更新該本地副本。這個行為可以通過使用Maven配置文件(settings.xml)中的配置或者命令行標誌來進行配置。

A.2 POM示例

在這一節中,我們將通過介紹一些POM示例來說明我們在前一節中所討論的主題。

A.2.1 一個項目的POM

代碼清單A-4展示了一個POM,其為一個簡單的Netty項目創建了一個JAR文件。

代碼清單A-4 獨立的pom.xml

<?xml version="1.0" encoding="ISO-8859-15"?>
<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>   ← --  該項目的GAV 坐標

  <groupId>com.example</groupId>  
  <artifactId>myproject</artifactId>
  <version>1.0-SNAPSHOT</version>

  <packaging>jar</packaging>  ← -- 該項目產生的構件將是一個JAR文件(默認值)

  <name>My Jar Project</name>

  <dependencies>
    <dependency>  ← -- 這個POM 只聲明了Netty JAR作為依賴;一個典型的Maven項目會有許多依賴 
      <groupId>io.netty</groupId>
      <artifactId>netty-all</artifactId>
      <version>4.1.9.Final</version>
    </dependency>
  </dependencies>

  <build>  ← -- <build>部分聲明了用於執行構建任務的插件。我們只自定義了編譯器插件,對於其他的插件,我們接受默認值
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.2</version>
        <configuration>
          <source>1.7</source>
          <target>1.7</target>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>  

這個POM創建的構件將是一個JAR文件,其中包含從項目的Java源代碼編譯而來的類。在編譯的過程中,被聲明為依賴的Netty JAR將會被添加到CLASSPATH中。

下面是使用這個POM時會用到的基本Maven命令。

  • 在項目的構建目錄(「target」)中創建JAR文件:
  mvn package  
  • 將該JAR文件存儲到本地存儲庫中:
  mvn install  
  • 將該JAR文件發佈到全局存儲庫中(如果已經定義了一個):
  mvn deploy  

A.2.2 POM的繼承和聚合

正如我們之前所提到的,有幾種使用POM的方式。在這裡,我們將討論它作為父POM或者聚合器的用法。

1.POM繼承

POM文件可能包含子項目要繼承(並可能重寫)的信息。

2.POM聚合

聚合器POM會構建一個或者多個子項目,這些子項目駐留在該POM所在目錄的子目錄中。子項目,或者<modules>標籤,是由它們的目錄名標識的:

<modules>
  <module>Server</module>
  <module>Client</module>
</modules>  

當構建子項目時,Maven將創建一個reactor,它將計算存在於它們之間的任何依賴,以確定它們必須遵照的構建順序。注意,聚合器POM不一定是它聲明為模塊的項目的父POM。(每個子項目都可以聲明一個不同POM作為它的<parent>元素的值。)

用於第2章的Echo客戶端/服務器項目的POM既是一個父POM,也是一個聚合器[13]。示例代碼根目錄下的chapter2目錄,包含了代碼清單A-5中所展示的內容。

代碼清單A-5 chapter2目錄樹

chapter2
  |---pom.xml    ← --  父級/聚合器POM
  |---\Client  ← -- 客戶端模塊
    |---pom.xml
    |---\src
      |---\main
        |---\java
          |---\nia
            |---\chapter2
              |---\echoclient
                EchoClient.java
                EchoClientHandler.java
  |---\Server  ← --  服務器模塊
    |---pom.xml
    |---\src
      |---\main
        |---\java
          |---\nia
            |---\chapter2
              |---\echoserver
                EchoServer.java
                EchoServerHandler.java  

代碼清單A-6所示的根級POM的打包類型是<pom>,這表示它本身並不產生構件。相反,它會為將它聲明為<parent>的項目提供配置信息,如該Client和Server項目。它也是一個聚合器,這意味著你可以通過在chapter2目錄中運行mvn install來構建它的<modules>中所定義的模塊。

代碼清單A-6 父級和聚合器POM:echo-parent

<project>
  <modelVersion>4.0.0</modelVersion>

  <parent>  ← --  <parent>聲明了samples-parentPOM 作為這個POM 的父POM
    <groupId>nia</groupId>
    <artifactId>nia-samples-parent</artifactId>
    <version>1.0-SNAPSHOT</version>
  </parent>

  <artifactId>chapter2</artifactId>
  <packaging>pom</packaging> 
  <name>2. Echo Client and Server</name>

  <modules> ← --  <modules>聲明了父POM 下的目錄,其中包含將由這個POM 來構建的Maven 項目 
    <module>Client</module>
    <module>Server</module>
  </modules>

  <properties>  ← -- <property>值可以通過在命令行上使用Java 系統屬性(-D)進行重寫。屬性由子項目繼承 
    <echo-server.hostname>localhost</echo-server.hostname>
    <echo-server.port>9999</echo-server.port>
  </properties> 

  <dependencies>  ← -- 父POM 的<dependencies>元素由子項目繼承 
    <dependency>
      <groupId>io.netty</groupId>
      <artifactId>netty-all</artifactId>
    </dependency>
  </dependencies>

  <build>
    <plugins>  ← -- 父POM 的<plugins>元素由子項目繼承  
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
      </plugin>
      <plugin>
        <artifactId>maven-failsafe-plugin</artifactId>
      </plugin>
      <plugin>
        <artifactId>maven-surefire-plugin</artifactId>
      </plugin>
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>exec-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </build>
</project>  

得益於Maven對於繼承的支持,該Server和Client項目的POM並沒有太多的事情要做。代碼清單A-7展示了該Server項目的POM。(該Client項目的POM幾乎是完全相同的。)

代碼清單A-7 Echo-服務器項目的POM

<project>  ← --  <parent>聲明了父POM
  <parent>
    <groupId>nia</groupId> 
    <artifactId>chapter2</artifactId>
    <version>1.0-SNAPSHOT</version>
  </parent>

  <artifactId>echo-server</artifactId>   ← --  <artifactId>必須聲明,因為對於該子項目來說它是唯一的。<groupId>和<version>,如果沒有被定義則從父POM 繼承

  <build>
    <plugins>    ← --  exec-maven-plugin 插件可以執行Maven 命令行的任意命令;在這裡,我們用它來運行Echo 服務器
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>exec-maven-plugin</artifactId>
        <executions>
          <execution>
            <id>run-server</id>
            <goals>
              <goal>java</goal>
            </goals>
          </execution>
        </executions>
        <configuration> 
          <mainClass>nia.echo.EchoServer</mainClass>  
          <arguments>
            <argument>${echo-server.port}</argument>
          </arguments>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>  

這個POM非常簡短,因為它從它的父POM和祖父POM(甚至還有一個曾祖父級別的POM存在——Maven Super POM)那裡繼承了非常多的信息。注意,例如,${echo-server.port}屬性的使用,其繼承自父POM。

Maven執行的POM,在組裝了所有的繼承信息並應用了所有的活動配置文件之後,被稱為「有效POM」。要查看它,請在任何POM文件所在的目錄中執行下面的Maven命令:

mvn help:effective-pom  

A.3 Maven命令行

mvn命令的語法如下:

mvn [options] [<goal(s)>] [<phase(s)>]  

有關其用法的詳細信息,以及有關我們在這個附錄中所討論的許多主題的更多信息,參見Sonatype的《Maven: The Complete Reference》,這是一個很好的資源。[14]

表A-3展示了mvn的命令行選項,這些選項可以通過執行。

mvn –help  

來顯示。

表A-3 mvn的命令行參數

選  項

描  述

-am,--also-make

如果指定了項目列表,還會構建列表所需的項目

-amd,--also-make-dependents

如果指定了項目列表,還會構建依賴於列表中的項目的項目

-B,--batch-mode

在非交互(批處理)模式下運行

-b,--builder <arg>

要使用的構建策略的id

-C,--strict-checksums

如果校驗和不匹配,則讓這次構建失敗

-c,--lax-checksums

如果校驗和不匹配,則發出警告

-cpu,--check-plugin-updates

無效,只是為了保持向後的兼容性

-D,--define <arg>

定義一個系統屬性

-e,--errors

生成執行錯誤的信息

-emp,--encrypt-master-password <arg>

加密主安全密碼

-ep,--encrypt-password <arg>

加密服務器密碼

-f,--file <arg>

強制使用備用的POM文件(或者包含pom.xml的目錄)

-fae,--fail-at-end

只在最後讓構建失敗,允許所有不受影響的構建繼續進行

-ff,--fail-fast

在反應化的構建中,首次失敗便停止構建

-fn,--fail-never

不管項目的結果如何,都決不讓構建失敗

-gs,--global-settings <arg>

全局設置文件的備用路徑

-h,--help

顯示幫助信息

-l,--log-file <arg>

所有構建輸出的日誌文件的位置

-llr,--legacy-local-repository

使用Maven2的遺留本地存儲庫(Legacy Local Repository)行為;也就是說,不使用_remote.repositories。也可以通過使用-Dmaven.legacyLocalRepo=true.激活

-N,--non-recursive

不遞歸到子項目中

-npr,--no-plugin-registry

無效,只是為了保持向後的兼容性

-npu,--no-plugin-updates

無效,只是為了保持向後的兼容性

-nsu,--no-snapshot-updates

取消快照更新

-o,--offline

脫機工作

-P,--activate-profiles <arg>

等待被激活的由逗號分隔的配置文件列表

-pl,--projects <arg>

構建由逗號分隔的指定的reactor項目,而不是所有項目。 項目可以通過[groupId]:artifactId或者它的相對路徑來指定

-q,--quiet

靜默輸出,只顯示錯誤

-rf,--resume-from <arg>

從指定的項目恢復reactor

-s,--settings <arg>

用戶配置文件的備用路徑

-T,--threads <arg>

線程數目,如2.0C,其中C是乘上的CPU核心數

-t,--toolchains <arg>

用戶工具鏈文件的備用路徑

-U,--update-snapshots

強制檢查缺少的發佈,並更新遠程存儲庫上的快照

-up,--update-plugins

無效,只是為了保持向後的兼容性

-V,--show-version

顯示版本信息而不停止構建

-v,--version

顯示版本信息

--debug

生成執行調試輸出

A.4 小結

在本附錄中,我們介紹了Apache Maven,涵蓋了它的基本概念和主要的用例。我們通過本書示例項目中的例子說明了這一切。

我們目標是幫助你更好地理解這些項目的構建方式,並為獨立開發提供了一個起點。


[1] Maven項目,「What is a POM?」:http://maven.apache.org/guides/introduction/introduction-tothe-pom.html。

[2] 在http://maven.apache.org/ref/3.3.9/maven-model/maven.html有關於POM的詳細描述。

[3] 在本書中文版出版時,Maven的最新版本是3.3.9。

[4] 在默認情況下,這是你當前操作系統的HOME目錄下的.m2/repository目錄。

[5] 有關標準目錄結構的優點,參考http://maven.apache.org/guides/introduction/introductionto-the-standard- directory-layout.html。

[6] 有關SNAPSHOT構件的更多信息參見本節後面關於「快照和發佈」的討論。

[7] 例如,在擁有POM文件的項目目錄中,在命令行執行「mvn dependency:tree」。

[8] 管理依賴項:http://maven.apache.org/guides/introduction/introduction-to-dependencymechanism.html。

[9] 參見後面的「依賴管理」小節。

[10] 「Introduction to the Build Lifecycle」:http://maven.apache.org/guides/introduction/introduction-to-thelifecycle.html。

[11] 「Available Plugins」:http://maven.apache.org/plugins/index.html。

[12] 參考http://maven.apache.org/guides/introduction/introduction-to-repositories.html。

[13] 它也是它的上級目錄中的nia-samples-parent POM的一個子POM,繼承了其元素的值,並傳遞給了它自己的子項目。

[14] 參見http://books.sonatype.com/mvnref-book/pdf/mvnref-pdf.pdf。