【構建工具】《Maven實戰》讀書筆記

  • 2019 年 11 月 2 日
  • 筆記

Maven是我們在做Java開發過程中用經常用到的一個輔助工具。本篇部落格是我學習Maven的一個記錄部落格,學習過程主要參考《Maven實戰》這本書。同時也參考了Maven的官方文檔。


1. Maven簡介

1.1 什麼是Maven

Maven是一個開源的Java項目構建、依賴管理和項目資訊管理工具。

1.2 何為構建

所謂構建是指項目編譯、運行單元測試、生成項目文檔、打包和項目部署等一系列操作。

1.3 Maven的優點

Maven是一個優秀的構建工具:幫我們規範了項目的構建過程,我們不在需要像以前那樣自己編寫不通用的項目構建腳本,大大降低自寫腳本出錯的效率,同時也規範了團隊項目的標準化。只需執行一些簡單的Maven命令就可以實現項目構建工作。另外,Maven還提供了很多現成插件來完成各種構建任務,不需要我們自己去寫腳本。

Maven抽象了一個完整的項目構建的生命周期模型,這個模型吸取了其他構建工具的優點,總結了大量項目的實際需求。如果遵循這個模型,可以避免很多不必要的錯誤。Maven已經提供了大量成熟的Maven插件來完成它抽象的這套生命周期模型,如果我們的項目有特殊的構建需求,可以通過實現自己的構建插件來完成項目特殊的構建需求。

統一的依賴管理(中央倉庫)。

Maven還可以管理如下資訊:項目描述、開發者列表、版本控制系統地址、許可證和缺陷管理系統地址等。

1.3 同類技術對比

在Maven之前,Make和Ant也是兩個比較流行的項目構建工具。但是使用這兩個工具,針對每個項目我們必須自己編寫項目構建腳本,而且這個兩個工具都沒有依賴管理的功能(Ant依賴於Lvy進行依賴管理),所以這兩個工具逐漸成為過去式,Maven成為了Java世界中項目構建的標準。(Gradle也是一款不錯的項目構建工具)

2. Maven安裝配置

2.1 windows環境安裝

    step1:安裝JDK,配置JAVA_HOME,添加到path;      step2:下載Maven安裝包,解壓到指定目錄;      step3:將Maven的安裝目錄添加到path;      step3:使用mvn -v校驗是否安裝成功。

配置M2_HOME 和 MAVEN_HOME兩個環境變數(指代Maven的安裝目錄,到bin那層目錄)
通常需要設置MAVEN_OPTS的值為-Xms128m -Xmx512m,因為Java默認的最大可用記憶體往往不能夠滿足Maven運行的需要,比如在項目較大時,使用Maven生成項目站點需要佔用大量的記憶體,如果沒有該配置,則很容易得到java.lang.OutOfMemeoryError異常。因此,一開始就配置該環境變數是推薦的做法。

2.2 Linux環境安裝

安裝方式和上述類似

2.3 .m2目錄

安裝完Maven後,一般會在用戶的主目錄下有一個.m2目錄。這個下面會有一個倉庫目錄,默認情況下,快取下來的Jar包會存放在這個目錄下。我們可以在這個目錄下添加settings.xml文件配置倉庫目錄。注意這個settings文件只對當前用戶生效。M2_HOME/config下的settings文件對所有用戶生效。推薦使用用戶範圍的settings。

2.4 配置Http代理

有時候公司為了安全起見,內部網路不能直接訪問外部互聯網。但是提供了代理伺服器進行外部網路訪問,這是想要讓Maven訪問到外部的Maven中心倉庫需要配置代理。

    <settings>          ...          <proxies>              <proxy>                 <id>optional</id>                 <active>true</active>                 <protocol>http</protocol>                 <host>10.47.22.247</host>                 <port>80</port>                 <username>xx</username>                 <password>xx</pawwword>                 <nonProxyHosts>some.host.com|*.google.com</nonProxyHosts>              </proxy>          </proxies>          ...      </settings>

proxies下面可以配置多個代理,默認情況下第一個被激活的代理生效。如果代理伺服器需要認證,那我們還需要提供用戶名密碼。標籤用來指定訪問哪個地址時不需要經過這個代理。

2.5 Maven安裝最佳實踐

2.5.1 配置MAVEN_OPTS

通常需要設置MAVEN_OPTS的值為-Xms128m -Xmx512m,因為Java默認的最大可用記憶體往往不能夠滿足Maven運行的需要,比如在項目較大時,使用Maven生成項目站點需要佔用大量的記憶體,如果沒有該配置,則很容易得到java.lang.OutOfMemeoryError異常。因此,一開始就配置該環境變數是推薦的做法。

2.5.2 配置用戶範圍的settings.xml

我們可以配置M2_HOME/conf/settings.xml,也可以配置~/.m2/settings.xml。前者會對所有用戶生效,後者只會對當前用戶生效。推薦配置用戶範圍內的setting,這樣不會影響其他用戶的配置,在升級Maven時也不需要重新配置。當然,如果你需要做全局配置,就需要配置做全局配置。

3. Maven坐標和依賴

3.1 Maven坐標

在Maven中,坐標可以理解為一個依賴的唯一標識。Maven的坐標元素包括groupId、artifactId、version、packaging、classfier。只要我們提供正確的坐標元素,Maven就能找到對應的構件,首先去你的本地倉庫查找,沒有的話再去遠程倉庫下載。如果沒有配置遠程倉庫,會默認從中央倉庫地址(http://repo1.maven.org/maven2)下載構件,該中央倉庫包含了世界上大部分流行的開源項目構件,但不一定所有構件都有。下面介紹下坐標中每個標籤的含義:

  • groupId:定義當前Maven項目的隸屬的實際項目。比如xx公司xx團隊的xx項目,xx項目下又包含幾個模組項目。這時groupId我們就可以命名為com.xxCompany.xxTeam.xxProject,子項目的名字可以通過artifactId來指定;
  • artifactId:定義實際項目中的一個Maven模組(子項目)的名字。一般推薦用項目的名稱作為前綴。比如我們整個大項目的名稱是xxProject,這個子模組的名稱是core。那artifactId可以設置為xxProject-core。
  • version:項目的版本
  • packaging:項目打包方式(jar、war、pom等)默認的是jar。
  • classifier:用來定義javadoc和源程式碼source等構件,這個元素不能直接定義。

郵件測試時我們可以考慮使用GreenMail這個類庫

3.2 dependeny標籤的內容

dependeny標籤下面可以有下面的標籤:

  • groupId、artifactId和version這幾個標籤來定位具體的依賴;
  • type這個標籤對用maven坐標中的packaging屬性,一般不需要指定,默認是jar;
  • scope:見3.3節詳解;
  • optional:值是true或false,可選依賴不會被傳遞;
  • exclusions:排除傳遞依賴。

3.3 scope屬性詳解

Maven環境下有三種classpath,分別是編譯classpath、測試classpa和運行classpath。scope屬性和這三種classpath有關。scope的值可以是下面幾種:

  • compile:如果將一個依賴的scope屬相設置為compile(默認就是compile),那麼這個依賴對於三種classpath都有效;
  • test:編譯測試classpath有效,這種Jar包不會被打包進最終的項目;
  • provide:對於編譯和測試有效,運行時無效。說明在項目運行時這個依賴會被提供,就不需要我們打進項目了。典型的就是Servlet-api(這個依賴在運行時會由web容器提供)
  • runtime:典型的應用就是JDBC的驅動實現(就是這個依賴的Jar只在項目運行是才會用到)
  • system:依賴的Jar包在本地(不建議使用這個屬性)
<dependency>      <groupId>org.xx.xx</groupId>      <artifactId>xx-web</artifactId>      <scope>system</scope>      <systemPath>{java.home}/lib/xx.jar</systemPath>  </dependency>
  • import:該屬性只會在dependenceManagement屬性中生效。用來導入類型為pom的依賴,典型的應用看Springboot。
Scope 編譯classpath 測試classpath 運行時classpath 列子
compile Y Y Y Spring-core
test – Y – junit
provide Y Y – servlet-api
runtime – Y Y jdbc驅動實現
system Y Y –

3.4 傳遞依賴

Maven的依賴是具有傳遞性的,比如A->B,B->C,那麼A間接的依賴於C,這就是依賴的傳遞性,其中A對於B是第一直接依賴,B對於C是第二直接依賴,C為A的傳遞性依賴。這裡還有一個依賴scope的問題。如果A->B的scope是compile,B->C的scope也是compile,那麼A->C的scope也是compile。

表格:傳遞依賴
| |compile|test|provided|runtime|
|—–|:—–:|—-|——:|——-|
|compile|compile| – | – | runtime |
|test|test| – | – | test |
|provided|provided| – | provided | provided |
|runtime|runtime| – | – | runtime |

上表中最左邊一列表示A->B的第一直接依賴,最上面一行表示B->C的第二直接依賴,表格中的內容表示A->C的依賴內容。舉個列子,當A->B的直接依賴範圍是test,B->C的第二直接依賴是compile,那麼A->C的依賴範圍是test。

3.5 依賴調解

下面我們來思考這樣一個問題,如果A->B->C->X(1.0),A->D-X(2.0),即A間接依賴X,我們可以看到有兩條路徑都依賴X,那麼maven將會選擇哪個版本的X?maven當中有一套自己的規則,我們來說明一下,maven傳遞性依賴的一些規則以及如何排除依賴衝突。

Maven裡面對於傳遞性依賴有以下幾個規則:

  1. 最短路徑原則:如果A對於依賴路徑中有兩個相同的jar包,那麼選擇路徑短的那個包,路徑最近者優先,上述會選X(2.0)。

  2. 第一聲明優先原則:如果A對於依賴路徑中有兩個相同的jar包,路徑長度也相同,那麼依賴寫在前面的優先。例如:A->B->F(1.0),A->C->F(2.0),會選F(1.0)。

  3. 可選依賴不會被傳遞,如A->B,B->C,B->D,A對B直接依賴,B對C和D是可選依賴,那麼在A中不會引入C和D。可選依賴通過optional元素配置,true表示可選。如果要在A項目中使用C或者D則需要顯式地聲明C或者D依賴。

3.6 依賴排除

<dependency>       <groupId>org.springframework</groupId>       <artifactId>spring-core</artifactId>       <version>3.2.8</version>       <exclusions>             <exclusion>                  <groupId>commons-logging</groupId>                  <artifactId>commons-logging</artifactId>             </exclusion>        </exclusions>  </dependency

4. Maven倉庫

Maven的倉庫分為本地倉庫和遠程倉庫。Maven在尋找依賴的時候會先從本地倉庫尋找,本地沒有的話會從遠程倉庫去下載到本地然後再使用。

4.1 倉庫的分類

4.1.1 本地倉庫

本地倉庫需要我們在settings.xml配置文件中配置。注意,默認情況下.m2目錄下是沒有這文件的,需要我們自己配置。所有的依賴都會被快取到本地倉庫,本地沒有的話回去遠程倉庫拿。

<localRepository>D:softwaremavenRepository</localRepository>

4.1.2 遠程倉庫

遠程倉庫有好幾種分類:Maven的中央倉庫、Nexus搭建的私有伺服器(如果Nexus上沒有相應的依賴,Nexus會自動從外部的倉庫下載)和JBoss、Google的中央倉庫等。一般我們只配置一個遠程倉庫。如果中央倉庫中沒有相應的依賴,就會報錯。下面是Maven配置的默認的遠程倉庫,這個倉庫中包含了絕大多數的依賴構建,如果我們沒做其他配置,Maven就會從這個倉庫中下載依賴。

    <repositories>      <repository>        <id>central</id>        <name>Central Repository</name>        <url>https://repo.maven.apache.org/maven2</url>        <layout>default</layout>        <snapshots>          <enabled>false</enabled>        </snapshots>      </repository>      </repositories>

Maven還允許用戶配置私有伺服器。如果我們將遠程倉庫配置成我們自己搭建的私有伺服器,那麼Maven每次都會從私有伺服器上去請求依賴,如果私服上不存在我們需要的依賴,私服會先從外部倉庫下載依賴然後快取到伺服器上再提供給我們。另外私服也允許我們自己上傳依賴。架設Maven私服有如下優點:

  • 節省公司外網貸款;
  • 因為依賴會被快取到私服上,所以依賴的下載速度會很快;
  • 方便上傳團隊內部的依賴,統一管理,共享。

4.2 遠程倉庫的配置

Maven已經默認為我們配置了一個遠程倉庫,在maven安裝目錄下的:/lib/maven-model-builder-${version}.jar中我們可以找到這個配置。

    <repositories>       <repository>          <id>central</id>          <name>Central Repository</name>          <url>https://repo.maven.apache.org/maven2</url>          <layout>default</layout>          <snapshots>            <enabled>false</enabled>          </snapshots>       </repository>      </repositories>  

這裡我們只要知道,中央倉庫的id為central,遠程url地址為http://repo.maven.apache.org/maven2,它關閉了snapshot版本構件下載的支援。

當然我們也可以自己在pom中配置repository,有可能你依賴的一個jar在central中找不到,它只存在於某個特定的公共倉庫,這樣你也不得不添加那個遠程倉庫的配置。

    <project>      ...        <repositories>          <repository>            <id>maven-net-cn</id>            <name>Maven China Mirror</name>            <url>http://maven.net.cn/content/groups/public/</url>            <releases>              <enabled>true</enabled>            </releases>            <snapshots>              <enabled>false</enabled>            </snapshots>          </repository>        </repositories>        <pluginRepositories>          <pluginRepository>            <id>maven-net-cn</id>            <name>Maven China Mirror</name>            <url>http://maven.net.cn/content/groups/public/</url>            <releases>              <enabled>true</enabled>            </releases>            <snapshots>              <enabled>false</enabled>            </snapshots>          </pluginRepository>        </pluginRepositories>      ...      </project>  

我們先看一下的配置,你可以在它下面添加多個 ,每個都有它唯一的ID,一個描述性的name,以及最重要的,遠程倉庫的url。此外,true告訴Maven可以從這個倉庫下載releases版本的構件,而false告訴Maven不要從這個倉庫下載snapshot版本的構件。禁止從公共倉庫下載snapshot構件是推薦的做法,因為這些構件不穩定,且不受你控制,你應該避免使用。當然,如果你想使用區域網內組織內部的倉庫,你可以激活snapshot的支援。

這邊整理下Maven查詢倉庫的順序:

  1. 先查詢本地倉庫,如果本地倉庫中沒有,進入第二步;
  2. 查詢Maven給central倉庫配置鏡像,如果配置了鏡像就去鏡像查,如果沒有,就去中央倉庫查;
  3. 查詢Maven有沒給你自己配置的倉庫指定鏡像,如果配置了鏡像就去鏡像查,否則就從你自己配置的倉庫查。

4.2.1 遠程倉庫的認證

大部分公共的遠程倉庫無須認證就可以直接訪問,但我們在平時的開發中往往會架設自己的Maven遠程倉庫,出於安全方面的考慮,我們需要提供認證資訊才能訪問這樣的遠程倉庫。配置認證資訊和配置遠程倉庫不同,遠程倉庫可以直接在pom.xml中配置,但是認證資訊必須配置在settings.xml文件中。這是因為pom往往是被提交到程式碼倉庫中供所有成員訪問的,而settings.xml一般只存在於本機。因此,在settings.xml中配置認證資訊更為安全。

    <servers>          <server>             <id>releases</id>             <username>pub_mvn_deploy</username>             <password>mvn.Deploy</password>           </server>      </servers>

上面程式碼我們配置了一個id為releases的遠程倉庫認證資訊。Maven使用settings.xml文件中的servers元素及其子元素server配置倉庫認證資訊。認證用戶名為admin,認證密碼為admin123。這裡的關鍵是id元素,settings.xml中server元素的id必須與pom.xml中需要認證的repository元素的id完全一致。正是這個id將認證資訊與倉庫配置聯繫在了一起。

4.2.2 部署構建到遠程倉庫

     <distributionManagement>               <repository>                   <id>releases</id>                   <name>public</name>                   <url>http://59.50.95.66:8081/nexus/content/repositories/releases</url>               </repository>               <snapshotRepository>                   <id>snapshots</id>                   <name>Snapshots</name>                   <url>http://59.50.95.66:8081/nexus/content/repositories/snapshots</url>               </snapshotRepository>       </distributionManagement>

distributionManagement包含repository和snapshotRepository子元素,前者表示發布版本(穩定版本)構件的倉庫,後者表示快照版本(開發測試版本)的倉庫。這兩個元素都需要配置id、name和url,id為遠程倉庫的唯一標識,name是為了方便人閱讀,關鍵的url表示該倉庫的地址。往遠程倉庫部署構件的時候,往往需要認證,配置認證的方式同上。

配置正確後,運行命令mvn clean deploy,Maven就會將項目構建輸出的構件部署到配置對應的遠程倉庫,如果項目當前的版本是快照版本,則部署到快照版本的倉庫地址,否則就部署到發布版本的倉庫地址。

4.3 快照版本

在Maven世界中版本分為發布版本和快照版本。1.0.0、1.3-alpha-4和2.0這種版本是穩定的發布版本。2.1-SNAPSHOT或者2.1–20091214-13這種版本是快照版本。
如果我們依賴一個版本為快照的依賴,這種情況下我們我們每次build的時候都會從遠程倉庫拉取這個依賴的最新版本。(遠程倉庫會給這個依賴維護一個時間戳,所以Maven能知道最新的版本)

release(最新發布版本)、latest(最新版本)、snapshot這三種版本都是上面的策略。在我們平時使用時不建議使用release和latest版本,因為這些版本更新比較頻繁,你的項目依賴了這些版本的Jar包,今天還能編譯通過,明天可能就編譯不過了。推薦使用穩定發布版本。

    <repository>          <releases>              <enabled></enabled>          </releases>          <snapshots>              <enabled></enabled>          </snapshots>      </repository>

4.4 從倉庫解析依賴的機制

。。。待完成

4.5 鏡像

如果倉庫X可以提供倉庫Y存儲的所有內容,那麼就可以認為X是Y的一個鏡像。關於鏡像的一個更為常見的用法是結合私服。由於私服可以代理任何外部的公共倉庫(包括中央倉庫),因此,對於組織內部的Maven用戶來說,使用一個私服地址就等於使用了所有需要的外部倉庫,這可以將配置集中到私服,從而簡化Maven本身的配置。在這種情況下,任何需要的構件都可以從私服獲得,私服就是所有倉庫的鏡像。這時,可以配置這樣的一個鏡像:

    <mirrors>          <mirror>              <!--This sends everything else to /public -->              <id>nexus</id>              <mirrorOf>*</mirrorOf>              <url>http://maven.dev.xxxx.com/nexus/content/groups/public</url>          </mirror>      </mirrors>

mirrorOf標籤可以有多種配置方式:

<!-- 匹配所有倉庫 -->  <mirrorOf>*</mirrorOf>  <!-- 匹配倉庫rep1和rep2 -->  <mirrorOf>rep1,rep2</mirrorOf>  <!-- 匹配rep1之外的所有 -->  <mirrorOf>*,!rep1</mirrorOf>  <!-- 匹配不在本機上的遠程倉庫 -->  <mirrorOf>external:*</mirrorOf>

5. Maven生命周期和插件

在Maven中,我們需要掌握的重要概念有坐標、依賴、倉庫、生命周期、插件。這個章節我們講解生命周期和插件的主題。

Maven通過對大量項目構建過程的總結,抽象出了一套高度完善的項目構建的生命周期。整個生命周期包含項目的清理、初始化、編譯、測試、打包、集成測試、驗證、部署、站點生成等幾乎所有的過程。Maven的整個生命周期是抽象的,這意味著整個生命周期本身不會幹任何事情。在Maven項目的整個構建過程中,實際的構建任務都是由Maven插件完成的。Maven本身內置了很多插件,所以我們在項目進行編譯、測試、打包的過程是沒有感覺到。比如編譯就是通過maven-compile-plugin實現的、測試是通過maven-surefire-plugin實現的。

5.1 生命周期詳解

Maven擁有3套獨立的生命周期:clean周期、default周期和site生命周期。clean周期的主要作用是清理項目,default周期的作用是構建項目,site周期的作用是生成項目站點。每個生命周期都若干個階段,比如使用mvn clean命令,就是調用了clean生命周期的clean階段。

5.1.1 clean生命周期

Clean生命周期一共包含了三個階段:

  • pre-clean:執行一些需要在clean之前完成的工作。
  • clean:移除所有上一次構建生成的文件。
  • post-clean:執行一些需要在clean之後立刻完成的工作。

mvn clean中的clean就是上面的clean,在一個生命周期中,運行某個階段的時候,它之前的所有階段都會被運行,也就是說,mvn clean等同於 mvn pre-clean clean,如果我們運行 mvn post-clean,那麼 pre-clean、clean 都會被運行。這是Maven很重要的一個規則,可以大大簡化命令行的輸入。但是執行clean生命周期的某個階段,不會觸發其他生命周期的階段,因為Maven的每個生命周期都是相互獨立的。

5.1.2 site生命周期

下面看一下Site生命周期的各個階段:

  • pre-site:執行一些需要在生成站點文檔之前完成的工作。
  • site:生成項目的站點文檔。
  • post-site:執行一些需要在生成站點文檔之後完成的工作,並且為部署做準備。
  • site-deploy:將生成的站點文檔部署到特定的伺服器上。

這裡經常用到的是site階段和site-deploy階段,用以生成和發布Maven站點,這可是Maven相當強大的功能,Manager比較喜歡,文檔及統計數據自動生成,很好看。

5.1.3 default生命周期

最後,來看一下Maven的最重要的default生命周期,絕大部分工作都發生在這個生命周期中,這裡我只解釋一些比較重要和常用的階段:

  • validate
  • initialize
  • generate-sources
  • process-sources:處理項目主資源文件,一般來說,是對src/main/resources目錄的內容進行變數替換等工作後,複製到項目輸出的主classpath目錄中;
  • generate-resources
  • process-resources
  • compile 編譯項目的源程式碼,一般來說,是編譯src/main/java目錄下的Java文件至項目輸出的主classpath目錄中。
  • process-classes
  • generate-test-sources
  • process-test-sources:處理項目測試資源文件,一般來說,是對src/test/resources目錄的內容進行變數替換等工作後,複製到項目輸出的測試classpath目錄中。
  • generate-test-resources
  • process-test-resources
  • test-compile:編譯項目的測試源程式碼,一般來說,是編譯src/test/java目錄下的Java文件至項目輸出的測試classpath目錄中。
  • process-test-classes
  • test:使用合適的單元測試框架運行測試。這些測試程式碼不會被打包或部署。
  • prepare-package
  • package:接受編譯好的程式碼,打包成可發布的格式,如 JAR 。
  • pre-integration-test
  • integration-test
  • post-integration-test
  • verify
  • install:將包安裝至本地倉庫,以讓其它項目依賴。
  • deploy:將最終的包複製到遠程的倉庫,以讓其它開發人員與Maven項目使用。

基本上,根據名稱我們就能猜出每個階段的用途,關於階段的詳細解釋以及其她階段的解釋,請參考 http://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html 。

5.1.4 命令行和生命周期

mvn clean install site-deploy

5.2 插件詳解

這邊先理解一個重要的概念:插件目標(goal)。每個插件的單個功能就可以理解為是一個插件的目標。舉個例子,maven-dependency-plugin這個插件可以分析項目的依賴樹、可以分析潛在無用的依賴,這一個個功能我們就可以理解為插件的goal。

5.2.1 默認插件綁定

Maven生命周期的某個階段會和插件的某個goal綁定來完成某個特定的任務。Maven已經在生命周期的某個階段默認綁定了一些goal,讓用戶可以直接使用。

表格:clean生命周期綁定的插件
| 生命周期階段 | 插件目標 |
|————|———-|
| pre-clean | – |
| clean | maven-clean-plugin:clean|
| post-clean | – |

表格:site生命周期綁定的插件目標
| 生命周期階段 | 插件目標 |
|————|———-|
| pre-site | – |
| site | maven-site-plugin:site|
| post-site | – |
| site-deploy | maven-site-plugin:deploy |

表格:default生命周期綁定的插件目標
| 生命周期階段 | 插件目標 | 執行的任務 |
|————|———-|———–|
| process-resources | maven-resources-plugin:resources |複製主資源文件到主輸出目錄 |
| compile | maven-compiler-plugin:compile| 編譯主程式碼到主輸出目錄 |
| process-test-resources | maven-resources-plugin:testResources | -|
| test-compile | maven-compiler-plugin:testCompile | – |
| test | maven-surefire-plugin:test | 執行測試用例 |
| package | maven-jar-plugin:jar | 項目打包(如果打成war包的話會綁定其他插件) |
| install | maven-install-plugin:install | 安裝到本地倉庫 |
| deploy | maven-deploy-plugin | 項目部署 |

上表只是列出了擁有插件綁定關係的階段,default生命周期還有很多其他階段,默認他們沒有綁定任何插件,因此這些階段沒有任何實際行為。

除了默認的打包類型jar之外,常見的打包類型還有war、pom、maven-plugin和ear等。

5.2.2 自定義插件綁定

除了內置的綁定之外,用戶還能自己選擇將某個插件的目標綁定到生命周期的某個階段上。這種自定義的綁定方式能讓Maven執行更多功能。下面是一個插件配置的列子:

    <build>          <plugins>              <plugin>                  <groupId>org.apache.maven.plugins</groupId>                  <artifactId>maven-source-plugin</artifactId>                  <version>2.1.2</version>                   <executions>                    <execution>                     <goals>                          <goal>jar-no-fork</goal>                     </goals>                     <phase>package</phase>                    </execution>                   </executions>              </plugin>          </plugins>      </build>

在上面的列子中我們將>maven-source-plugin插件的jar-no-fork目標綁定到了default周期的package階段。所以在執行到package階段的時候jar-no-fork會被調用。有時候我們會發現我們沒配置任何phase,插件的目標也能被調用。這是因為插件已經為目標默認綁定到生命周期的某個階段上了。使用下面的命令可以查看某個目標綁定到了哪個階段上

    mvn help:describe -Dplugin=org.apache.maven.plugins:maven-dependency-plugin -Ddetail      mvn help:describe -Dplugin=dependency -Ddetail

插件倉庫和依賴的倉庫不是公用的,需要我們另外配置。

    <pluginRepositories>          <pluginRepository>            <id>central</id>            <url>http://central</url>            <releases><enabled>true</enabled></releases>            <snapshots><enabled>true</enabled></snapshots>          </pluginRepository>      </pluginRepositories>

6. Maven聚合和繼承

6.1 聚合

聚合最主要的表現形式是使用modle標籤來管理多個子模組的構建:

    <modules>          <module>spring-boot-quickstart</module>          <module>spring-boot-web</module>      </modules>

6.2 繼承

如果多個模組出現相同的依賴包,這樣在pom.xml文件的內容出現了冗餘、重複的內容,解決這個問題其實使用Maven的繼承機制即可,就像Java的繼承一樣,父類就像一個模板,子類繼承自父類,那麼有些通用的方法、變數都不必在子類中再重複聲明了。Maven的繼承機制類似,在一個父級別的Maven的pom文件中定義了相關的常量、依賴、插件等等配置後,實際項目模組可以繼承此父項目 的pom文件,重複的項不必顯示的再聲明一遍了,相當於父Maven項目就是個模板,等著其他子模組去繼承。不過父Maven項目要高度抽象,高度提取公共的部分(交集),做到一處聲明,多處使用。

可繼承的POM元素:
groupId和version是可以被繼承的,那麼還有哪些POM元素可以被繼承呢?以下是一個完整的列表,並附帶了簡單的說明:

  • groupId:項目組 ID,項目坐標的核心元素;
  • version:項目版本,項目坐標的核心元素;
  • description:項目的描述資訊;
  • organization:項目的組織資訊;
  • inceptionYear:項目的創始年份;
  • url:項目的 url 地址;
  • develoers:項目的開發者資訊;
  • contributors:項目的貢獻者資訊;
  • distributionManagerment:項目的部署資訊;
  • issueManagement:缺陷跟蹤系統資訊;
  • ciManagement:項目的持續繼承資訊;
  • scm:項目的版本控制資訊;
  • mailingListserv:項目的郵件列表資訊;
  • properties:自定義的 Maven 屬性;
  • dependencies:項目的依賴配置;
  • dependencyManagement:醒目的依賴管理配置;
  • repositories:項目的倉庫配置;
  • build:包括項目的源碼目錄配置、輸出目錄配置、插件配置、插件管理配置等;
  • reporting:包括項目的報告輸出目錄配置、報告插件配置等。

聚合 VS 繼承

雖然聚合通常伴隨著父POM的繼承關係,但是這兩者不是必須同時存在的,從上面兩者的介紹可以看出來,這兩者的都有不同的作用,他們的作用不依賴於另一個的配置。

父POM是為了抽取統一的配置資訊和依賴版本控制,方便子POM直接引用,簡化子POM的配置。聚合(多模組)則是為了方便一組項目進行統一的操作而作為一個大的整體,所以要真正根據這兩者不同的作用來使用,不必為了聚合而繼承同一個父POM,也不比為了繼承父POM而設計成多模組。

6.3 插件管理

和依賴管理(dependencyManagement)的理念相同,我們可以在父pom中配置插件管理。這樣子模組就可以繼承父模組的插件。

    <build>          <pluginManagement>              <plugins>                </plugins>          </pluginManagement>      </build>

7. Nexus創建私服

私服是一種特殊的Maven遠程倉庫。Nexus分為免費版和專業版。安裝包分為附帶web容器的Bundle版本,和不帶web容器的war包版本。

7.1 Nexus倉庫類型

倉庫有四種類型:

  • group(倉庫組) :沒有實際的內容,就是將若干個其他倉庫組合成一個組,在這種類型的倉庫中下載依賴的時候,Maven會從這個組中包含的倉庫依次輪詢,知道下載到依賴為止;
  • hosted(宿主) :建在Nexus一台機器上的本地Maven倉庫;
  • proxy(代理) :是一個代理,代理了其他遠程倉庫,比如Maven的central倉庫;
  • virtual(虛擬):兼容Maven1 版本的jar或者插件。

每個倉庫的格式為maven2或者maven1。此外,倉庫還有一個屬性為Policy(策略),表示該倉庫為發布(Release)版本倉庫還是快照(Snapshot)版本倉庫。最後兩列的值為倉庫的狀態和路徑。

  • Maven Central:該倉庫代理Maven中央倉庫,其策略為Release,因此只會下載和快取中央倉庫中的發布版本構件。
  • Releases:這是一個策略為Release的宿主類型倉庫,用來部署組織內部的發布版本構件。
  • Snapshots:這是一個策略為Snapshot的宿主類型倉庫,用來部署組織內部的快照版本構件。
  • 3rd party:這是一個策略為Release的宿主類型倉庫,用來部署無法從公共倉庫獲得的第三方發布版本構件。
  • Apache Snapshots:這是一個策略為Snapshot的代理倉庫,用來代理Apache Maven倉庫的快照版本構件。
  • Codehaus Snapshots:這是一個策略為Snapshot的代理倉庫,用來代理Codehaus Maven倉庫的快照版本構件。
  • Google Code:這是一個策略為Release的代理倉庫,用來代理Google Code Maven倉庫的發布版本構件。
  • java.net-Maven 2:這是一個策略為Release的代理倉庫,用來代理java.net Maven倉庫的發布版本構件。
  • Public Repositories:該倉庫組將上述所有策略為Release的倉庫聚合併通過一致的地址提供服務。
  • Public Snapshot Repositories:該倉庫組將上述所有策略為Snapshot的倉庫聚合併通過一致的地址提供服務。

7.2 配置Nexus倉庫

  1. 在POM中配置倉庫
        <project>            <repositories>              <repository>                <id>maven-net-cn</id>                <name>Maven China Mirror</name>                <url>http://maven.net.cn/content/groups/public/</url>                <releases>                  <enabled>true</enabled>                </releases>                <snapshots>                  <enabled>false</enabled>                </snapshots>              </repository>            </repositories>            <pluginRepositories>              <pluginRepository>                <id>maven-net-cn</id>                <name>Maven China Mirror</name>                <url>http://maven.net.cn/content/groups/public/</url>                <releases>                  <enabled>true</enabled>                </releases>                <snapshots>                  <enabled>false</enabled>                </snapshots>              </pluginRepository>            </pluginRepositories>          </project>  

這種配置方式只會對當前項目生效,如果想對所有項目生效,可以在settings文件中配置。

  1. 在Settings.xml中配置
        <settings>            ...            <profiles>              <profile>                <id>Nexus</id>                <!-- repositories and pluginRepositories here-->              </profile>            </profiles>            <activeProfiles>              <activeProfile>Nexus</activeProfile>            </activeProfiles>            ...          </settings>  
  1. 使用鏡像
    如果你的地理位置附近有一個速度更快的central鏡像,或者你想覆蓋central倉庫配置,或者你想為所有POM使用唯一的一個遠程倉庫(這個遠程倉庫代理的所有必要的其它倉庫)
        <settings>          ...            <mirrors>              <mirror>                <id>maven-net-cn</id>                <name>Maven China Mirror</name>                <url>http://maven.net.cn/content/groups/public/</url>                <mirrorOf>*</mirrorOf>              </mirror>            </mirrors>          ...          </settings>  

7.3 部署構建到Nexus

…

8. Maven測試

Maven的測試插件會自動執行src/test/java/下面的

  • **/Test*.java;
  • **/*Test.java;
  • **/*TestCase.java

以上形式的類。

8.1 跳過測試

mvn clean install -DskipTests    <properties>      <skipTests>true</skipTests>  </properties>

8.2 指定運行某個測試用例

mvn test -Dtest=Random1Test,Random2Test  mvn test -Dtest=Random*Test

8.3 排除測試

    <build>      <plugins>        <plugin>          <groupId>org.apache.maven.plugins</groupId>          <artifactId>maven-surefire-plugin</artifactId>          <version>3.0.0-M3</version>          <configuration>            <includes>             <include>**/*Tests.java</include>            </includes>            <excludes>              <exclude>**/TestCircle.java</exclude>              <exclude>**/TestSquare.java</exclude>            </excludes>          </configuration>        </plugin>      </plugins>      </build>

9. 持續集成

Hudson+Maven+git版本控制–>持續集成

10. 構建Web項目

使用Cargo可以進行遠程Web應用部署…

11. Maven版本管理

Maven的版本管理一般會遵循:

<主版本>.<次版本>.<增量版本>-<里程碑版本>
  • 主版本號:表示項目架構有重大的變更;
  • 次版本號:較大範圍的功能增加和變化,以及Bug修復,但總體項目架構沒什麼變化;
  • 增量版本:一般表示重大Bug修復,例如1.4.0發布後發現一個重大Bug,發現一個重大Bug修復後發布1.4.1;
  • 里程碑版本:SNAPSHOT–>alpha–>beta–>release–>GA

    SNAPSHOT:正在開發中的版本
    Alpha: 內部測試的版本(項目組內部測試)
    Beta:用戶下載下來測試使用
    Release: 用戶使用下來沒什麼問題就可以發布Release版本
    GA:穩定版本

11.1 GPG簽名

使用GPG簽名來校驗依賴包有沒被第三方惡意篡改

12. 靈活的構建

12.1 Maven屬性

Maven中有6中基本屬性:

  1. 內置屬性
    主要有兩個常用內置屬性——${basedir}表示項目根目錄,即包含pom.xml文件的目錄;${version}表示項目版本。

  2. POM屬性
    pom中對應元素的值,例如${project.artifactId}對應了元素的值,常用的POM屬性包括:

  • ${project.build.sourceDirectory}:項目的主源碼目錄,默認為src/main/java/;
  • ${project.build.testSourceDirectory}:項目的測試源碼目錄,默認為/src/test/java/;
  • ${project.build.directory}:項目構建輸出目錄,默認為target/;
  • ${project.build.outputDirectory}:項目主程式碼編譯輸出目錄,默認為target/classes/;
  • ${project.build.testOutputDirectory}:項目測試程式碼編譯輸出目錄,默認為target/testclasses/;
  • ${project.groupId}:項目的groupId;
  • ${project.artifactId}:項目的artifactId;
  • ${project.version}:項目的version,於${version}等價;
  • ${project.build.finalName}:項目打包輸出文件的名稱,默認為${project.artifactId}${project.version}.
  1. 自定義屬性
    在pom中元素下自定義的Maven屬性。

  2. Settings屬性
    與POM屬性同理。如${settings.localRepository}指向用戶本地倉庫的地址。

  3. Java系統屬性
    所有Java系統屬性都可以使用Maven屬性引用,例如${user.home}指向了用戶目錄。可以通過命令行mvn help:system查看所有的Java系統屬性

  4. 環境變數屬性
    所有環境變數都可以使用以env.開頭的Maven屬性引用。例如${env.JAVA_HOME}指代了JAVA_HOME環境變數的值。也可以通過命令行mvn help:system查看所有環境變數。

12.2 資源過濾

    <build>          <resources>              <resource>                  <directory>src/main/resources</directory>                  <filtering>true</filtering>                  <excludes>                      <exclude>**/config/*.*</exclude>                  </excludes>              </resource>              <resource>                  <directory>src/main/resources/config</directory>                  <filtering>true</filtering>                  <includes>                      <include>application-${profiles.active}.yml</include>                  </includes>              </resource>          </resources>      </build>

12.3 Maven profile

12.3.1 針對不同環境profile

    <project>          ...          <profiles>              <profile>                  <id>dev</id>                  <properties>                      <profiles.active>dev</profiles.active>                  </properties>                  <activation>                      <!-- **這邊可以配置各種激活條件** -- >                      <property>                          <name></name>                          <value></value>                      </property>                      <file>                          <exists></exists>                      </file>                      <os>                          ...                      </os>                      <jdk>                          ...                      </jdk>                      <activeByDefault>true</activeByDefault>                  </activation>              </profile>              <profile>                  <id>stg</id>                  <properties>                      <profiles.active>stg</profiles.active>                  </properties>              </profile>              <profile>                  <id>prod</id>                  <properties>                      <profiles.active>prod</profiles.active>                  </properties>              </profile>          </profiles>          ...      </project>

12.3.2 激活profile

  1. 命令行激活

    mvn clean install -Pdev

  2. settings文件顯示激活
    如果你希望某個profile一直處於激活狀態,可以直接在settings文件中配置:
      <profiles>          <profile>            <id>nexus</id>            <repositories>                <repository>                <id>central</id>                <url>http://central</url>                <releases><enabled>true</enabled></releases>                <snapshots><enabled>true</enabled></snapshots>               </repository>            </repositories>            <pluginRepositories>              <pluginRepository>                <id>central</id>                <url>http://central</url>                <releases><enabled>true</enabled></releases>                <snapshots><enabled>true</enabled></snapshots>              </pluginRepository>            </pluginRepositories>          </profile>        </profiles>        <activeProfiles>          <!--make the profile active all the time -->          <activeProfile>nexus</activeProfile>        </activeProfiles

12.3.3 Web項目打包

…

13. 項目站點生成

…後續完成…

14. 編寫Mavem插件

…後續完成…

附錄

A. 常用命令

下面我們總結下Maven中常用的命令。

  • mvn help:system 調用help這個插件,列印出jvm的系統屬性和作業系統的環境變數
  • mvn help:describe -Dplugin=org.apache.maven.plugins:maven-dependency-plugin -Ddetail
  • mvn archetype:generate 這個命令可以幫助我們生成一個Maven項目的骨架
     |--Pom.xml         |--src             |--main                   |--java                   |--resource             |--test                   |--java                   |--resource
  • mvn dependency:list
  • mvn denpedency:tree
  • mvn dependency:analyze 可以分析當前項目依賴的問題
  • mvn clean install -DskipTests
  • mvn clean deploy 編譯打包並發布到遠程倉庫
  • mvn clean install site

B. POM文件總結

        元素名稱                             簡   介        <project>                         POM的xml根元素        <parent>                          聲明繼承        <modules>                         聲明聚合        <groupId>                         坐標元素之一        <artifactId>                      坐標元素之一        <version>                         坐標元素之一        <packaging>                       坐標元素之一,默認值jar        <name>                            名稱        <description>                     描述        <organization>                    所屬組織        <licenses><license>               許可證        <mailingLists><mailingList>       郵件列表        <developers><developer>           開發者        <contributors><contributor>       貢獻者        <issueManagement>                 問題追蹤系統        <ciManagement>                    持續集成系統        <scm>                             版本控制系統        <prerequisites><maven>            要求Maven最低版本,默認值為2.0        <build><sourceDirectory>          主源碼目錄        <build><scriptSourceDirectory>    腳本源碼目錄        <build><testSourceDirectory>      測試源碼目錄        <build><outputDirectory>          主源碼輸出目錄        <build><testOutputDirectory>      測試源碼輸出目錄        <build><resources><resource>      主資源目錄        <build><testResources><testResource> 測試資源目錄        <build><finalName>                輸出主構件的名稱        <build><directory>                輸出目錄        <build><filters><filter>          通過properties文件定義資源過濾屬性        <build><extensions><extension>    擴展Maven的核心        <build><pluginManagement>         插件管理        <build><plugins><plugin>          插件        <profiles><profile>               POM Profile        <distributionManagement><repository>  發布版本部署倉庫        <distributionManagement> <snapshotRepository> 快照版本部署倉庫        <distributionManagement> <site>   站點部署        <repositories><repository>        倉庫        <pluginRepositories><pluginRepository>   插件倉庫        <dependencies><dependency>        依賴        <dependencyManagement>            依賴管理        <properties>                      Maven屬性        <reporting><plugins>              報告插件

C. settings文件例子

<settings>  <localRepository>D:softwaremavenRepository</localRepository>  <proxies>  <proxy>      <id>optional</id>      <active>true</active>      <protocol>http</protocol>      <host>10.47.22.247</host>      <port>80</port>      <nonProxyHosts>some.host.com</nonProxyHosts>  </proxy>  </proxies>  <servers>  <server>      <id>releases</id>      <username>pub_mvn_deploy</username>      <password>mvn.Deploy</password>  </server>  <server>      <id>snapshots</id>      <username>pub_mvn_deploy</username>      <password>mvn.Deploy</password>  </server>  <server>      <id>nexus</id>      <username>pub_mvn_deploy</username>      <password>mvn.Deploy</password>  </server>  </servers>    <mirrors>  <mirror>      <id>nexus</id>      <mirrorOf>*</mirrorOf>      <url>http://maven.dev.xxx.com/nexus/content/groups/public</url>  </mirror>  </mirrors>    <profiles>  <profile>      <id>nexus</id>      <repositories>          <repository>              <id>central</id>              <url>http://central</url>              <releases>                  <enabled>true</enabled>              </releases>              <snapshots>                  <enabled>true</enabled>              </snapshots>          </repository>      </repositories>      <pluginRepositories>          <pluginRepository>              <id>central</id>              <url>http://central</url>              <releases>                  <enabled>true</enabled>              </releases>              <snapshots>                  <enabled>true</enabled>              </snapshots>          </pluginRepository>      </pluginRepositories>  </profile>  <!-- if you want to be able to switch to the defaultprofile profile put this in the active profile -->  <profile>      <id>defaultprofile</id>      <repositories>          <repository>              <id>maven.default</id>              <name>default maven repository</name>              <url>http://repo1.maven.org/maven2</url>              <snapshots>                  <enabled>true</enabled>              </snapshots>          </repository>          <repository>              <id>maven.snapshot</id>              <name>Maven snapshot repository</name>              <url>http://people.apache.org/repo/m2-snapshot-repository</url>              <snapshots>                  <enabled>true</enabled>              </snapshots>          </repository>      </repositories>  </profile>  </profiles>  <activeProfiles>  <!--make the profile active all the time -->  <activeProfile>nexus</activeProfile>  </activeProfiles>  </settings>

D. settings文件元素參考表

1,<settings>,settings.xml文檔的根元素    2,<localRepository>,本地倉庫    3,<interactiveMode>,Maven是否與用戶交互,默認值true    4,<offline>,離線模式,默認值false    5,<pluginGroups>  <pluginGroup>,插件組    6,<servers>  <server>,下載與部署倉庫的認證資訊    7,<mirrors>  <mirror>,倉庫鏡像    8,<proxies>  <proxy>,代理    9,<profiles>  <profile>,Settings Profile    10,<activeProfiles>  <activeProfile>,激活Profile

E. 常用插件總結

插件名稱                    用途                  來源  maven-clean-plugin     清理項目           Apache  maven-compiler-plugin   編譯項目              Apache  maven-deploy-plugin     部署項目              Apache  maven-install-plugin        安裝項目              Apache  maven-resources-plugin  處理資源文件        Apache  maven-site-plugin       生成站點              Apache  maven-surefire-plugin   執行測試              Apache  maven-jar-plugin            構建jar項目       Apache  maven-war-plugin        構建war項目       Apache  maven-shade-plugin  構建包含依賴的jar包   Apache  maven-changelog-plugin  生成版本控制變更報告  Apache  maven-checkstyle-plugin 生成CheckStyle報告   Apache  maven-javadoc-plugin    生成javadoc文檔    Apache  maven-jxr-plugin          生成源碼交叉引用文檔       Apache  maven-pmd-plugin          生成pmd報告          Apache  maven-project-info-reports-plugin   生成項目資訊報告    Apache  maven-surefire-report-plugin     生成單元測試報告   Apache  maven-antrun-plugin     調用Ant任務         Apache  maven-archetype-plugin  基於Archetype生成項目骨架   Apache  maven-assembly-plugin   構建自定義格式的分發包     Apache  maven-dependency-plugin 依賴分析及控制         Apache  maven-enforcer-plugin   定義規則並強制要求項目遵守   Apache  maven-pgp-plugin          為項目構件生成pgp簽名      Apache  maven-help-plugin     獲取項目及Maven環境的資訊   Apache  maven-invoker-plugin      自動運行Maven項目構建並驗證  Apache  maven-release-plugin  自動化項目版本發布         Apache  maven-scm-plugin          集成版本控制系統         Apache  maven-source-plugin   生成源碼包                Apache  maven-eclipse-plugin  生成eclipse項目環境配置     Apache  build-helper-maven-plugin 包含各種支援構建生命周期的目標 Codehaus  exec-maven-plugin     運行系統程式或者java程式    Codehaus  jboss-maven-plugin   啟動、停止jboss、部署項目    Codehaus  properties-maven-plugin 從properties文件讀寫Maven屬性  Codehaus  sql-maven-plugin         運行sql腳本                    Codehaus  tomcat-maven-plugin  啟動、停止tomcat、部署項目   Codehaus  versions-maven-plugin   自動化批量更新pom版本       Codehaus  cargo-maven-plugin   啟動/停止/配置各類web容器自動化部署web項目  Cargo  jetty-maven-plugin  集成jetty容器、實現快速開發測試      Eclipse  mave-gae-plugin     集成google app engine     Googlecode  maven-license-plugin     自動化添加許可證證明至源碼文件    Googlecode  maven-andmid-plugin  構建Android項目                Googlecode

F. 超級POM

這個POM會被每個POM繼承。它的定義在model-builder.jar這個包下面。我們解壓就可以看到。

G. 圖片工具

http://techsmith.com/jing這個網站提供了一個不錯的製圖工具,有時間可以研究下。

H. 參考書籍

  1. 《Maven實戰》
  2. 《Maven權威指南》

I. Maven和gradle的對比

Exit mobile version