Maven

Maven

一.Maven簡介

1.1 何爲maven

Maven可翻譯爲"知識的積累" or"專家",是一款成功的開源跨平臺的項目管理工具,不管小型的開源類庫項目,仍是大型的企業級應用;不管傳統的瀑布式開發,仍是流行的敏捷模式,Maven都能大顯身手.

1.1.1 構建工具java

​ 咱們一直在不停的尋找避免重複的方法,設計的重複,編碼的重複,文檔的重複,固然還有構建的重複.Maven最大化的消除了構建的重複,抽象了構建生命週期,而且爲絕大部分的構建任務提供了已實現的插件,咱們不須要再定義過程,甚至不須要去實現這些過程當中的一些任務,只須要遵循Maven中的約定.mysql

​ 同時Maven幫助咱們標準化構建過程,之前十個項目可能有十種構建方式,有了Maven後,全部項目的構建命令都是一直且簡單的.所以Maven做爲一個構建工具:算法

  1. 能夠幫咱們自動化構建,
  2. 能夠幫咱們抽象構建過程
  3. 提供構建任務是實現
  4. 跨平臺
  5. 對外提供一直的操做接口

1.1.2 不只僅是構建工具spring

​ Maven不只是構建工具,仍是一個依賴管理工具和項目信息管理工具,提供中央倉庫來幫忙咱們自動下載構建,經過引入一套經緯機制來系統準確的定位每個構建(artifact).sql

1.1.3 Maven數據庫

​ 在Maven以前,有過程式的Make和Ant,開發者須要顯示的指定每個目標,以及完成該目標所須要執行的任務.針對每個項目,開發者都須要從新編寫這一過程,而其中就隱含着大量重複.apache

​ 而Maven是聲明式的,項目構建過程和過程各個階段所需的工做都由插件實現,而且大部分插件都是現成的,開發者只須要聲明項目的基本元素,Maven就執行內置的,完整的構建過程.設計模式

二.Maven的使用

2.1 pom文件

​ Maven項目的核心是pom.xml,POM(Project Object Model,項目對象模型)定義了項目的基本信息,用於描述項目如何構建,聲明項目依賴等.api

<modelVersion>4.0.0</modelVersion>:modelVersion指定了當前Pom模型的版本,固定爲4.0.0

<groupId>com.lsy</groupId>:groupId定義了項目屬於哪一個組,這個組每每和項目所在的組織和公司相關緩存

<artifactId>hello-world</artifactId>:artifactId定義了當前Maven項目在組中惟一的ID

<version>1.0-SNAPSHOT</version>:version指定了Hello World項目當前的版本,其中SNAPSHOT意爲快照,說明該項目還處於開發中

<name>Maven Hello World Project</name>:name元素聲明瞭一個對於用戶更爲友好的項目名稱.非必須

​ 當運行mvn clean compile命令:clean告訴Maven清理輸出目錄target/,compile告訴Maven編譯項目主代碼,從輸出中看到Maven首先執行clean:clean任務,刪除target/目錄(默認狀況下,Maven構建的全部輸出都在target目錄中,接着執行resources:resources任務(未定義項目資源),最後執行compiler:compile任務,將項目主代碼編譯至target/classes目錄

​ 其中clean:clean,resources:resources和compiler:compile對應了Maven插件及插件目標,好比clean:clean是clean插件的clean目標,compiler:compile是compiler插件的compile目標

2.2 測試

​ 首先添加Junit依賴

<dependencies>

​ <dependency>

​ <groupId>junit</groupId>

​ <artifactId>junit</artifactId>

​ <version>4.7</version>

​ <scope>test</scope>

​ </dependency>

<dependencies>

​ 其中scope元素爲依賴範圍,若依賴範圍爲test則表示該依賴只對測試有效,若是不聲明依賴範圍,則默認是compile,表示該依賴對主代碼和測試代碼均有效

當運行mvn clean test命令,Maven實際執行的不止test任務,而是clean:clean,resources:resources,compiler:compile,resources:testResources以及compiler:testCompile,即在執行測試以前,會先自動執行項目資源處理,主代碼編譯,測試資源處理,測試代碼編譯等工做,這是Maven生命週期的一個特性.

​ 因爲Maven核心插件之一compiler插件默認支持Java1.3,所以須要配置插件支持Java1.8

<build>

​ <plugins>

​ <plugin>

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

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

​ <configuration>

​ <source>1.8<source>

​ <target>1.8</target>

​ </configuration>

​ </plugin>

​ </plugins>

</build>

2.3 打包和運行

​ 將項目進行編譯,測試以後,下一個重要的步驟就是打包(package),當沒有指定打包類型時,默認打包類型是jar,當執行mvn clean package命令,Maven會在打包以前進行編譯,測試等操做,以後經過jar:jar任務負責打包,實際上就是jar插件的jar目標將項目主代碼打包成一個hello-world-1.0-SNAPSHOT.jar的文件

​ 當其餘項目須要直接引用這個jar時,接下來須要一個安裝的步驟,執行mvn clean install,此命令執行了安裝任務install:install,將項目輸出的jar安裝到了Maven本地倉庫中,而構件只有被下載到本地倉庫後,才能由Maven項目使用.

2.4 使用Archetype生成項目骨架

​ mvn archetype:generate,當運行插件時,格式爲:groupId:artifactId:version:goal,默認使用最新的穩定版

三. 座標和依賴

3.1 座標

  • Maven的一大功能仍是管理項目依賴,爲了能自動化解析任何一個Java構件,Maven就必須將它們惟一標識,這就依賴管理的底層基礎---座標
  • 在實際生活中,咱們能夠將地址看做是一種座標,省,市,區,街道等一系列信息能夠惟一標識城市中的任意居住地址,對應在Maven的世界中擁有很是巨大的構件,也就是日常使用的一些jar,war等文件
  • Maven定義了這樣一規則:世界上任意一個構建均可以使用Maven座標惟一標識,Maven座標的元素包括

    • groupId:定義當前Maven項目隸屬公司的實際項目
    • artifactId:該元素定義實際項目中的一個Maven模塊,推薦作法使用實際項目名稱爲artifact的前綴,便於尋找實際構建
    • version:該元素定義Maven項目當前所處的版本
    • package:該元素定義Maven項目的打包方式,默認爲jar包
    • classifier:該元素用來幫助定義構建輸出一些附屬構建,附屬構件與主構件對應
<groupId\>org.sonatype.nexus</groupId\>  
<artifactId\>nexus-indexer</artifactId\>  
<version\>2.0.0</version\>  
<packaging\>jar</packaging\>  
​  
<!-->   
1\. 以上5個元素,groupId,artifactId,version是必須定義的,packaging是可選的,而classifier是不能直接定義的  
2\. 項目構件的文件名格式:artifactId-version\[-classifier\].packaging

3.2 依賴

3.2.1 依賴的配置

  • 項目要引用Maven中的構建,就須要在pom文件中,經過座標來使用依賴,在根元素project下的dependencies能夠包含一個或多個dependency元素,用以聲明一個或多個項目依賴

    • groupId,artifactId和version:依賴的基本座標,對於任何一個依賴來講,基本座標是最重要的
    • type:依賴的類型,對應項目座標定義的packaging,通常沒必要聲明,默認爲jar
    • scope:依賴的範圍
    • optional:標記依賴是否可選
    • exclusions:用來排除傳遞性依賴

3.2.2 依賴的範圍

  • Maven項目在編譯項目主代碼時須要使用一套classpath,如編譯項目主代碼時須要使用spring-core,該文件以依賴的方式被引入到classpath中.其次,Maven在編譯和執行測試的時候會使用另一套classpath,依賴一樣會引入到相應的classpath中,最後在運行Maven項目時,又會使用一套classpath
  • 依賴範圍就是用來控制依賴與三種classpath(編譯classpath,測試classpath,運行classpath)的關係

    • compile:編譯依賴範圍,沒有指定時,爲默認依賴範圍.使用此依賴範圍的Maven依賴,對於編譯,測試運行三種classpath都有效,典型的例子爲:spring-core,在編譯,測試,運行階段都須要使用該依賴
    • test:測試依賴範圍,使用此依賴範圍的Maven依賴,只對於測試的classpath有效,在編譯主代碼或者運行項目時將沒法使用此類依賴,典型的例子就是JUnit,它只有在編譯器測試代碼及運行測試的時候才須要
    • provided:已提供依賴範圍,使用此依賴範圍的Maven依賴,對於編譯和測試classpath有效,但在運行時無效,典型的例子就是servlet-api,編譯和測試項目的時候須要該依賴,但在運行項目的時候,因爲容器已經提供,就不須要Maven重複引入了
    • runtime:運行時依賴範圍,使用此依賴範圍的Maven依賴,對於測試和運行classpath有效,但在編譯主代碼時無效,典型的例子是JDBC驅動實現,項目主代碼的編譯只須要JDK提供的JDBC接口,只有在執行測試或運行項目的時候才須要實現上述接口的具體JDBC驅動
    • system:系統依賴範圍,該依賴與三種classpath的關係,和provided依賴範圍徹底一致,可是,使用system範圍的依賴時必須經過systemPath元素顯示地指定依賴文件的路徑,因爲此類依賴不是經過Maven倉庫解析的,並且每每與本機系統綁定,可能形成構建的不可移植性

      <dependency>
      <groupId>javax.sql</groupId>
      <artifactId>jdbc-stdext</artifactId>
      <version>2.0</version>
      <scope>system</scope>
      <systemPath>${java.home}/lib/rt.jar</systemPath>
      </dependency>

    • import:導入依賴範圍,該依賴範圍不會對三種classpath產生實際的影響,主要用於導入其餘pom文件中的dependencyManagement元素對於依賴版本約束的內容

3.2.3 傳遞性依賴

  • 在使用Maven依賴時,如Spring Framework,此依賴又會依賴其餘的開源庫,所以實際中每每會下載一個很大的如spring-framework-2.5.6-with-dependencies.zip包,這樣每每就引入了不少沒必要要的依賴,而Maven的傳遞性依賴機制就能夠很好的解決這一問題

    • 當A項目引入一個compile範圍的B依賴,而B依賴中有一個compile範圍的C依賴,那麼C依賴一樣會成爲A的compile範圍依賴

      image.png

  • 傳遞性依賴和依賴範圍

    • 假設A依賴於B,B依賴於C,那麼A對於B是第一直接依賴,B對於C是第二直接依賴,A對於C是傳遞性依賴
    • 當第二直接依賴是compile的時候,傳遞性依賴與第一直接依賴範圍一致
    • 當第二直接依賴是test的時候,依賴不會得以傳遞
    • 當第二直接依賴是provided的時候,只傳遞第一直接依賴範圍爲provided的依賴,且傳遞性依賴範圍爲provided
    • 當第二直接依賴是runtime的時候,傳遞性地依賴的範圍與第一直接依賴的範圍一致,但compile例外,此時傳遞性依賴的範圍爲runtime

image.png

3.2.4 可選依賴

  • 假若有這樣一個依賴關係,A依賴於B,B依賴於X和Y,B對於X和Y的依賴都是可選依賴,根據傳遞性依賴的定義,若是這三個依賴的範圍都是compile,那麼X,Y就是A的compile範圍傳遞性依賴,可是因爲X,Y都是可選依賴,因此依賴不會得以傳遞,所以X,Y不會對A有任何影響

    image.png

    • 爲何會有可選依賴這一特性呢?

      • 當B實現了兩個特性,特性一依賴於X,特性二依賴於Y,而且這兩個特性是互斥的,用戶不可能同時使用兩個特性,好比B是一個持久層隔離工具包,支持多種數據庫,在使用這個工具包的時候,只會依賴一種數據庫
<dependencies\>  
 <dependency\>  
 <groupId\>mysql</groupId\>  
 <artifact\>mysql-connector-java</artifact\>  
 <version\>5.6.0</version\>  
 <optional\>true</optional\>  
 </dependency\>  
 <dependency\>  
 <groupId\>postgresql</groupId\>  
 <artifactpostgresql</artifact>  
 <version\>8.4-701.jdbc3</version\>  
 <optional\>true</optional\>  
 </dependency\>  
</dependencies\>

*   以上使用<optional\>元素表示兩個依賴爲可選依賴,他們只會對當前項目B產生影響,當其餘項目依賴於B時,這兩個依賴不會被傳遞,因此當A依賴於B項目時,若是要使用mysql數據庫,那麼須要顯式的聲明mysql-connector-java這一依賴
    
*   關於可選依賴,在理想的狀況下,是不該該使用可選依賴的,使用可選依賴的緣由是某一個項目中實現了多個特性,而根據單一職責原則,應該針對不一樣的特性分別建立一個Maven項目,用戶根據須要選擇使用其中某一個依賴

3.2.5 排除依賴

  • 傳遞性依賴會給項目隱式的引入不少依賴,這極大的簡化了項目依賴的管理,可是有時候這種特性䧥帶來問題

    • 例如,當前項目有一個第三方依賴,而這個依賴因爲某些緣由依賴了另外一個類庫的SNAPSHOT,那麼整個SNAPSHOT就會成爲當前項目的傳遞性依賴,而SNAPSHOT的不穩定性會直接影響到當前的項目,這時候就須要排除掉該SNAPSHOT,而且在當前項目中聲明該類庫的某個正式發佈版

      <dependencies>
      <dependency>
      <groupId>com.lsy.myproject</groupId>
      <artifactId>myproject-a</artifactId>
      <version>1.0.0</version>
      <exclusions>
      <exclusion>
      <groupId>com.lsy.myproject</groupId>
      <artifactId>myproject-b</artifactId>
      </exclusion>
      </exclusions>
      </dependency>
      <dependency>
      <groupId>com.lsy.myproject</groupId>
      <artifactId>myproject-b</artifactId>
      <version>1.1.0</version>
      </dependency>
      </dependencies>

3.2.6 歸類依賴

  • 當一些依賴來自同一個項目的不一樣模塊,這些依賴的版本都應該是相同的,未來升級也是一塊兒升級,如Spring Framework,這時可使用properties元素定義Maven屬性

    <properties>
    <springframework.version>4.2.1</springframework.version>
    </properties>

    <dependencies>
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <verison>${springframework.version}</verison>
    </dependency>
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-beans</artifactId>
    <verison>${springframework.version}</verison>
    </dependency>
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <verison>${springframework.version}</verison>
    </dependency>
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context-support</artifactId>
    <verison>${springframework.version}</verison>
    </dependency>
    </dependencies>

3.2.7 優化依賴

  • 在軟件開發過程當中,一般會經過重構等方式不斷優化本身代碼,一樣,對於Maven項目的依賴也須要對其進行優化

    • 去除多餘的依賴
    • 顯示聲明某些必要的依賴
  • Maven會自動解析全部項目的直接依賴和間接依賴,而且根據規則判斷每一個依賴範圍,對於一些依賴衝突,也能進行調整,以確保任何一個構建只有惟一的版本在依賴中存在,此稱爲解析依賴

    • mvn dependency:list 查看當前項目的已解析依賴
    • mvn dependency:tree 以樹結構查看已解析依賴
    • mvn dependency:analyze 解析依賴

      • Used undeclared dependencies:指項目中使用到的,可是沒有顯示聲明的依賴,這種依賴意味着潛在的風險,當前項目直接在使用它們,因此須要顯示聲明任何項目中直接用到的依賴
      • Unused declared dependencies:指項目中未使使用的.但顯示聲明的依賴

四. 倉庫

  • 以前已經介紹了Maven的座標和依賴,座標和依賴是任何一個構件在Maven世界中的邏輯表示方式,而構建的物理表示方式是文件,Maven經過倉庫來同一管理這些文件
  • 在Maven世界中,任何一個以依賴,插件或項目構建的輸出,均可以成爲構件

    • 例如:log4j-1.2.15.jar,maven-compiler-plugin-2.0.2.jar或項目打包後的myproject-1.0.0-SNAPSHOT.jar
  • 在一臺工做站上,可能會有幾十個項目,全部項目在/lib目錄下都會有本身所需的依賴包,而這些依賴中都有大量的重複,每一個項目各自存儲本身所需的依賴包不只形成磁盤空間的浪費,並且也難於同一管理,文件的複製等操做
  • 得益於座標機制,任何Maven項目使用任何一個構建的方式都是相同的,所以,Maven能夠在某個位置統一存儲全部Maven項目共享的構件,這個統一的位置就是倉庫,爲了實現重用,項目構建完畢後生成的構建也能夠安裝或部署到倉庫中

4.1 倉庫的佈局

  • 任何一個構建都有其惟一的座標,根據這個座標能夠定義其在倉庫中的惟一存儲路徑,這就是Maven的倉庫佈局方式,如log4j:log4j:1.2.15這一依賴,其對應的倉庫路徑爲log4j/log4j/1.2.15/log4j-1.2.15.jar

    • 路徑與座標的關係爲:groupId/artifactId/version/artifactId-verision.packaging

4.2 倉庫的分類

  • 對於Maven來講,倉庫分爲兩類

    • 本地倉庫
    • 遠程倉庫

      • 中央倉庫:Maven核心自帶的倉庫服務器,它包含了絕大部分開源的構件
      • 私服:另外一種特殊的遠程倉庫,爲了節省寬帶和時間,應該在局域網內部構建一個私有倉庫服務器,用其代理全部外部的遠程倉庫,內部項目部署到私服上供其它項目使用
      • 其餘公共庫
  • 當Maven根據座標尋找構件時,它首先會查看本地倉庫,若是本地倉庫存在此構件,則直接使用,若是本地倉庫不存在此構件,或者須要查看是否有更新的構件版本,Maven就會去遠程倉庫查找,發現須要的構件後下載到本地倉庫再使用,若本地和遠程都沒有,則報錯

4.3 本地倉庫

  • 通常來講,在Maven項目目錄下,沒有注入lib/這樣用來存放依賴文件的目錄,當Maven執行編譯或測試時,若是須要使用依賴文件,它老是基於座標使用本地倉庫的依賴文件

    • 默認狀況,在用戶目錄下路徑名爲.m2/repository/的倉庫目錄,當想自定義本地倉庫目錄地址時,能夠編輯~/.m2/setting.xml,設置localRepository元素來指定倉庫地址

      <settings>
      <localRepository>D:/java/repository/</localRepository>
      </settings>

    • 默認狀況下,~/.m2/settings.xml文件是不存在的,用戶須要從Maven安裝目錄複製$M2_HOME/conf/settings.xml文件再編輯,建議不要直接修改全局目錄的settings.xml,而是在用戶目錄下進行修改
  • 一個構建只有在本地倉庫中,才能由其它Maven項目使用

    1. 依賴Maven從遠程倉庫下載到本地倉庫中
    2. 將本地項目的構件安裝到Maven本地倉庫中 mvn clean install

4.4 遠程倉庫

  • 安裝好Maven後,若是不執行任何Maven命令,本地倉庫目錄是不存在的,只有輸入第一條Maven命令後,Maven纔會建立本地倉庫,並根據配置和須要,從遠程倉庫下載至本地倉庫

    • 這就比如藏書,本地倉庫比如書房,遠程倉庫比如書店,我須要讀書時先去書房找,當書房沒有時,就去書店買回放到書房,而且通常對每一個人來講,書房只有一個,而外面的書店能夠有多個

4.4.1 中央倉庫

  • 最原始的本地倉庫是空的,因此Maven必須知道至少一個可用的遠程倉庫,才能在執行Maven命令時下載到須要的構建,中央倉庫就是這樣一個默認的遠程倉庫,能夠經過解壓工具打開$M2_HOME/lib/maven-model-builder-3.0.jar中的org/apache/maven/model/pom-4.0.0.xml文件

    <repositories>
    <repository>
    <id>central</id>
    <name>Maven Repository Switchboard</name>
    <url>http://repo1.maven/org/maven2&lt;/url>
    <layout>default</layout>
    <snapshots>
    <enable>false</enable>
    </snapshots>
    </repository>
    </repositories>

    • 這段配置是全部Maven項目都會繼承的超級Pom文件,這段配置使用id central對中央倉庫進行惟一標識,其名稱爲Maven Repository Switchboard,它使用default倉庫佈局,也就是在以前介紹的倉庫佈局,最後snapshots元素表示不從該倉庫下載快照版

4.4.2 私服

  • 私服是一種特殊的遠程倉庫,它是架設在局域網內的倉庫服務,私服代理廣域網上的遠程倉庫,供局域網內的Maven用戶使用,當Maven須要下載構建的時候,它從私服請求,若是私服上不存在該構件,則從外部的遠程倉庫下載,緩存在私服上以後,再爲Maven的下載提供服務

    • 此外,一些沒法從外部下載的構件也能夠從本地上傳到私服上供你們使用
  • 私服的好處

    • 節省外網帶寬:創建私服一樣能夠減小組織本身的開支,大量的對於外部倉庫的重複請求會消耗很大的帶寬,利用私服代理外部倉庫以後,對外的重複構件下載即可以消除,即下降外網帶寬的壓力
    • 加速Maven構件:不停的鏈接請求外部倉庫是十分耗時的,但Maven的一些內部機制(如快照更新檢查井)要求Maven在執行構建時不停的檢查遠程倉庫數據,所以,當項目配置不少外部倉庫時,構建速度就會下降
    • 部署第三方構件:當某個構件沒法從任何一個外部遠程倉庫獲取,創建私服以後,即可以將這些構件部署到這個內部的倉庫中,供內部的Maven項目使用
    • 提升穩定性,加強控制:Maven構建高度依賴遠程倉庫,所以,大哥Internet不穩定的時候,Maven構建也會變得不穩定,甚至沒法構建,使用私服後,便是短暫時沒有Internet鏈接,因爲私服中有大量緩存,Maven依然能夠正常運行,而且私服中有不少額外的權限功能控制
    • 下降中央倉庫的負荷:天天中央倉庫都須要面對大量的下載請求,使用私庫能夠下降對於中央倉庫的負荷
  • 遠程倉庫的配置

    • 不少狀況下,默認的中央倉庫沒法知足項目的需求,可能項目須要的構件存在於另外一個遠程倉庫中,能夠經過Pom/settings文件中來配置該倉庫

      <!--> POM文件 </!-->
      <project>
      .....
      <repositories>
      <repository>
      <id>jboss</id>
      <name>JBoss Repository</name>
      <url>http://repository.jboss.com/m...;/url>
      <release>
      <enabled>true</enabled>
      </release>
      <snapshots>
      <enabled>false</enabled>
      </snapshots>
      <layout>default</layout>
      </repository>
      </repositories>
      </project>

      <!--> settings文件 </!-->
      <profiles>
      <profile>
      <id>dev</id>
      <activation>
      <activatedByDefault>true</activatedByDefault>
      </activation>
      <repositories>
      <repository>
      <id>jboss</id>
      <name>JBoss Repository</name>
      <url>http://repository.jboss.com/m...;/url>
      <release>
      <enabled>true</enabled>
      </release>
      <snapshots>
      <enabled>false</enabled>
      </snapshots>
      <layout>default</layout>
      </repository>
      </repositories>
      </profile>
      </profiles>

      • 在repositories元素下,可使用repository子元素聲明一個或多個遠程倉庫,而且聲明一個id,任何一個倉庫聲明的id必須是惟一id
      • Maven自帶的中央倉庫使用的id爲central,若是其餘倉庫使用該id,則會覆蓋中央倉庫的配置,該配置中的url指向了倉庫的地址
      • 該配置中的release和snapshots元素用來控制Maven對於發佈版構件和快照版構件的下載,對於release和snapshots元素來講除了enabled子元素,還有updatePolicy和checksumPolicy元素

        • updatePolicy:用來配置Maven從遠程倉庫檢查更新的頻率,默認爲daily

          • daily:天天
          • never:從不
          • always:每次
          • interval :X :每隔X分鐘一次
        • checksumPolicy:配置Maven檢查文件失敗時的策略,默認爲warn

          • fail:Maven遇到驗證和錯誤就讓構建失敗
          • warn:Maven遇到驗證和錯誤就發出警告信息
          • ignore:Maven遇到驗證和錯誤時徹底忽略
<snapshots\>  
         <enabled\>true</enabled\>  
         <updatePolicy\>daily</updatePolicy\>  
         <checksumPolicy\>ignore</checksumPolicy\>  
        </snapshots\>
        
    
*   遠程倉庫的驗證
    
    *   大部分遠程倉庫無需認證就能夠訪問,但出於安全考慮,咱們須要提供一些認證信息才能訪問一些遠程倉庫
        
    *   配置認證信息和配置倉庫信息不一樣,倉庫信息能夠直接配置在POM文件中,可是認證信息必須配置在settings.xml文件中
        
    *   其中server元素的id必須與POM文件中須要認證的repository元素的id徹底一致
        
          
        <settings\>  
         ......  
         <servers\>  
         <server\>  
         <id\>my-project</id\>  
         <username\>user</username\>  
         <password\>password</password\>  
         </server\>  
         </servers\>  
         .......  
        </settings\>
        
    
*   部署至遠程倉庫
    
    *   私服一大做用就是部署第三方構件,包括組織內部生成的構件以及沒法從外部倉庫直接獲取的構件,不管是平常開發中生成的構件,仍是正式版本發佈的構件,都須要部署到倉庫中
        
        *   distributionManagement包含repository和snapshotRepository子元素,前者表示發佈版構件的倉庫,後者表示快照版的倉庫
            
        *   往遠程倉庫部署構件時,每每須要認證,認證配置需在settings.xml中建立一個server元素,其id與倉庫的id匹配,不管從遠程倉庫下載構件,仍是部署構件至遠程倉庫,當須要認證時,配置方式都是同樣的
            
        
          
        <!--> 在POM文件中配置distributionManagement </!-->  
        <project\>  
         .....  
         <distributionManagement\>  
         <repository\>  
         <id\>jboss-release</id\>  
         <name\>JBoss Release Repository</name\>  
         <url\>http://192.168.1.99/content/repository/jboss-release</url\>  
         </repository\>  
         <snapshotRepository\>  
         <id\>jboss-snapshots</id\>  
         <name\>JBoss Snapshots Repository</name\>  
         <url\>http://192.168.1.99/content/repository/jboss-snapshots</url\>  
         </snapshotRepository\>  
         </distributionManagement\>  
         .....  
        </project\>

4.5 快照

  • 在Maven的世界中,任何一個項目或者構件都必須有本身的版本,版本的值多是1.0.0,1.3-alpha-4,2.0,2.1-SNAPSHOT或者2.1-20191214.221414-13,其中1.0.0,1.3-alpha-4和2.0是穩定的發佈版本,而2.1-SNAPSHOT和2.1-20091214.221414-13是不穩定的快照版本
  • 對於一個穩定的版本,若是倉庫中已經包含,那麼Maven就不會再去對照遠程倉庫進行更新,除非每次執行Maven命令前,清除本地倉庫中等待穩定版本,而對於一個正在迭代的項目,若是要實時更新版本的內容就須要頻繁的修改新的版本名稱,這樣是對版本號的濫用
  • 針對這種狀況,使用快照版時,Maven會自動爲構件打上時間戳,所以,Maven就能隨時找到倉庫中該構建最新版本的文件,一旦有新的更新,就會去同步到本地倉庫.當項目通過完善的測試後須要發佈的時候,再將快照版本更改成發佈版本
  • 快照版本只應該在組織內部的項目或模塊之間依賴使用,由於這時,組織對這些快照版本的依賴具備徹底的理解和控制權,項目不該該依賴任何組織外部的快照版本依賴,因爲快照版本的不穩定性,隨時可能發生變化,這樣的依賴會有潛在的危險

4.6 從倉庫解析依賴的機制

  • 當本地倉庫沒有依賴構件的時候,Maven會自動從遠程倉庫下載,當依賴版本爲快照版本時,Maven會自動找到最新的快照,這背後的依賴機制能夠歸納以下

    1. 當依賴的範圍時system的時候,Maven直接從本地文件系統解析構件
    2. 根據依賴座標計算倉庫路徑後,嘗試從本地倉庫尋找構件,若是發現相應的構件,則解析成功
    3. 在本地倉庫不存在相應構件的狀況下,若是依賴的版本時顯式的發佈版本構件,如1.2,2.1-beta-1等,則遍歷全部的遠程倉庫,發現後,下載並解析使用
    4. 若是依賴的版本時RELEASE或LATEST,則基於更新策略讀取全部遠程倉庫的元數據groupId/artifact/maven-metadata.xml,將其與本地倉庫的對應元數據合併後,計算出RELEASE或LATEST真實的值,而後基於這個真實的值檢查本地倉庫和遠程倉庫
    5. 若是依賴的版本是SNAPSHOT,則基於更新策略讀取全部遠程倉庫的元數據groupId/artifactId/maven-metadata.xml,將其與本地倉庫的對應數據合併後,獲得最新的快照版本的值,而後基於該值檢查本地倉庫,或者從遠程倉庫下載
    6. 若是最後解析獲得的構件版本是時間戳格式,如1.4.1-20191104.121455-8,則複製其時間戳格式的文件至非時間戳格式,如SNAPSHOT,並使用該非時間戳格式的構件

      <!--> 基於groupId和artifactId的maven-metadata.xml </!-->
      <?xml version="1.0" encoding="UTF-8"?>
      <metadata>
      <groupId>org.sonatype.nexus</groupId>
      <artifactId>nexus</artifactId>
      <versioning>
      <latest>1.4.2-SNAPSHOT</latest>
      <release>1.3.7</release>
      <versions>
      <version>1.3.5</version>
      <version>1.3.6</version>
      <version>1.3.7</version>
      <version>1.4.0-SNAPSHOT</version>
      <version>1.4.1-SNAPSHOT</version>
      <version>1.4.2-SNAPSHOT</version>
      </versions>
      <lastUpdated>20191214221133</lastUpdated>
      </versioning>
      </metadata>

  • 該XML文件列出了倉庫中存在的構件全部可用的版本,同時latest元素指向了這些版本中最新的那個版本1.4.2-SNAPSHOT,而release元素指向了這些版本中最新的發佈版本1.3.7,Maven經過合併多個遠程倉庫及本地倉庫的元數據,就能計算出基於全部倉庫的latest和release

    • 須要注意的是,在依賴聲明使用LATEST和RELEASE是不推薦的作法,由於Maven隨時可能解析到不一樣的構件,且Maven不會明確告訴用戶這樣的變化

4.7 鏡像

  • 若是倉庫X能夠提供倉庫Y存儲的全部內容,那麼就能夠認爲X是Y的一個鏡像,因爲地理位置的因素,中央倉庫的下載速度會比較慢,這時咱們能夠配置Maven使用鏡像來代替中央倉庫,編輯settings.xml

    <settings>
    ......
    <mirrors>
    <mirror>
    <id>maven.net.cn</id>
    <name>one of the central mirror in china</name>
    <url>http://maven.net.cn/content/g...;/url>
    <mirrorOf>central</mirrorOf>
    </mirror>
    </mirrors>
    ......
    </settings>

    • <mirrorOf>的值爲central,表示該配置爲中央倉庫的鏡像,任何對於中央倉庫的請求都會轉至該鏡像,用戶也可使用一樣的方法配置其餘倉庫的鏡像,另外三個元素id,name,url與通常倉庫配置無異,表示該鏡像倉庫的惟一標識,名稱以及地址

      • 若該鏡像要進行驗證,即基於該id配置倉庫認證
<settings\>  
 .....  
 <mirrors\>  
 <mirror\>  
 <id\>internal-repository</id\>  
 <name\>Internal Repository Mananger</name\>  
 <url\>http://192.168.1.100/maven2/</url\>  
 <mirrorOf\>\*</mirrorOf\>  
 </mirror\>  
 </mirrors\>  
 .....  
</settings\>
  • 以上mirrorOf元素的值爲*,表示該配置是全部Maven倉庫的鏡像,對於任何遠程倉庫的請求都會被轉至該指定的倉庫,若是鏡像倉庫須要驗證,則配置一個id爲internal-repository的<server>便可

    • <mirrorOf>*<mirrorOf>:匹配全部遠程倉庫
    • <mirrorOf>external:*<mirrorOf>:匹配全部遠程倉庫,使用localhost的除外,使用file://協議的除外,也就是說,匹配全部不在本機上的遠程倉庫
    • <mirrorOf>repo1,repo2<mirrorOf>:匹配倉庫repo1和repo2,用逗號分隔多個倉庫
    • <mirrorOf>*,! repo1<mirrorOf>:匹配全部的遠程倉庫,除repo1外,使用感嘆號將倉庫從匹配中排除
  • 須要注意的是,因爲鏡像倉庫徹底屏蔽了被鏡像倉庫,當鏡像倉庫不穩定或中止服務時,Maven仍將沒法訪問被鏡像倉庫,所以沒法下載構件

五. 生命週期和插件

5.1 Maven的生命週期

  • 在Maven出現以前,項目構建的生命週期就已經存在,可是不一樣的公司和開發人員雖然一樣在作構件工做,其對於不一樣的項目卻不可以重用,只能從新定製開發,而Maven的生命週期就是爲了對全部的構件過程進行抽象和統一

    • 這個生命週期包含了項目的清理,初始化,編譯,測試,打包,集成測試,驗證,部署和站點生成等幾乎全部的構件步驟,也就是說幾乎全部項目的構件,都能映射到這樣一個生命週期上
  • Maven的生命週期是抽象的,這意味着生命週期自己不作任何實際的工做,在Maven的設計中,實際的任務(如編譯主代碼)都交由插件完成,這種思想和設計模式的模方法相似,在父類中定義算法的總體結構,子類能夠經過實現或重寫父類的方法來控制實際的行爲

    public abstract class Template{
    public void build(){
    initialize();
    compile();
    test();
    packagee();
    integrationTest();
    deploy();
    }

    protect abstract void initialize();
    protect abstract void compile();
    protect abstract void test();
    protect abstract void packagee();
    protect abstract void integrationTest();
    protect abstract void deploy();
    }

    • 在Maven的生命週期中抽象了各個步驟,定義了他們的次序,可是沒有提供具體的實現,而經過插件機制爲每一個構件步驟綁定一個或多個插件行爲,並且Maven爲大多數構件步驟都綁定了默認的插件,例如,針對編譯的maven-compiler-plguin,針對測試的maven-surefire-plugin等
    • Maven定義的生命週期和插件機制一方面保證了全部Maven項目有一致的構件標準,另外一方面又經過默認的插件簡化和穩定實際項目的構件,此外,該機制還提供了足夠的擴展,用戶能夠經過配置現有的插件或自定義插件來自定義構件行爲
  • 三套聲明週期

    • Maven擁有三套相互獨立的生命週期,他們分別爲clean,default和site,每一個生命週期包含一些階段,這些階段是有序的,而且後面的階段依賴於前面的階段,可是三套聲明週期自己是互相獨立的

      1. clean生命週期:清理項目

        • pre-clean:執行一些清理前須要完成的工做
        • clean:清理上次構件生成的文件
        • post-clean:執行一些清理後須要完成的工做
      2. default聲明週期:定義了真正的構件所需執行的全部步驟

        • validate
        • initialize
        • generate-sources
        • process-sources:處理項目主資源文件,通常來講針對/src/main/resources目錄的內容進行變量替換等工做後,複製到項目輸出的主classpath目錄中
        • compile:編譯項目的主源碼,通常來講針對/src/main/java目錄下的Java文件至目錄輸出的主classpath目錄中
        • process-classes
        • generate-test-sources
        • process-test-sources:處理項目測試資源文件,通常來講針對/src/test/resources目錄的內容進行變量替換工做後,複製到項目輸出的測試classpath目錄
        • test-compile:編譯項目的測試代碼,通常來講針對/src/test/java目錄下的java文件至輸出的測試classpath目錄中
        • test:使用單元測試框架運行測試,測試代碼不會被打包或部署
        • prepare-package
        • package:接收編譯好的代碼,打包成可發佈的格式,如jar
        • pre-integration-test
        • integration-test
        • post-integration-test
        • verify
        • install:將包安裝到Maven本地倉庫,供本地其餘Maven項目使用
        • deploy:將最終的包複製到遠程倉庫,供其餘開發人員和Maven項目使用
      3. site聲明週期:創建和發佈項目站點,Maven可以基於POM所包含的信息,自動生成一個友好的站點供交流和發佈項目信息

        • pre-site:執行一些在生成項目站點以前須要完成的工做
        • site:生成項目站點文檔
        • post-site:執行一些在生成項目站點後須要完成的工做
        • site-deploy:將生成的項目站點發布到服務器上
    • 命令行與生命週期

      • 從命令行執行Maven任務最主要方式就是調用Maven的生命週期,各個生命週期是相互獨立的,而生命週期的階段是有先後依賴關係的

        • mvn clean:該命令調用clean生命週期的clean階段,實際執行階段爲pre-clean和clean
        • mvn test:該命令調用default生命週期的test階段,實際執行的階段爲default生命週期的validate,initialize直到test的全部階段,這也解釋爲何在執行測試的時候,項目代碼可以自動編譯
        • mvn clean install:該命令調用clean生命週期的clean階段和default生命週期的install階段
        • mvn clean deploy site-deploy:該命令調用clean生命週期的clean階段,default生命週期的deploy階段和site生命週期的site-deploy階段

5.2 插件

5.2.1 插件目標和綁定

  • Maven的核心僅僅定義了抽象的生命週期,具體的任務是交由插件完成的,插件以獨立的構件形式存在

    • 對於插件自己而言,爲了可以複用代碼,它每每可以完成多個任務,爲每一個功能編寫一個插件顯然不合理,由於這些任務背後有大量可複用的代碼,所以,這些功能彙集到一個插件裏面,每一個功能就是一個插件目標
  • Maven的生命週期和插件相互綁定,用以完成實際的構件任務,具體而言,是生命週期的階段與插件的目標相互綁定,以完成某個具體的構件任務

    • 例如:編譯這一任務對應了default生命週期的compile這一階段,而maven-compiler-plugin這一插件的compile目標能完成此任務

image.png

image.png

  • 自定義綁定

    • 除了內置綁定之外,用戶還可以本身選擇將某個插件目標綁定到生命週期的某個階段上,這種自定義綁定方式能讓Maven項目在構件過程當中執行更多更豐富的任務

      • 當須要建立項目的源碼jar包,maven-source-plugin能夠幫咱們完成任務,可是內置的插件綁定關係中並無涉及這一任務,所以須要自行配置,它的jar-no-fork目標能將項目主代碼打包成jar文件,能夠將其綁定到default生命週期的verify階段,在執行完集成測試和安裝構件以前建立源碼jar包

        <build>
        <plugins>
        <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-source-plugin</artifactId>
        <version>2.1.1</version>
        <executions>
        <execution>
        <id>attach-sources</id>
        <phase>verify</phase>
        <goals>
        <goal>jar-no-fork</goal>
        </goals>
        </execution>
        </executions>
        </plugin>
        </plugins>
        </build>

      • 在POM的build元素下的plugins子元素中聲明插件的使用,上例用到了是maven-source-plugin,其groupId爲org.apache.maven.plugins,用戶老是應該聲明一個非快照版本,這樣能夠避免因爲插件版本變化形成的構件不穩定性

        • 除了基本的插件座標聲明外,還有插件執行配置,executions下每一個execution子元素能夠用來配置執行一個任務,該例配置了一個id爲attach-sources的任務,經過phase配置將其綁定到verify生命週期階段上,再經過goals配置指定要執行的插件目標,在運行mvn verify時就會執行該任務
        • 有時候.便是不經過phase元素配置生命週期階段,插件目標也能綁定到生命週期中去,緣由是:有不少插件的目標在編寫時就已經定義了默認綁定階段,可使用maven-help-plugin查看詳細信息

          • mvn help:describe -Dplugin=org.apache.maven.plugins:maven-source-plugin:2.1 -Ddetail

            • Bound to phase : package 默認綁定到package生命週期
      • 當插件目標被綁定到不一樣生命週期階段的時候,其執行順序會由生命週期階段的前後順序決定,若是多個目標被綁定到同一個階段,他們的順序就由插件聲明的前後順序決定

5.2.2 插件配置

  • 用戶能夠經過配置插件目標的參數,進一步調整插件目標所執行的任務,幾乎全部Maven插件的目標均可以配置參數,用戶能夠經過命令行和POM配置等方式來配置這些參數

    • 命令行插件配置

      • 在平常的Maven使用中,咱們一般從命令行輸入並執行Maven命令,不少插件目標的參數都支持從命令行配置,用戶能夠在Maven命令中使用-D參數,並伴隨一個參數鍵=參數值的形式,來配置插件目標的參數

        • mvn install -Dmaven.test.skip = ture:給maven.surefire-plugin提供一個maventest.skip參數,當參數爲true時,就會跳過執行測試

          • 參數-D是Java自帶的,其功能是經過命令行設置一個Java系統屬性,Maven簡單地重用了該參數,在準備插件的時候檢查系統屬性來實現插件參數的配置
    • POM中插件全局配置

      • 並非全部的插件參數都適合用命令行配置,有些參數的值從項目建立到項目發佈都不會改變,或者說不多改變,這種狀況下在POM文件中一次性配置比重複在命令行輸入更合理

        • 用戶能夠在聲明插件的時候,對插件進行一個全局的配置,全部基於該插件的目標任務,都會使用這些配置,如:maven-compiler-plugin來編譯1.8版本的源文件,生成與JVM1.8兼容的字節碼文件

          <build>
          <plugins>
          <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifact>maven-compiler-plugin</artifact>
          <version>2.1</version>
          <configuration>
          <source>1.8</source>
          <target>1.8</target>
          </configuration>
          </plugin>
          </plugins>
          </build>

5.2.3 獲取插件信息

  • 當遇到一個構建任務時,不只須要知道去哪裏找到合適的插件,還須要詳細的瞭解插件的配置點,因爲Maven的插件很是多,其中大部分沒有完善的文檔,所以,經過幫助命令來了解插件顯得很是重要

    1. 使用maven-help-plugin描述插件

      • 除了訪問在線的插件文檔外,能夠藉助maven-help-plugin來獲取插件的詳細信息

        • mvn help:decribe -Dplugin = org.apache.maven.plugins:maven-compiler-plugin:2.1

          • 這裏執行的是maven-help-plugin的decribe目標,在參數plugin中輸入須要描述插件的groupId,artifactId和version,Maven在命令行的輸出maven-compiler-plugin的簡要信息,包括插件的座標,目標前綴和目標等信息
      • 在描述插件時,能夠省去版本信息,Maven會自動獲取最新版原本進行表述,並能夠用目標前綴來替換座標

        • mvn help:describe -Dplugin=compiler
      • 若是僅僅描述某個插件目標的信息,則加上goal參數,更詳細的信息,則加上detail參數

        • mvn help:describe -Dplugin=compiler -Dgoal=compile -Ddetail

5.2.4 插件解析機制

  • 爲了方便用戶使用和配置插件,Maven不須要用戶提供完整的插件座標信息,就能夠解析獲得正確的插件

    • 插件倉庫

      • 與依賴構件同樣,插件構件一樣基於座標存儲在Maven倉庫中,在須要的時候Maven先從本地倉庫尋找,若是不存在,則從遠程倉庫查找,找到插件以後,再下載到本地倉庫使用
      • 不一樣於repositories以及repository子元素,插件的遠程倉庫使用pluginRepositories和pluginRepository配置

        <pluginRepositories>
        <pluginRepository>
        <id>central</id>
        <name>Maven Plugin Resository</name>
        <url>http://repo1.maven.org/maven2&lt;/url>
        <layout>default</layout>
        <snapshots>
        <enabled>false</enabled>
        </snapshots>
        <release>
        <enabled>true</enabled>
        <updatePolicy>never</updatePolicy>
        </release>
        </pluginRepository>
        </pluginRepositories>

  • 插件默認的groupId

    • 在POM中配置插件時,若是該插件時Maven的官方插件(即groupId爲org.apache.maven.plugins),就能夠省略groupId,Maven在解析該插件的時候,會自動使用默認groupId不起

      <build>
      <plugins>
      <plugin>
      <artifactId>maven-compiler-plugin</artifactId>
      <version>2.1</version>
      <configuration>
      <source>1.8</source>
      <target>1.8</target>
      </configuration>
      </plugin>
      </plugins>
      </build>

  • 解析插件版本

    • 一樣爲了簡化插件的配置和使用,在用戶沒有提供插件版本的狀況下,Maven會自動解析插件版本

      • 首先Maven在超級POM中爲全部核心插件設定了版本,超級POM是全部Maven項目的父POM,全部項目都繼承這個超級POM配置,所以即便用戶不加任何配置,Maven在使用核心插件時,它的版本就已經肯定了
      • 若是用戶使用的某個插件沒有設定版本,而且這個插件也不屬於核心插件,Maven機會去檢查全部倉庫中可用的版本,經過倉庫元數據groupId/artifactId/maven-metadata.xml文件,如maven-compiler-plugin插件爲例,它在org/apache/maven/plugins/maven-compiler-plugin/maven-metadata.xml

        <?xml version="1.0" encoding="UTF-8"?>
        <metadata>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <versioning>
        <latest>2.1</latest>
        <release>2.1</release>
        <versions>
        <version>2.0-beta-1</version>
        <version>2.1</version>
        <version>2.2</version>
        <version>2.3</version>
        </versions>
        <lastUpdated>20190102092331</lastUpdated>
        </versioning>
        </metadata>

      • Maven遍歷本地倉庫和全部遠程倉庫,將倉庫元數據合併後,就能計算出latest和release的值,maven3將版本解析到最新的非快照版,如2.1

5.2.5 解析插件前綴

  • Maven命令支持使用插件前綴來簡化插件的使用,插件前綴和groupId:artifact是一一對應的,這種匹配關係存儲在倉庫元數據中,與以前提到的groupId/artifactId/maven-metadata.xml不一樣,這裏倉庫元數據爲groupId/maven-metadata.xml,通常插件都位於/org/apache/maven/plugins/和org/code-haus/mojo/,能夠經過settings.xml配置讓Maven檢查其餘groupId上的插件倉庫元數據

    <settings>
    <pluginGroups>
    <pluginGroup>com.my.plugins</pluginGroup>
    </pluginGroups>
    </settings>

  • 在倉庫的元數據文件中能夠看到插件的前綴定義

    <metadata>
    <plugins>
    <plugin>
    <name>Maven Clean Plugin</name>
    <prefix>clean</prefix>
    <artifact>maven-clean-plugin</artifact>
    </plugin>
    <plugin>
    <name>Maven Compiler Plugin</name>
    <prefix>compile</prefix>
    <artifact>maven-compile-plugin</artifact>
    </plugin>
    <plugin>
    <name>Maven Dependency Plugin</name>
    <prefix>dependency</prefix>
    <artifact>maven-dependency-plugin</artifact>
    </plugin>
    </plugins>
    </metadata>

六.聚合與繼承

  • Maven的集合特性可以把項目各個模塊聚合在一塊兒構建,而Maven的繼承特性則能幫助抽泣各模塊相同的依賴和插件等配置,在簡化POM的同時,還能促進各個模塊配置的一致性

6.1 集合

  • aggregator

    <modelVersion>4.0.0</modelVersion>
    <groupId>com.lsy.project</groupId>
    <artifact>project-aggregator</artifact>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>pom</packaging>
    <name>Aggregator</name>

    <modules>
    <module>project-email</module>
    <module>project-register</module>
    </modules>

    • 對於聚合模塊,其打包方式packaging的值必須爲pom
    • 經過modules元素來實現聚合,用戶能夠經過在一個打包方式爲pom的Maven項目中聲明任意數量的module元素來實現模塊的聚合,這裏每一個module的值都是一個當前POM的相對目錄

      • 如aggregator的POM路徑爲.../project-aggregator/pom.xml,那麼project-email對應 的目錄爲.../project-aggregator/project-email/,而且目錄中包含了pom.xml,src/main/java,src/test/java等內容,離開了project-aggregator也能獨立建立=
    • 聚合模塊和其餘模塊的目錄結構並不是必定要父子關係,也能夠是平行關係

      <modules>
      <module>../prject-email</module>
      <module>../project-register</module>
      </modules>

    • 當在聚合模塊中執行mvn clean install時,Maven首先解析聚合模塊的POM,分析要構件的模塊,並計算出一個反應堆構件順序(Reactor Build Order),而後根據這個順序構件各個模塊
    • 聚合模塊的構件順序

      • Maven按序讀取POM文件,若是POM沒有依賴模塊,那麼就構件模塊,不然就先構件其依賴模塊

6.2 繼承

  • 從以上的聚合模塊和其餘模塊中能夠看到,多個被管理模塊的POM文件中會有大量重複的相同配置,他們有相同的groupId和version,相同的依賴,相同的插件配置,而經過POM文案的繼承能夠消除重複

    • project-parent父模塊

      <modelVersion>4.0.0</modelVersion>
      <groupId>com.lsy.project</groupId>
      <artifactId>project-parent</artifactId>
      <version>1.0.0-SNAPSHOT</version>
      <packaging>pom</packaging>
      <name>Parent</name>

      • 父模塊的POM文件使用與其餘模塊一直的groupId和version,它的packaging方式爲pom,與聚合模塊一致
      • 父模塊主要是爲了消除配置的重複,所以它自己不包含除POM文件以外的項目文件,也就不須要src/main/java之類的文件夾了
    • project-email子模塊

      <parent>
      <groupId>com.lsy.project</groupId>
      <artifactId>project-parent</artifactId>
      <version>1.0.0-SNAPSHOT</version>
      <relativePath>../project-parent/pom.xml</relativePath>
      </parent>

      <artifactId>project-email</artifactId>
      <name>Email</name>

      <dependencies>
      .....
      </dependencies>

      • 使用parent元素聲明父模塊,parent下子元素groupId,artifactId,version指定父模塊的座標,元素relativePath表示父模塊POM的相對路徑,表示Email模塊和其父模塊是在平行的目錄下

        • 當構件項目時,Maven會首先根據relativePath檢查父POM文件,若是找不到再從本地倉庫找,relativePath的默認值爲../pom.xml,也就是說,Maven默認父POM在上一層目錄
      • 子模塊沒有groupId和version,是由於子模塊隱式的從父模塊繼承了這兩個元素,從而消除了沒必要要的配置
  • 可繼承的POM元素

    • groupId:項目組ID,項目座標的核心元素
    • version:項目版本,項目座標的核心元素
    • description:項目的描述信息
    • organization:項目的組織信息
    • inceptionYear:項目的創始年份
    • url:項目的URL地址
    • develops:項目的開發者信息
    • contributors:項目貢獻者信息
    • distributionManagement:項目的部署配置
    • issueManagement:項目的缺陷跟蹤系統信息
    • ciManagement:項目的持續集成系統信息
    • scm:項目的版本控制系統信息
    • mailingList:項目的郵件列表信息
    • properties:自定義屬性
    • dependencies:項目的依賴配置
    • dependencyManagement:項目的依賴管理配置
    • repositories:項目的倉庫配置
    • pluginRepositories:項目的插件倉庫配置
    • build:包括項目的源碼目錄配置,輸出目錄配置,插件配置,插件管理配置等
    • reporting:項目的輸出目錄配置,報告插件配置等
  • 依賴管理

    • dependencies元素是能夠被繼承的,說明依賴是會被繼承的,因此咱們能夠將子模塊共有的依賴配置到父模塊中,子模塊就能夠移除這些依賴,簡化配置
    • 上述方法是可行的,可是可能未來會有新的模塊並不須要父模塊中的一些依賴,這就會產生不合理的現象,從而Maven提供了dependencyManagement元素,既能讓子模塊繼承到父模塊的依賴配置,又能保證子模塊依賴使用的靈活性

      • 在dependencyManagement元素下的依賴聲明不會引入實際的依賴,不過它可以約束dependencies下的依賴使用
<modelVersion\>4.0.0</modelVersion\>  
    <groupId\>com.lsy.project</groupId\>  
    <artifactId\>project-parent</artifactId\>  
    <version\>1.0.0-SNAPSHOT</version\>  
    <packaging\>pom</packaging\>  
    <name\>Parent</name\>  
    ​  
    <properties\>  
     <springframework.version\>4.3.1</springframework.version\>  
    </properties\>  
    ​  
    <dependencyManagement\>  
     <dependencies\>  
     <dependency\>  
     <groupId\>org.springframework</groupId\>  
     <artifactId\>spring-core</artifactId\>  
     <version\>${springframwork.version}</version\>  
     </dependency\>  
     <dependency\>  
     <groupId\>org.springframework</groupId\>  
     <artifactId\>spring-beans</artifactId\>  
     <version\>${springframwork.version}</version\>  
     </dependency\>  
     <dependency\>  
     <groupId\>org.springframework</groupId\>  
     <artifactId\>spring-context</artifactId\>  
     <version\>${springframwork.version}</version\>  
     </dependency\>  
     </dependencies\>  
    </dependencyManagement\>
    
    *   這裏使用dependencyManagement聲明的依賴既不會給parent模塊引入依賴,也不會給子模塊引入依賴,不過這段配置是會被繼承的
        
    *   子模塊POM
        
          
        <parent\>  
         <groupId\>com.lsy.project</groupId\>  
         <artifactId\>project-parent</artifactId\>  
         <version\>1.0.0-SNAPSHOT</version\>  
         <relativePath\>../project-parent/pom.xml</relativePath\>  
        </parent\>  
        ​  
        <artifactId\>project-email</artifactId\>  
        <name\>Email</name\>  
        ​  
         <dependencies\>  
         <dependency\>  
         <groupId\>org.springframework</groupId\>  
         <artifactId\>spring-core</artifactId\>  
         </dependency\>  
         <dependency\>  
         <groupId\>org.springframework</groupId\>  
         <artifactId\>spring-beans</artifactId\>  
         </dependency\>  
         <dependency\>  
         <groupId\>org.springframework</groupId\>  
         <artifactId\>spring-context</artifactId\>  
         </dependency\>  
         </dependencies\>
        
        *   使用這種依賴管理機制,能夠在父POM中使用dependencyManagement聲明依賴可以統一項目規範中的依賴版本,當版本在父POM中聲明以後,子模塊使用依賴時就不須要聲明瞭.也不會發生多個子模塊使用依賴版本不一致的狀況
            
            *   scoper元素的import依賴範圍只在dependencyManagement元素下才有效果,使用該範圍的依賴一般指向一個POM文件,做用是將POM中的dependencyManagement配置導入併合併到當前POM的dependencyManagement元素中
                
            *   因此除了複製配置和繼承父模塊這兩種方式外,還能夠經過import範圍依賴導入這一配置
                
            
              
            <dependencyManagement\>  
             <dependencies\>  
             <dependency\>com.lsy.project</dependency\>  
             <artifactId\>project-parent</artifactId\>  
             <version\>1.0-SNAPSHOT</version\>  
             <type\>pom</type\>  
             <scope\>import</scope\>  
             </dependencies\>  
            </dependencyManagement\>
  • 插件管理

    • Maven提供了dependencyManagement元素幫助管理依賴,相似的,Maven也提供了pluginManagement元素管理插件,該元素中配置的依賴一樣不會形成實際的插件調用行爲,而POM文件中配置了真正的plugin元素,而且groupId和artifact一致時,纔會產生實際的插件行爲
    • 父模塊

      <build>
      <pluginManagement>
      <plugins>
      <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-source-plugin</artifactId>
      <version>3.1.1</version>
      <executions>
      <execution>
      <id>attach-source</id>
      <phase>verify</phase>
      <goals>
      <goal>jar-no-fork</goal>
      </goals>
      </execution>
      </executions>
      </plugin>
      </plugins>
      </pluginManagement>
      </build>

    • 子模塊

      <build>
      <plugins>
      <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifact>maven-source-plugin</artifact>
      </plugin>
      </plugins>
      </build>

      • 子模塊使用了maven-source-plugin插件,同時又繼承了父模塊的pluginManagement配置
  • 集合與繼承的關係

    • 多模塊的聚合與繼承實際上是兩個概念,其目的是徹底不一樣的,前者爲了方便快速的構件項目,後者主要爲了消除重複配置

      • 對於聚合模塊來講:它知道有哪些模塊被聚合,可是那些被聚合的模塊並不知道這個聚合模塊的存在
      • 對於繼承關係的父POM來講:它不知道有哪些子模塊繼承於它,可是那些子模塊都必須知道本身的父模塊是什麼
    • 在實際項目中,一個POM每每便是聚合模塊,又是父模塊
  • 約定優於配置

    • Maven提倡"約定優於配置",Maven只須要一個簡單的POM文件,就能夠完成清除,構件等任務

      • 源碼目錄:src/main/java/
      • 編譯輸出目錄爲:target/classes/
      • 打包方式爲:jar
      • 包輸出目錄爲:target/
    • Maven此機制的來源就是超級POM文件,此文件在$MAVEN_HOME/lib/maven-model-builder-x.x.x.jar中的org/apache/maven/model/pom-4.0.0.xml路徑下

      <repositories>
      <repository>
      <id>central</id>
      <name>Maven Repository Swithboard</name>
      <url>http://repo1.maven.org/maven2&lt;/url>
      <layout>default</layout>
      <snapshots>
      <enabled>false</enabled>
      </snapshots>
      </repository>
      </repositories>


      <pluginRepositories>
      <pluginRepository>
      <id>central</id>
      <name>Maven Plugin Repository</name>
      <url>http://repo1.maven.org/maven2&lt;/url>
      <layout>default</layout>
      <snapshots>
      <enabled>false</enabled>
      </snapshots>
      <releases>
      <updatePolicy>never</updatePolicy>
      </releases>
      </pluginRepository>
      </pluginRepositories>


      <build>
      <!--> 定義了項目的主輸出目錄 </!-->
      <directory>${project.basedir}/target</directory>
      <!--> 主代碼輸出目錄 </!-->
      <outputDirectory>${project.build.directory}/classes</outputDirectory>
      <!--> 最終構件的名稱格式 </!-->
      <finalName>${project.artifactId}-${project.version}</finalName>
      <!--> 測試代碼輸出目錄 </!-->
      <testOutputDirectory>${project.build.directory}/test-
      classes</testOutputDirectory>
      <!--> 主源碼目錄 </!-->
      <sourceDirectory>${project.basedir}/src/main/java</sourceDirectory>
      <!--> 腳本源碼目錄 </!-->
      <scriptSourceDirectory>src/main/scripts</scriptSourceDirectory>
      <!--> 測試源碼目錄 </!-->
      <testSourceDirectory>${project.basedir}/src/test/java</testSourceDirectory>
      <!--> 主資源目錄 </!-->
      <resources>
      <resource>
      <directory>${project.basedir}/src/main/resources</directory>
      </resource>
      </resources>
      <!-- >測試資源目錄 </!-->
      <testResources>
      <testResource>
      <directory>${project.basedir}/src/test/resources</directory>
      </testResource>
      </testResources>

      <!--> 核心插件版本 </!-->
      <pluginManagement>
      <plugins>
      <plugin>
      <artifactId>maven-antrun-plugin</artifactId>
      <version>1.3</version>
      </plugin>
      <plugin>
      <artifactId>maven-assembly-plugin</artifactId>
      <version>2.3</version>
      </plugin>
      <plugin>
      <artifactId>maven-clean-plugin</artifactId>
      <version>2.3</version>
      </plugin>
      <plugin>
      <artifactId>maven-compiler-plugin</artifactId>
      <version>2.0.3</version>
      </plugin>
      ......
      </plugins>
      </pluginManagement>
      </build>

七. Nexus私服

7.1 Nexus內置的倉庫

  • Nexus倉庫有四種類型

    1. group:倉庫組
    2. hosted:宿主倉庫
    3. proxy:代理倉庫
    4. virtual:虛擬倉庫

image.png

*   Maven能夠從宿主倉庫下載構件,Maven也能夠從代理倉庫下載構件,而代理倉庫會簡介的從遠程倉庫下載並緩存構件
    
*   而通常爲了方便,咱們會從倉庫組下載構件,而倉庫組沒有實際的內容,它只是管理一組實際的倉庫,當它接收到請求時,它會轉向其包含的宿主倉庫或代理倉庫得到實際構件的內容
  • 倉庫的Policy屬性

    • Release:發佈版
    • Snapshot:快照版

7.2 配置Maven從Nexus下載構件

  • 在POM文件中配置倉庫和插件倉庫------只對當前項目有效

    <project>
    .......
    <repositories>
    <repository>
    <id>nexus</id>
    <name>Nexus</name>
    <url>http://localhost:8081/nexus/content/groupId/public/</url>
    <snapshots>
    <enabled>true</enabled>
    </snapshots>
    </repository>
    </repositories>
    <pluginRepositories>
    <pluginRepository>
    <id>nexus</id>
    <Name>Nexus</Name>
    <url>http://localhost:8081/nexus/content/groupId/public/</url>
    <release>
    <enabled>true</enabled>
    </release>
    <snapshots>
    <enabled>true</enabled>
    </snapshots>
    </pluginRepository>
    </pluginRepositories>
    .......
    </project>

  • 在settings.xml文件中配置---全局有效

    • settings.xml文件並不支持直接配置repositories和pluginRepositories,可是能夠經過Maven提供的Profile機制,讓用戶將倉庫配置到settings.xml中的Profile中
<settings\>  
 ....  
 <profiles\>  
 <profile\>  
 <id\>nexus</id\>  
 <repositories\>  
 <repository\>  
 <id\>nexus</id\>  
 <name\>Nexus</name\>  
 <url\>http://localhost:8081/nexus/content/groupId/public/</url\>  
 <release\>  
 <enabled\>true</enabled\>  
 </release\>  
 <snapshots\>  
 <enabled\>ture</enabled\>  
 </snapshots\>  
 </repository\>  
 </repositories\>  
   
 <pluginRepositories\>  
 <pluginRepository\>  
 <id\>nexus</id\>  
 <Name\>Nexus</Name\>  
 <url\>http://localhost:8081/nexus/content/groupId/public/</url\>  
 <release\>  
 <enabled\>true</enabled\>  
 </release\>  
 <snapshots\>  
 <enabled\>true</enabled\>  
 </snapshots\>  
 </pluginRepository\>  
 </pluginRepositories\>  
 </profile\>  
 </profiles\>  
   
 <!--> 激活指定id的profile </!-->  
 <activeProfiles\>  
 <activeProfile\>nexus</activeProfile\>  
 </activeProfiles\>  
</settings\>
  • 以上配置已經能讓Maven項目從Nexus私服下載構件了,可是Maven仍然會去訪問中央倉庫,如今咱們想要將全部請求都僅僅經過私服,這就須要藉助於Maven鏡像了

    <settings>
    .....
    <mirrors>
    <mirror>
    <id>nexus</id>
    <mirrorOf>*</mirrorOf>
    <url>http://localhost:8081/nexus/content/groupId/public/</url>
    </mirror>
    </mirrors>

    <profiles>
    <profile>
    <id>nexus</id>
    <repositories>
    <repository>
    <id>central</id>
    <name>central</name>
    <url>http://central&lt;/url>
    <release>
    <enabled>true</enabled>
    </release>
    <snapshots>
    <enabled>ture</enabled>
    </snapshots>
    </repository>
    </repositories>

    <pluginRepositories>
    <pluginRepository>
    <id>central</id>
    <Name>Nexus</Name>
    <url>http://central&lt;/url>
    <release>
    <enabled>true</enabled>
    </release>
    <snapshots>
    <enabled>true</enabled>
    </snapshots>
    </pluginRepository>
    </pluginRepositories>
    </profile>
    </profiles>

    <!--> 激活指定id的profile </!-->
    <activeProfiles>
    <activeProfile>nexus</activeProfile>
    </activeProfiles>
    </settings>

    • 這裏倉庫和插件倉庫配置的id都爲central,它們將會覆蓋POM中央倉庫的配置,而且這裏的url已經可有可無,由於全部的請求都會經過鏡像訪問私服地址

7.3 部署構件至Nexus

  • 若是隻是爲了代理外部公共倉庫,那麼Nexus的代理倉庫就已經徹底足夠了,對於另外一類Nexus倉庫---宿主倉庫來講,他們主要做用是存儲組織內部的,或一些沒法從公共倉庫得到的第三方構件,用戶能夠經過配置Maven自動部署構件至Nexus的宿主倉庫

    • 使用Maven部署構件至Nexus

      • 平常開發生成的快照版本構件能夠直接部署到Nexus中策略爲Snapshot的宿主倉庫中,項目正式發佈的構件則應該部署到Nexus中策略爲Release的宿主倉庫中
      • POM文件配置

        <project>
        .....
        <distributeManagement>
        <repository>
        <id>nexus-releases</id>
        <name>Nexus Release Repository</name>
        <url>http://localhost:8081/nexus/content/repositories/
        release/</url>
        </repository>
        <snapshotRepository>
        <id>nexus-snapshots</id>
        <name>Nexus Snapshots Repository</name>
        <url>http://localhost:8081/nexus/content/repositories/
        snapshots</url>
        </snapshotRepository>
        </distributeManagement>
        .....
        </project>

      • Nexus的倉庫對於匿名用戶只是可讀的,爲了可以部署構件,還須要在settings.xml中配置認證信息

        <settings>
        ....
        <servers>
        <server>
        <id>nexus-releases</id>
        <username>admin</username>
        <password>admin123</password>
        </server>
        <server>
        <id>nexus-snapshots</id>
        <username>admin</username>
        <password>admin123</password>
        </server>
        </servers>
        ....
        </settings>

八. Profile

  • 一個優秀的構件系統必須足夠靈活,它應該可以讓項目在不一樣的環境下都可以成功的構件,例如:開發環境,測試環境和產品環境,這些環境的數據庫配置不盡相同,那麼項目構建時就須要可以識別其所在的環境並正確使用配置

8.1 屬性

<properties>
<springframework.version>4.3.1</springframework.version>
</properties>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${springframwork.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${springframwork.version}</version>
</dependency>
</dependencies>
</dependencyManagement>

  • 這是最多見的Maven屬性使用的方式,經過properties元素使得用戶自定義一個或多個Maven屬性,而後在POM的其餘地方使用${屬性名稱}的方式來引用該屬性,這種作法的最大意義是消除重複
  • Maven的屬性有六類

    1. 內置屬性

      1. ${basedir}:表示項目根目錄,即包含pom.xml文件的目錄
      2. ${version}:表示項目的版本
    2. POM屬性:用戶可使用該類屬性引用POM文件中對應元素的值

      • ${project.artifactId}:對應<project><artifactId>元素的值

        • ${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/test-classes
        • ${project.build.groupId}:項目的groupId
        • ${project.build.artifactId}:項目的artifactId
        • ${project.build.build.finalName}:項目打包輸出文件的名稱,默認爲${project.artifactId}

          -${project.version}

    3. 自定義屬性:<properties>元素下自定義的Maven屬性
    4. settings屬性:與POM屬性同理,用戶使用settings.來引用,如:${settings.localRepository}指向用戶本地倉庫的地址
    5. Java系統屬性:全部Java系統屬性均可以使用Maven屬性引用,如${user.home}指向用戶目錄

      • 能夠經過mvn help:system查看全部的java系統屬性
    6. 環境變量屬性:全部環境變量均可以用env.來引用,例如:${env.JAVA_HOME}

      • 能夠經過 mvn help:system查看全部的環境變量

8.2 資源過濾

  • 在不一樣的開發環境中,項目的源碼應該使用不一樣的方式進行構件,最多見的就是數據庫的配置了,在開發中,有些項目會在src/main/resources/目錄下放置不一樣環境下的數據庫配置

    database.jdbc.driverClass = com.mysql.jdbc.Driver
    database.jdbc.connectionURL = jdbc:mysql://localhost:3306/dev
    database.jdbc.username = dev
    database.jdbc.password = dev-passwd

    database.jdbc.driverClass = com.mysql.jdbc.Driver
    database.jdbc.connectionURL = jdbc:mysql://localhost:3306/test
    database.jdbc.username = test
    database.jdbc.password = test-passwd

  • 爲了應對環境的變化,咱們可使用Maven屬性將這些會發生變化的部分提取出來

    database.jdbc.driverClass = ${db.driver}
    database.jdbc.connectionURL = ${db.url}
    database.jdbc.username = ${db.username}
    database.jdbc.password = ${db.password}

  • 在settings.xml中經過profile定義不一樣環境下的配置數據

    <profiles>
    <profile>
    <id>dev</id>
    <properties>
    <db.driver>com.mysql.jdbc.Driver</db.driver>
    <db.url>jdbc:mysql://localhost:3306/dev</db.url>
    <db.username>dev</db.username>
    <db.password>dev-passwd</db.password>
    </properties>
    </profile>

    <profile>
    <id>test</id>
    <properties>
    <db.driver>com.mysql.jdbc.Driver</db.driver>
    <db.url>jdbc:mysql://localhost:3306/test</db.url>
    <db.username>test</db.username>
    <db.password>test-passwd</db.password>
    </properties>
    </profile>
    </profiles>

    • 須要注意的是:

      • Maven屬性默認只會在POM文件中才會被解析,也就是說${db.username}放到POM文件中會變成dev或test,可是在src/main/resources/目錄下仍然仍是${db.username},所以,須要讓Maven解析資源文件中的Maven屬性
      • 資源文件處理的是maven-reosurces-plugin作事,它默認的行爲只是將項目主資源文件複製到主代碼編譯輸出目錄
  • 開啓資源過濾

    • Maven默認的主資源目錄和測試目錄的定義是在超級POM文件中
<resources\>  
 <resource\>  
 <directory\>${project.basedir}/src/main/resources</directory\>  
 <filtering\>true</filtering\>  
 </resource\>  
</resources\>  
<testResources\>  
 <testResource\>  
 <directory\>${project.basedir}/src/test/resources</directory\>  
 <filtering\>true</filtering\>  
 </testResource\>  
</testResources\>
  • 經過mvn clean install -Pdev : 經過mvn 的 -P參數表示在命令行激活一個profile=dev的profile

8.3 Profile

  • 激活Pofile

    1. 命令行激活

      • 用戶可使用mvn命令行參數-P 加上profile的id來激活profile,多個id之間逗號分隔

        • mvn clean install -Pdev,-Ptest
    2. settings文件顯示激活

      • 若是用戶但願某個profile默認一直處於激活狀態,就能夠配置settings.xml文件的activeProfile元素,表示其配置的profile對全部項目都處於激活狀態

        <settings>
        .....
        <activeProfiles>
        <activeProfile>dev</activeProfile>
        </activeProfiles>
        .....
        </settings>

    3. 系統屬性激活

      • 用戶能夠配置當前某系統屬性存在時,自動激活profile

        <profiles>
        <profile>
        <activation>
        <property>
        <name>test</name>
        </property>
        </activation>
        .....
        </profile>
        </profiles>

      • 用戶能夠配置當前某系統屬性存在且等於a時,自動激活profile

        <profiles>
        <profile>
        <activation>
        <property>
        <name>test</name>
        <value>a</value>
        </property>
        </activation>
        .....
        </profile>
        </profiles>

      • 用戶能夠在命令行聲明系統屬性

        • mvn clean install -Dtest=x
    4. 操做系統環境變量激活

      • Profile還能夠根據操做系統環境激活

        <profiles>
        <profile>
        <activation>
        <os>
        <name>Windows10</name>
        </os>
        </activation>
        .....
        </profile>
        </profiles>

    5. 文件存在與否激活

      • Maven能夠根據項目中某個文件是否存在來決定是否激活profile

        <profiles>
        <profile>
        <activation>
        <file>
        <missing>a.properties</missing>
        <exists>b.properties</exists>
        </file>
        </activation>
        .....
        </profile>
        </profiles>

    6. 默認激活

      • 用戶能夠在定義profile的時候指定其默認激活

        <profiles>
        <profile>
        <id>dev</id>
        <activation>
        <activeByDefault>true</activeByDefault>
        </activation>
        .....
        </profile>
        </profiles>

  • 以上激活優先級從上之下依次減少

    • 能夠經過mvn help:active-profiles來查看當前激活的profile
    • 能夠經過mvn help:all-profiles來查看全部的profile
  • profile的種類

    • 根據具體的須要,能夠在一下位置聲明profile

      Maven

一.Maven簡介

1.1 何爲maven

Maven可翻譯爲"知識的積累" or"專家",是一款成功的開源跨平臺的項目管理工具,不管小型的開源類庫項目,仍是大型的企業級應用;不管傳統的瀑布式開發,仍是流行的敏捷模式,Maven都能大顯身手.

1.1.1 構建工具

​ 咱們一直在不停的尋找避免重複的方法,設計的重複,編碼的重複,文檔的重複,固然還有構建的重複.Maven最大化的消除了構建的重複,抽象了構建生命週期,而且爲絕大部分的構建任務提供了已實現的插件,咱們不須要再定義過程,甚至不須要去實現這些過程當中的一些任務,只須要遵循Maven中的約定.

​ 同時Maven幫助咱們標準化構建過程,之前十個項目可能有十種構建方式,有了Maven後,全部項目的構建命令都是一直且簡單的.所以Maven做爲一個構建工具:

  1. 能夠幫咱們自動化構建,
  2. 能夠幫咱們抽象構建過程
  3. 提供構建任務是實現
  4. 跨平臺
  5. 對外提供一直的操做接口

1.1.2 不只僅是構建工具

​ Maven不只是構建工具,仍是一個依賴管理工具和項目信息管理工具,提供中央倉庫來幫忙咱們自動下載構建,經過引入一套經緯機制來系統準確的定位每個構建(artifact).

1.1.3 Maven

​ 在Maven以前,有過程式的Make和Ant,開發者須要顯示的指定每個目標,以及完成該目標所須要執行的任務.針對每個項目,開發者都須要從新編寫這一過程,而其中就隱含着大量重複.

​ 而Maven是聲明式的,項目構建過程和過程各個階段所需的工做都由插件實現,而且大部分插件都是現成的,開發者只須要聲明項目的基本元素,Maven就執行內置的,完整的構建過程.

二.Maven的使用

2.1 pom文件

​ Maven項目的核心是pom.xml,POM(Project Object Model,項目對象模型)定義了項目的基本信息,用於描述項目如何構建,聲明項目依賴等.

<modelVersion>4.0.0</modelVersion>:modelVersion指定了當前Pom模型的版本,固定爲4.0.0

<groupId>com.lsy</groupId>:groupId定義了項目屬於哪一個組,這個組每每和項目所在的組織和公司相關

<artifactId>hello-world</artifactId>:artifactId定義了當前Maven項目在組中惟一的ID

<version>1.0-SNAPSHOT</version>:version指定了Hello World項目當前的版本,其中SNAPSHOT意爲快照,說明該項目還處於開發中

<name>Maven Hello World Project</name>:name元素聲明瞭一個對於用戶更爲友好的項目名稱.非必須

​ 當運行mvn clean compile命令:clean告訴Maven清理輸出目錄target/,compile告訴Maven編譯項目主代碼,從輸出中看到Maven首先執行clean:clean任務,刪除target/目錄(默認狀況下,Maven構建的全部輸出都在target目錄中,接着執行resources:resources任務(未定義項目資源),最後執行compiler:compile任務,將項目主代碼編譯至target/classes目錄

​ 其中clean:clean,resources:resources和compiler:compile對應了Maven插件及插件目標,好比clean:clean是clean插件的clean目標,compiler:compile是compiler插件的compile目標

2.2 測試

​ 首先添加Junit依賴

<dependencies>

​ <dependency>

​ <groupId>junit</groupId>

​ <artifactId>junit</artifactId>

​ <version>4.7</version>

​ <scope>test</scope>

​ </dependency>

<dependencies>

​ 其中scope元素爲依賴範圍,若依賴範圍爲test則表示該依賴只對測試有效,若是不聲明依賴範圍,則默認是compile,表示該依賴對主代碼和測試代碼均有效

當運行mvn clean test命令,Maven實際執行的不止test任務,而是clean:clean,resources:resources,compiler:compile,resources:testResources以及compiler:testCompile,即在執行測試以前,會先自動執行項目資源處理,主代碼編譯,測試資源處理,測試代碼編譯等工做,這是Maven生命週期的一個特性.

​ 因爲Maven核心插件之一compiler插件默認支持Java1.3,所以須要配置插件支持Java1.8

<build>

​ <plugins>

​ <plugin>

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

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

​ <configuration>

​ <source>1.8<source>

​ <target>1.8</target>

​ </configuration>

​ </plugin>

​ </plugins>

</build>

2.3 打包和運行

​ 將項目進行編譯,測試以後,下一個重要的步驟就是打包(package),當沒有指定打包類型時,默認打包類型是jar,當執行mvn clean package命令,Maven會在打包以前進行編譯,測試等操做,以後經過jar:jar任務負責打包,實際上就是jar插件的jar目標將項目主代碼打包成一個hello-world-1.0-SNAPSHOT.jar的文件

​ 當其餘項目須要直接引用這個jar時,接下來須要一個安裝的步驟,執行mvn clean install,此命令執行了安裝任務install:install,將項目輸出的jar安裝到了Maven本地倉庫中,而構件只有被下載到本地倉庫後,才能由Maven項目使用.

2.4 使用Archetype生成項目骨架

​ mvn archetype:generate,當運行插件時,格式爲:groupId:artifactId:version:goal,默認使用最新的穩定版

三. 座標和依賴

3.1 座標

  • Maven的一大功能仍是管理項目依賴,爲了能自動化解析任何一個Java構件,Maven就必須將它們惟一標識,這就依賴管理的底層基礎---座標
  • 在實際生活中,咱們能夠將地址看做是一種座標,省,市,區,街道等一系列信息能夠惟一標識城市中的任意居住地址,對應在Maven的世界中擁有很是巨大的構件,也就是日常使用的一些jar,war等文件
  • Maven定義了這樣一規則:世界上任意一個構建均可以使用Maven座標惟一標識,Maven座標的元素包括

    • groupId:定義當前Maven項目隸屬公司的實際項目
    • artifactId:該元素定義實際項目中的一個Maven模塊,推薦作法使用實際項目名稱爲artifact的前綴,便於尋找實際構建
    • version:該元素定義Maven項目當前所處的版本
    • package:該元素定義Maven項目的打包方式,默認爲jar包
    • classifier:該元素用來幫助定義構建輸出一些附屬構建,附屬構件與主構件對應
    <groupId>org.sonatype.nexus</groupId>
    <artifactId>nexus-indexer</artifactId>
    <version>2.0.0</version>
    <packaging>jar</packaging>
    
    <!--> 
    1. 以上5個元素,groupId,artifactId,version是必須定義的,packaging是可選的,而classifier是不能直接定義的
    2. 項目構件的文件名格式:artifactId-version[-classifier].packaging

3.2 依賴

3.2.1 依賴的配置

  • 項目要引用Maven中的構建,就須要在pom文件中,經過座標來使用依賴,在根元素project下的dependencies能夠包含一個或多個dependency元素,用以聲明一個或多個項目依賴

    • groupId,artifactId和version:依賴的基本座標,對於任何一個依賴來講,基本座標是最重要的
    • type:依賴的類型,對應項目座標定義的packaging,通常沒必要聲明,默認爲jar
    • scope:依賴的範圍
    • optional:標記依賴是否可選
    • exclusions:用來排除傳遞性依賴

3.2.2 依賴的範圍

  • Maven項目在編譯項目主代碼時須要使用一套classpath,如編譯項目主代碼時須要使用spring-core,該文件以依賴的方式被引入到classpath中.其次,Maven在編譯和執行測試的時候會使用另一套classpath,依賴一樣會引入到相應的classpath中,最後在運行Maven項目時,又會使用一套classpath
  • 依賴範圍就是用來控制依賴與三種classpath(編譯classpath,測試classpath,運行classpath)的關係

    • compile:編譯依賴範圍,沒有指定時,爲默認依賴範圍.使用此依賴範圍的Maven依賴,對於編譯,測試運行三種classpath都有效,典型的例子爲:spring-core,在編譯,測試,運行階段都須要使用該依賴
    • test:測試依賴範圍,使用此依賴範圍的Maven依賴,只對於測試的classpath有效,在編譯主代碼或者運行項目時將沒法使用此類依賴,典型的例子就是JUnit,它只有在編譯器測試代碼及運行測試的時候才須要
    • provided:已提供依賴範圍,使用此依賴範圍的Maven依賴,對於編譯和測試classpath有效,但在運行時無效,典型的例子就是servlet-api,編譯和測試項目的時候須要該依賴,但在運行項目的時候,因爲容器已經提供,就不須要Maven重複引入了
    • runtime:運行時依賴範圍,使用此依賴範圍的Maven依賴,對於測試和運行classpath有效,但在編譯主代碼時無效,典型的例子是JDBC驅動實現,項目主代碼的編譯只須要JDK提供的JDBC接口,只有在執行測試或運行項目的時候才須要實現上述接口的具體JDBC驅動
    • system:系統依賴範圍,該依賴與三種classpath的關係,和provided依賴範圍徹底一致,可是,使用system範圍的依賴時必須經過systemPath元素顯示地指定依賴文件的路徑,因爲此類依賴不是經過Maven倉庫解析的,並且每每與本機系統綁定,可能形成構建的不可移植性

      <dependency>
          <groupId>javax.sql</groupId>
          <artifactId>jdbc-stdext</artifactId>
          <version>2.0</version>
          <scope>system</scope>
          <systemPath>${java.home}/lib/rt.jar</systemPath>
      </dependency>
    • import:導入依賴範圍,該依賴範圍不會對三種classpath產生實際的影響,主要用於導入其餘pom文件中的dependencyManagement元素對於依賴版本約束的內容

3.2.3 傳遞性依賴

  • 在使用Maven依賴時,如Spring Framework,此依賴又會依賴其餘的開源庫,所以實際中每每會下載一個很大的如spring-framework-2.5.6-with-dependencies.zip包,這樣每每就引入了不少沒必要要的依賴,而Maven的傳遞性依賴機制就能夠很好的解決這一問題

    • 當A項目引入一個compile範圍的B依賴,而B依賴中有一個compile範圍的C依賴,那麼C依賴一樣會成爲A的compile範圍依賴

      1590662084736

  • 傳遞性依賴和依賴範圍

    • 假設A依賴於B,B依賴於C,那麼A對於B是第一直接依賴,B對於C是第二直接依賴,A對於C是傳遞性依賴
    • 當第二直接依賴是compile的時候,傳遞性依賴與第一直接依賴範圍一致
    • 當第二直接依賴是test的時候,依賴不會得以傳遞
    • 當第二直接依賴是provided的時候,只傳遞第一直接依賴範圍爲provided的依賴,且傳遞性依賴範圍爲provided
    • 當第二直接依賴是runtime的時候,傳遞性地依賴的範圍與第一直接依賴的範圍一致,但compile例外,此時傳遞性依賴的範圍爲runtime

1590662084736

3.2.4 可選依賴

  • 假若有這樣一個依賴關係,A依賴於B,B依賴於X和Y,B對於X和Y的依賴都是可選依賴,根據傳遞性依賴的定義,若是這三個依賴的範圍都是compile,那麼X,Y就是A的compile範圍傳遞性依賴,可是因爲X,Y都是可選依賴,因此依賴不會得以傳遞,所以X,Y不會對A有任何影響

    1590662084736

    • 爲何會有可選依賴這一特性呢?

      • 當B實現了兩個特性,特性一依賴於X,特性二依賴於Y,而且這兩個特性是互斥的,用戶不可能同時使用兩個特性,好比B是一個持久層隔離工具包,支持多種數據庫,在使用這個工具包的時候,只會依賴一種數據庫
<dependencies>
    <dependency>
        <groupId>mysql</groupId>
        <artifact>mysql-connector-java</artifact>
        <version>5.6.0</version>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>postgresql</groupId>
        <artifactpostgresql</artifact>
        <version>8.4-701.jdbc3</version>
        <optional>true</optional>
    </dependency>
</dependencies>
  • 以上使用<optional>元素表示兩個依賴爲可選依賴,他們只會對當前項目B產生影響,當其餘項目依賴於B時,這兩個依賴不會被傳遞,因此當A依賴於B項目時,若是要使用mysql數據庫,那麼須要顯式的聲明mysql-connector-java這一依賴
  • 關於可選依賴,在理想的狀況下,是不該該使用可選依賴的,使用可選依賴的緣由是某一個項目中實現了多個特性,而根據單一職責原則,應該針對不一樣的特性分別建立一個Maven項目,用戶根據須要選擇使用其中某一個依賴

3.2.5 排除依賴

  • 傳遞性依賴會給項目隱式的引入不少依賴,這極大的簡化了項目依賴的管理,可是有時候這種特性䧥帶來問題

    • 例如,當前項目有一個第三方依賴,而這個依賴因爲某些緣由依賴了另外一個類庫的SNAPSHOT,那麼整個SNAPSHOT就會成爲當前項目的傳遞性依賴,而SNAPSHOT的不穩定性會直接影響到當前的項目,這時候就須要排除掉該SNAPSHOT,而且在當前項目中聲明該類庫的某個正式發佈版

      <dependencies>
          <dependency>
              <groupId>com.lsy.myproject</groupId>
              <artifactId>myproject-a</artifactId>
              <version>1.0.0</version>
              <exclusions>
                  <exclusion>
                      <groupId>com.lsy.myproject</groupId>
                      <artifactId>myproject-b</artifactId>
                  </exclusion>
              </exclusions>
          </dependency>
          <dependency>
              <groupId>com.lsy.myproject</groupId>
              <artifactId>myproject-b</artifactId>
              <version>1.1.0</version>
          </dependency>
      </dependencies>

3.2.6 歸類依賴

  • 當一些依賴來自同一個項目的不一樣模塊,這些依賴的版本都應該是相同的,未來升級也是一塊兒升級,如Spring Framework,這時可使用properties元素定義Maven屬性

    <properties>
        <springframework.version>4.2.1</springframework.version>
    </properties>
    
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <verison>${springframework.version}</verison>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <verison>${springframework.version}</verison>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <verison>${springframework.version}</verison>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <verison>${springframework.version}</verison>
        </dependency>
    </dependencies>

3.2.7 優化依賴

  • 在軟件開發過程當中,一般會經過重構等方式不斷優化本身代碼,一樣,對於Maven項目的依賴也須要對其進行優化

    • 去除多餘的依賴
    • 顯示聲明某些必要的依賴
  • Maven會自動解析全部項目的直接依賴和間接依賴,而且根據規則判斷每一個依賴範圍,對於一些依賴衝突,也能進行調整,以確保任何一個構建只有惟一的版本在依賴中存在,此稱爲解析依賴

    • mvn dependency:list 查看當前項目的已解析依賴
    • mvn dependency:tree 以樹結構查看已解析依賴
    • mvn dependency:analyze 解析依賴

      • Used undeclared dependencies:指項目中使用到的,可是沒有顯示聲明的依賴,這種依賴意味着潛在的風險,當前項目直接在使用它們,因此須要顯示聲明任何項目中直接用到的依賴
      • Unused declared dependencies:指項目中未使使用的.但顯示聲明的依賴

四. 倉庫

  • 以前已經介紹了Maven的座標和依賴,座標和依賴是任何一個構件在Maven世界中的邏輯表示方式,而構建的物理表示方式是文件,Maven經過倉庫來同一管理這些文件
  • 在Maven世界中,任何一個以依賴,插件或項目構建的輸出,均可以成爲構件

    • 例如:log4j-1.2.15.jar,maven-compiler-plugin-2.0.2.jar或項目打包後的myproject-1.0.0-SNAPSHOT.jar
  • 在一臺工做站上,可能會有幾十個項目,全部項目在/lib目錄下都會有本身所需的依賴包,而這些依賴中都有大量的重複,每一個項目各自存儲本身所需的依賴包不只形成磁盤空間的浪費,並且也難於同一管理,文件的複製等操做
  • 得益於座標機制,任何Maven項目使用任何一個構建的方式都是相同的,所以,Maven能夠在某個位置統一存儲全部Maven項目共享的構件,這個統一的位置就是倉庫,爲了實現重用,項目構建完畢後生成的構建也能夠安裝或部署到倉庫中

4.1 倉庫的佈局

  • 任何一個構建都有其惟一的座標,根據這個座標能夠定義其在倉庫中的惟一存儲路徑,這就是Maven的倉庫佈局方式,如log4j:log4j:1.2.15這一依賴,其對應的倉庫路徑爲log4j/log4j/1.2.15/log4j-1.2.15.jar

    • 路徑與座標的關係爲:groupId/artifactId/version/artifactId-verision.packaging

4.2 倉庫的分類

  • 對於Maven來講,倉庫分爲兩類

    • 本地倉庫
    • 遠程倉庫

      • 中央倉庫:Maven核心自帶的倉庫服務器,它包含了絕大部分開源的構件
      • 私服:另外一種特殊的遠程倉庫,爲了節省寬帶和時間,應該在局域網內部構建一個私有倉庫服務器,用其代理全部外部的遠程倉庫,內部項目部署到私服上供其它項目使用
      • 其餘公共庫
  • 當Maven根據座標尋找構件時,它首先會查看本地倉庫,若是本地倉庫存在此構件,則直接使用,若是本地倉庫不存在此構件,或者須要查看是否有更新的構件版本,Maven就會去遠程倉庫查找,發現須要的構件後下載到本地倉庫再使用,若本地和遠程都沒有,則報錯

4.3 本地倉庫

  • 通常來講,在Maven項目目錄下,沒有注入lib/這樣用來存放依賴文件的目錄,當Maven執行編譯或測試時,若是須要使用依賴文件,它老是基於座標使用本地倉庫的依賴文件

    • 默認狀況,在用戶目錄下路徑名爲.m2/repository/的倉庫目錄,當想自定義本地倉庫目錄地址時,能夠編輯~/.m2/setting.xml,設置localRepository元素來指定倉庫地址

      <settings>
          <localRepository>D:/java/repository/</localRepository>
      </settings>
    • 默認狀況下,~/.m2/settings.xml文件是不存在的,用戶須要從Maven安裝目錄複製$M2_HOME/conf/settings.xml文件再編輯,建議不要直接修改全局目錄的settings.xml,而是在用戶目錄下進行修改
  • 一個構建只有在本地倉庫中,才能由其它Maven項目使用

    1. 依賴Maven從遠程倉庫下載到本地倉庫中
    2. 將本地項目的構件安裝到Maven本地倉庫中 mvn clean install

4.4 遠程倉庫

  • 安裝好Maven後,若是不執行任何Maven命令,本地倉庫目錄是不存在的,只有輸入第一條Maven命令後,Maven纔會建立本地倉庫,並根據配置和須要,從遠程倉庫下載至本地倉庫

    • 這就比如藏書,本地倉庫比如書房,遠程倉庫比如書店,我須要讀書時先去書房找,當書房沒有時,就去書店買回放到書房,而且通常對每一個人來講,書房只有一個,而外面的書店能夠有多個

4.4.1 中央倉庫

  • 最原始的本地倉庫是空的,因此Maven必須知道至少一個可用的遠程倉庫,才能在執行Maven命令時下載到須要的構建,中央倉庫就是這樣一個默認的遠程倉庫,能夠經過解壓工具打開$M2_HOME/lib/maven-model-builder-3.0.jar中的org/apache/maven/model/pom-4.0.0.xml文件

    <repositories>
        <repository>
            <id>central</id>
            <name>Maven Repository Switchboard</name>
            <url>http://repo1.maven/org/maven2</url>
            <layout>default</layout>
            <snapshots>
                <enable>false</enable>
            </snapshots>
        </repository>
    </repositories>
    • 這段配置是全部Maven項目都會繼承的超級Pom文件,這段配置使用id central對中央倉庫進行惟一標識,其名稱爲Maven Repository Switchboard,它使用default倉庫佈局,也就是在以前介紹的倉庫佈局,最後snapshots元素表示不從該倉庫下載快照版

4.4.2 私服

  • 私服是一種特殊的遠程倉庫,它是架設在局域網內的倉庫服務,私服代理廣域網上的遠程倉庫,供局域網內的Maven用戶使用,當Maven須要下載構建的時候,它從私服請求,若是私服上不存在該構件,則從外部的遠程倉庫下載,緩存在私服上以後,再爲Maven的下載提供服務

    • 此外,一些沒法從外部下載的構件也能夠從本地上傳到私服上供你們使用
  • 私服的好處

    • 節省外網帶寬:創建私服一樣能夠減小組織本身的開支,大量的對於外部倉庫的重複請求會消耗很大的帶寬,利用私服代理外部倉庫以後,對外的重複構件下載即可以消除,即下降外網帶寬的壓力
    • 加速Maven構件:不停的鏈接請求外部倉庫是十分耗時的,但Maven的一些內部機制(如快照更新檢查井)要求Maven在執行構建時不停的檢查遠程倉庫數據,所以,當項目配置不少外部倉庫時,構建速度就會下降
    • 部署第三方構件:當某個構件沒法從任何一個外部遠程倉庫獲取,創建私服以後,即可以將這些構件部署到這個內部的倉庫中,供內部的Maven項目使用
    • 提升穩定性,加強控制:Maven構建高度依賴遠程倉庫,所以,大哥Internet不穩定的時候,Maven構建也會變得不穩定,甚至沒法構建,使用私服後,便是短暫時沒有Internet鏈接,因爲私服中有大量緩存,Maven依然能夠正常運行,而且私服中有不少額外的權限功能控制
    • 下降中央倉庫的負荷:天天中央倉庫都須要面對大量的下載請求,使用私庫能夠下降對於中央倉庫的負荷
  • 遠程倉庫的配置

    • 不少狀況下,默認的中央倉庫沒法知足項目的需求,可能項目須要的構件存在於另外一個遠程倉庫中,能夠經過Pom/settings文件中來配置該倉庫

      <!--> POM文件 </!-->
      <project>
          .....
          <repositories>
              <repository>
                  <id>jboss</id>
                  <name>JBoss Repository</name>
                  <url>http://repository.jboss.com/maven2/</url>
                  <release>
                      <enabled>true</enabled>
                  </release>
                  <snapshots>
                      <enabled>false</enabled>
                  </snapshots>
                  <layout>default</layout>
              </repository>
          </repositories>
      </project>
      
      <!--> settings文件 </!-->
      <profiles>
          <profile>
              <id>dev</id>
              <activation>
                  <activatedByDefault>true</activatedByDefault>
              </activation>
              <repositories>
              <repository>
                  <id>jboss</id>
                  <name>JBoss Repository</name>
                  <url>http://repository.jboss.com/maven2/</url>
                  <release>
                      <enabled>true</enabled>
                  </release>
                  <snapshots>
                      <enabled>false</enabled>
                  </snapshots>
                  <layout>default</layout>
              </repository>
          </repositories>
          </profile>
      </profiles>
      • 在repositories元素下,可使用repository子元素聲明一個或多個遠程倉庫,而且聲明一個id,任何一個倉庫聲明的id必須是惟一id
      • Maven自帶的中央倉庫使用的id爲central,若是其餘倉庫使用該id,則會覆蓋中央倉庫的配置,該配置中的url指向了倉庫的地址
      • 該配置中的release和snapshots元素用來控制Maven對於發佈版構件和快照版構件的下載,對於release和snapshots元素來講除了enabled子元素,還有updatePolicy和checksumPolicy元素

        • updatePolicy:用來配置Maven從遠程倉庫檢查更新的頻率,默認爲daily

          • daily:天天
          • never:從不
          • always:每次
          • interval :X :每隔X分鐘一次
        • checksumPolicy:配置Maven檢查文件失敗時的策略,默認爲warn

          • fail:Maven遇到驗證和錯誤就讓構建失敗
          • warn:Maven遇到驗證和錯誤就發出警告信息
          • ignore:Maven遇到驗證和錯誤時徹底忽略
<snapshots>
    <enabled>true</enabled>
    <updatePolicy>daily</updatePolicy>
    <checksumPolicy>ignore</checksumPolicy>
</snapshots>
  • 遠程倉庫的驗證

    • 大部分遠程倉庫無需認證就能夠訪問,但出於安全考慮,咱們須要提供一些認證信息才能訪問一些遠程倉庫
    • 配置認證信息和配置倉庫信息不一樣,倉庫信息能夠直接配置在POM文件中,可是認證信息必須配置在settings.xml文件中
    • 其中server元素的id必須與POM文件中須要認證的repository元素的id徹底一致

      <settings>
          ......
          <servers>
              <server>
                  <id>my-project</id>
                  <username>user</username>
                  <password>password</password>
              </server>
          </servers>
          .......
      </settings>
  • 部署至遠程倉庫

    • 私服一大做用就是部署第三方構件,包括組織內部生成的構件以及沒法從外部倉庫直接獲取的構件,不管是平常開發中生成的構件,仍是正式版本發佈的構件,都須要部署到倉庫中

      • distributionManagement包含repository和snapshotRepository子元素,前者表示發佈版構件的倉庫,後者表示快照版的倉庫
      • 往遠程倉庫部署構件時,每每須要認證,認證配置需在settings.xml中建立一個server元素,其id與倉庫的id匹配,不管從遠程倉庫下載構件,仍是部署構件至遠程倉庫,當須要認證時,配置方式都是同樣的

        <!--> 在POM文件中配置distributionManagement </!-->
        <project>
            .....
            <distributionManagement>
                <repository>
                    <id>jboss-release</id>
                    <name>JBoss Release Repository</name>
                    <url>http://192.168.1.99/content/repository/jboss-release</url>
                </repository>
                <snapshotRepository>
                    <id>jboss-snapshots</id>
                    <name>JBoss Snapshots Repository</name>
                    <url>http://192.168.1.99/content/repository/jboss-snapshots</url>
                </snapshotRepository>
            </distributionManagement>
            .....
        </project>

4.5 快照

  • 在Maven的世界中,任何一個項目或者構件都必須有本身的版本,版本的值多是1.0.0,1.3-alpha-4,2.0,2.1-SNAPSHOT或者2.1-20191214.221414-13,其中1.0.0,1.3-alpha-4和2.0是穩定的發佈版本,而2.1-SNAPSHOT和2.1-20091214.221414-13是不穩定的快照版本
  • 對於一個穩定的版本,若是倉庫中已經包含,那麼Maven就不會再去對照遠程倉庫進行更新,除非每次執行Maven命令前,清除本地倉庫中等待穩定版本,而對於一個正在迭代的項目,若是要實時更新版本的內容就須要頻繁的修改新的版本名稱,這樣是對版本號的濫用
  • 針對這種狀況,使用快照版時,Maven會自動爲構件打上時間戳,所以,Maven就能隨時找到倉庫中該構建最新版本的文件,一旦有新的更新,就會去同步到本地倉庫.當項目通過完善的測試後須要發佈的時候,再將快照版本更改成發佈版本
  • 快照版本只應該在組織內部的項目或模塊之間依賴使用,由於這時,組織對這些快照版本的依賴具備徹底的理解和控制權,項目不該該依賴任何組織外部的快照版本依賴,因爲快照版本的不穩定性,隨時可能發生變化,這樣的依賴會有潛在的危險

4.6 從倉庫解析依賴的機制

  • 當本地倉庫沒有依賴構件的時候,Maven會自動從遠程倉庫下載,當依賴版本爲快照版本時,Maven會自動找到最新的快照,這背後的依賴機制能夠歸納以下

    1. 當依賴的範圍時system的時候,Maven直接從本地文件系統解析構件
    2. 根據依賴座標計算倉庫路徑後,嘗試從本地倉庫尋找構件,若是發現相應的構件,則解析成功
    3. 在本地倉庫不存在相應構件的狀況下,若是依賴的版本時顯式的發佈版本構件,如1.2,2.1-beta-1等,則遍歷全部的遠程倉庫,發現後,下載並解析使用
    4. 若是依賴的版本時RELEASE或LATEST,則基於更新策略讀取全部遠程倉庫的元數據groupId/artifact/maven-metadata.xml,將其與本地倉庫的對應元數據合併後,計算出RELEASE或LATEST真實的值,而後基於這個真實的值檢查本地倉庫和遠程倉庫
    5. 若是依賴的版本是SNAPSHOT,則基於更新策略讀取全部遠程倉庫的元數據groupId/artifactId/maven-metadata.xml,將其與本地倉庫的對應數據合併後,獲得最新的快照版本的值,而後基於該值檢查本地倉庫,或者從遠程倉庫下載
    6. 若是最後解析獲得的構件版本是時間戳格式,如1.4.1-20191104.121455-8,則複製其時間戳格式的文件至非時間戳格式,如SNAPSHOT,並使用該非時間戳格式的構件

      <!-->  基於groupId和artifactId的maven-metadata.xml </!-->
      <?xml version="1.0" encoding="UTF-8"?>
      <metadata>
          <groupId>org.sonatype.nexus</groupId>
          <artifactId>nexus</artifactId>
          <versioning>
              <latest>1.4.2-SNAPSHOT</latest>
              <release>1.3.7</release>
              <versions>
                  <version>1.3.5</version>
                  <version>1.3.6</version>
                  <version>1.3.7</version>
                  <version>1.4.0-SNAPSHOT</version>
                  <version>1.4.1-SNAPSHOT</version>
                  <version>1.4.2-SNAPSHOT</version>
              </versions>
              <lastUpdated>20191214221133</lastUpdated>
          </versioning>
      </metadata>
  • 該XML文件列出了倉庫中存在的構件全部可用的版本,同時latest元素指向了這些版本中最新的那個版本1.4.2-SNAPSHOT,而release元素指向了這些版本中最新的發佈版本1.3.7,Maven經過合併多個遠程倉庫及本地倉庫的元數據,就能計算出基於全部倉庫的latest和release

    • 須要注意的是,在依賴聲明使用LATEST和RELEASE是不推薦的作法,由於Maven隨時可能解析到不一樣的構件,且Maven不會明確告訴用戶這樣的變化

4.7 鏡像

  • 若是倉庫X能夠提供倉庫Y存儲的全部內容,那麼就能夠認爲X是Y的一個鏡像,因爲地理位置的因素,中央倉庫的下載速度會比較慢,這時咱們能夠配置Maven使用鏡像來代替中央倉庫,編輯settings.xml

    <settings>
        ......
        <mirrors>
            <mirror>
                <id>maven.net.cn</id>
                <name>one of the central mirror in china</name>
                <url>http://maven.net.cn/content/groups/public/</url>
                <mirrorOf>central</mirrorOf>
            </mirror>
        </mirrors>
        ......
    </settings>
    • <mirrorOf>的值爲central,表示該配置爲中央倉庫的鏡像,任何對於中央倉庫的請求都會轉至該鏡像,用戶也可使用一樣的方法配置其餘倉庫的鏡像,另外三個元素id,name,url與通常倉庫配置無異,表示該鏡像倉庫的惟一標識,名稱以及地址

      • 若該鏡像要進行驗證,即基於該id配置倉庫認證
<settings>
    .....
    <mirrors>
        <mirror>
            <id>internal-repository</id>
            <name>Internal Repository Mananger</name>
            <url>http://192.168.1.100/maven2/</url>
            <mirrorOf>*</mirrorOf>
        </mirror>
    </mirrors>
    .....
</settings>
  • 以上mirrorOf元素的值爲*,表示該配置是全部Maven倉庫的鏡像,對於任何遠程倉庫的請求都會被轉至該指定的倉庫,若是鏡像倉庫須要驗證,則配置一個id爲internal-repository的<server>便可

    • <mirrorOf>*<mirrorOf>:匹配全部遠程倉庫
    • <mirrorOf>external:*<mirrorOf>:匹配全部遠程倉庫,使用localhost的除外,使用file://協議的除外,也就是說,匹配全部不在本機上的遠程倉庫
    • <mirrorOf>repo1,repo2<mirrorOf>:匹配倉庫repo1和repo2,用逗號分隔多個倉庫
    • <mirrorOf>*,! repo1<mirrorOf>:匹配全部的遠程倉庫,除repo1外,使用感嘆號將倉庫從匹配中排除
  • 須要注意的是,因爲鏡像倉庫徹底屏蔽了被鏡像倉庫,當鏡像倉庫不穩定或中止服務時,Maven仍將沒法訪問被鏡像倉庫,所以沒法下載構件

五. 生命週期和插件

5.1 Maven的生命週期

  • 在Maven出現以前,項目構建的生命週期就已經存在,可是不一樣的公司和開發人員雖然一樣在作構件工做,其對於不一樣的項目卻不可以重用,只能從新定製開發,而Maven的生命週期就是爲了對全部的構件過程進行抽象和統一

    • 這個生命週期包含了項目的清理,初始化,編譯,測試,打包,集成測試,驗證,部署和站點生成等幾乎全部的構件步驟,也就是說幾乎全部項目的構件,都能映射到這樣一個生命週期上
  • Maven的生命週期是抽象的,這意味着生命週期自己不作任何實際的工做,在Maven的設計中,實際的任務(如編譯主代碼)都交由插件完成,這種思想和設計模式的模方法相似,在父類中定義算法的總體結構,子類能夠經過實現或重寫父類的方法來控制實際的行爲

    public abstract class Template{
        public void build(){
            initialize();
            compile();
            test();
            packagee();
            integrationTest();
            deploy();
        }
        
        protect abstract void initialize();
        protect abstract void compile();
        protect abstract void test();
        protect abstract void packagee();
        protect abstract void integrationTest();
        protect abstract void deploy();
    }
    • 在Maven的生命週期中抽象了各個步驟,定義了他們的次序,可是沒有提供具體的實現,而經過插件機制爲每一個構件步驟綁定一個或多個插件行爲,並且Maven爲大多數構件步驟都綁定了默認的插件,例如,針對編譯的maven-compiler-plguin,針對測試的maven-surefire-plugin等
    • Maven定義的生命週期和插件機制一方面保證了全部Maven項目有一致的構件標準,另外一方面又經過默認的插件簡化和穩定實際項目的構件,此外,該機制還提供了足夠的擴展,用戶能夠經過配置現有的插件或自定義插件來自定義構件行爲
  • 三套聲明週期

    • Maven擁有三套相互獨立的生命週期,他們分別爲clean,default和site,每一個生命週期包含一些階段,這些階段是有序的,而且後面的階段依賴於前面的階段,可是三套聲明週期自己是互相獨立的

      1. clean生命週期:清理項目

        • pre-clean:執行一些清理前須要完成的工做
        • clean:清理上次構件生成的文件
        • post-clean:執行一些清理後須要完成的工做
      2. default聲明週期:定義了真正的構件所需執行的全部步驟

        • validate
        • initialize
        • generate-sources
        • process-sources:處理項目主資源文件,通常來講針對/src/main/resources目錄的內容進行變量替換等工做後,複製到項目輸出的主classpath目錄中
        • compile:編譯項目的主源碼,通常來講針對/src/main/java目錄下的Java文件至目錄輸出的主classpath目錄中
        • process-classes
        • generate-test-sources
        • process-test-sources:處理項目測試資源文件,通常來講針對/src/test/resources目錄的內容進行變量替換工做後,複製到項目輸出的測試classpath目錄
        • test-compile:編譯項目的測試代碼,通常來講針對/src/test/java目錄下的java文件至輸出的測試classpath目錄中
        • test:使用單元測試框架運行測試,測試代碼不會被打包或部署
        • prepare-package
        • package:接收編譯好的代碼,打包成可發佈的格式,如jar
        • pre-integration-test
        • integration-test
        • post-integration-test
        • verify
        • install:將包安裝到Maven本地倉庫,供本地其餘Maven項目使用
        • deploy:將最終的包複製到遠程倉庫,供其餘開發人員和Maven項目使用
      3. site聲明週期:創建和發佈項目站點,Maven可以基於POM所包含的信息,自動生成一個友好的站點供交流和發佈項目信息

        • pre-site:執行一些在生成項目站點以前須要完成的工做
        • site:生成項目站點文檔
        • post-site:執行一些在生成項目站點後須要完成的工做
        • site-deploy:將生成的項目站點發布到服務器上
    • 命令行與生命週期

      • 從命令行執行Maven任務最主要方式就是調用Maven的生命週期,各個生命週期是相互獨立的,而生命週期的階段是有先後依賴關係的

        • mvn clean:該命令調用clean生命週期的clean階段,實際執行階段爲pre-clean和clean
        • mvn test:該命令調用default生命週期的test階段,實際執行的階段爲default生命週期的validate,initialize直到test的全部階段,這也解釋爲何在執行測試的時候,項目代碼可以自動編譯
        • mvn clean install:該命令調用clean生命週期的clean階段和default生命週期的install階段
        • mvn clean deploy site-deploy:該命令調用clean生命週期的clean階段,default生命週期的deploy階段和site生命週期的site-deploy階段

5.2 插件

5.2.1 插件目標和綁定

  • Maven的核心僅僅定義了抽象的生命週期,具體的任務是交由插件完成的,插件以獨立的構件形式存在

    • 對於插件自己而言,爲了可以複用代碼,它每每可以完成多個任務,爲每一個功能編寫一個插件顯然不合理,由於這些任務背後有大量可複用的代碼,所以,這些功能彙集到一個插件裏面,每一個功能就是一個插件目標
  • Maven的生命週期和插件相互綁定,用以完成實際的構件任務,具體而言,是生命週期的階段與插件的目標相互綁定,以完成某個具體的構件任務

    • 例如:編譯這一任務對應了default生命週期的compile這一階段,而maven-compiler-plugin這一插件的compile目標能完成此任務

1590662084736

1590662084736

  • 自定義綁定

    • 除了內置綁定之外,用戶還可以本身選擇將某個插件目標綁定到生命週期的某個階段上,這種自定義綁定方式能讓Maven項目在構件過程當中執行更多更豐富的任務

      • 當須要建立項目的源碼jar包,maven-source-plugin能夠幫咱們完成任務,可是內置的插件綁定關係中並無涉及這一任務,所以須要自行配置,它的jar-no-fork目標能將項目主代碼打包成jar文件,能夠將其綁定到default生命週期的verify階段,在執行完集成測試和安裝構件以前建立源碼jar包

        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-source-plugin</artifactId>
                    <version>2.1.1</version>
                    <executions>
                        <execution>
                            <id>attach-sources</id>
                            <phase>verify</phase>
                            <goals>
                                <goal>jar-no-fork</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
      • 在POM的build元素下的plugins子元素中聲明插件的使用,上例用到了是maven-source-plugin,其groupId爲org.apache.maven.plugins,用戶老是應該聲明一個非快照版本,這樣能夠避免因爲插件版本變化形成的構件不穩定性

        • 除了基本的插件座標聲明外,還有插件執行配置,executions下每一個execution子元素能夠用來配置執行一個任務,該例配置了一個id爲attach-sources的任務,經過phase配置將其綁定到verify生命週期階段上,再經過goals配置指定要執行的插件目標,在運行mvn verify時就會執行該任務
        • 有時候.便是不經過phase元素配置生命週期階段,插件目標也能綁定到生命週期中去,緣由是:有不少插件的目標在編寫時就已經定義了默認綁定階段,可使用maven-help-plugin查看詳細信息

          • mvn help:describe -Dplugin=org.apache.maven.plugins:maven-source-plugin:2.1 -Ddetail

            • Bound to phase : package 默認綁定到package生命週期
      • 當插件目標被綁定到不一樣生命週期階段的時候,其執行順序會由生命週期階段的前後順序決定,若是多個目標被綁定到同一個階段,他們的順序就由插件聲明的前後順序決定

5.2.2 插件配置

  • 用戶能夠經過配置插件目標的參數,進一步調整插件目標所執行的任務,幾乎全部Maven插件的目標均可以配置參數,用戶能夠經過命令行和POM配置等方式來配置這些參數

    • 命令行插件配置

      • 在平常的Maven使用中,咱們一般從命令行輸入並執行Maven命令,不少插件目標的參數都支持從命令行配置,用戶能夠在Maven命令中使用-D參數,並伴隨一個參數鍵=參數值的形式,來配置插件目標的參數

        • mvn install -Dmaven.test.skip = ture:給maven.surefire-plugin提供一個maventest.skip參數,當參數爲true時,就會跳過執行測試

          • 參數-D是Java自帶的,其功能是經過命令行設置一個Java系統屬性,Maven簡單地重用了該參數,在準備插件的時候檢查系統屬性來實現插件參數的配置
    • POM中插件全局配置

      • 並非全部的插件參數都適合用命令行配置,有些參數的值從項目建立到項目發佈都不會改變,或者說不多改變,這種狀況下在POM文件中一次性配置比重複在命令行輸入更合理

        • 用戶能夠在聲明插件的時候,對插件進行一個全局的配置,全部基於該插件的目標任務,都會使用這些配置,如:maven-compiler-plugin來編譯1.8版本的源文件,生成與JVM1.8兼容的字節碼文件

          <build>
              <plugins>
                  <plugin>
                      <groupId>org.apache.maven.plugins</groupId>
                      <artifact>maven-compiler-plugin</artifact>
                      <version>2.1</version>
                      <configuration>
                          <source>1.8</source>
                          <target>1.8</target>
                      </configuration>
                  </plugin>
              </plugins>
          </build>

5.2.3 獲取插件信息

  • 當遇到一個構建任務時,不只須要知道去哪裏找到合適的插件,還須要詳細的瞭解插件的配置點,因爲Maven的插件很是多,其中大部分沒有完善的文檔,所以,經過幫助命令來了解插件顯得很是重要

    1. 使用maven-help-plugin描述插件

      • 除了訪問在線的插件文檔外,能夠藉助maven-help-plugin來獲取插件的詳細信息

        • mvn help:decribe -Dplugin = org.apache.maven.plugins:maven-compiler-plugin:2.1

          • 這裏執行的是maven-help-plugin的decribe目標,在參數plugin中輸入須要描述插件的groupId,artifactId和version,Maven在命令行的輸出maven-compiler-plugin的簡要信息,包括插件的座標,目標前綴和目標等信息
      • 在描述插件時,能夠省去版本信息,Maven會自動獲取最新版原本進行表述,並能夠用目標前綴來替換座標

        • mvn help:describe -Dplugin=compiler
      • 若是僅僅描述某個插件目標的信息,則加上goal參數,更詳細的信息,則加上detail參數

        • mvn help:describe -Dplugin=compiler -Dgoal=compile -Ddetail

5.2.4 插件解析機制

  • 爲了方便用戶使用和配置插件,Maven不須要用戶提供完整的插件座標信息,就能夠解析獲得正確的插件

    • 插件倉庫

      • 與依賴構件同樣,插件構件一樣基於座標存儲在Maven倉庫中,在須要的時候Maven先從本地倉庫尋找,若是不存在,則從遠程倉庫查找,找到插件以後,再下載到本地倉庫使用
      • 不一樣於repositories以及repository子元素,插件的遠程倉庫使用pluginRepositories和pluginRepository配置

        <pluginRepositories>
            <pluginRepository>
                <id>central</id>
                <name>Maven Plugin Resository</name>
                <url>http://repo1.maven.org/maven2</url>
                <layout>default</layout>
                <snapshots>
                    <enabled>false</enabled>
                </snapshots>
                <release>
                    <enabled>true</enabled>
                    <updatePolicy>never</updatePolicy>
                </release>
            </pluginRepository>
        </pluginRepositories>
  • 插件默認的groupId

    • 在POM中配置插件時,若是該插件時Maven的官方插件(即groupId爲org.apache.maven.plugins),就能夠省略groupId,Maven在解析該插件的時候,會自動使用默認groupId不起

      <build>
          <plugins>
              <plugin>
                  <artifactId>maven-compiler-plugin</artifactId>
                  <version>2.1</version>
                  <configuration>
                      <source>1.8</source>
                      <target>1.8</target>
                  </configuration>
              </plugin>
          </plugins>
      </build>
  • 解析插件版本

    • 一樣爲了簡化插件的配置和使用,在用戶沒有提供插件版本的狀況下,Maven會自動解析插件版本

      • 首先Maven在超級POM中爲全部核心插件設定了版本,超級POM是全部Maven項目的父POM,全部項目都繼承這個超級POM配置,所以即便用戶不加任何配置,Maven在使用核心插件時,它的版本就已經肯定了
      • 若是用戶使用的某個插件沒有設定版本,而且這個插件也不屬於核心插件,Maven機會去檢查全部倉庫中可用的版本,經過倉庫元數據groupId/artifactId/maven-metadata.xml文件,如maven-compiler-plugin插件爲例,它在org/apache/maven/plugins/maven-compiler-plugin/maven-metadata.xml

        <?xml version="1.0" encoding="UTF-8"?>
        <metadata>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <versioning>
                <latest>2.1</latest>
                <release>2.1</release>
                <versions>
                    <version>2.0-beta-1</version>
                    <version>2.1</version>
                    <version>2.2</version>
                    <version>2.3</version>
                </versions>
                <lastUpdated>20190102092331</lastUpdated>
            </versioning>
        </metadata>
      • Maven遍歷本地倉庫和全部遠程倉庫,將倉庫元數據合併後,就能計算出latest和release的值,maven3將版本解析到最新的非快照版,如2.1

5.2.5 解析插件前綴

  • Maven命令支持使用插件前綴來簡化插件的使用,插件前綴和groupId:artifact是一一對應的,這種匹配關係存儲在倉庫元數據中,與以前提到的groupId/artifactId/maven-metadata.xml不一樣,這裏倉庫元數據爲groupId/maven-metadata.xml,通常插件都位於/org/apache/maven/plugins/和org/code-haus/mojo/,能夠經過settings.xml配置讓Maven檢查其餘groupId上的插件倉庫元數據

    <settings>
        <pluginGroups>
            <pluginGroup>com.my.plugins</pluginGroup>
        </pluginGroups>
    </settings>
  • 在倉庫的元數據文件中能夠看到插件的前綴定義

    <metadata>
        <plugins>
            <plugin>
                <name>Maven Clean Plugin</name>
                <prefix>clean</prefix>
                <artifact>maven-clean-plugin</artifact>
            </plugin>
            <plugin>
                <name>Maven Compiler Plugin</name>
                <prefix>compile</prefix>
                <artifact>maven-compile-plugin</artifact>
            </plugin>
            <plugin>
                <name>Maven Dependency Plugin</name>
                <prefix>dependency</prefix>
                <artifact>maven-dependency-plugin</artifact>
            </plugin>
        </plugins>
    </metadata>

六.聚合與繼承

  • Maven的集合特性可以把項目各個模塊聚合在一塊兒構建,而Maven的繼承特性則能幫助抽泣各模塊相同的依賴和插件等配置,在簡化POM的同時,還能促進各個模塊配置的一致性

6.1 集合

  • aggregator

    <modelVersion>4.0.0</modelVersion>
    <groupId>com.lsy.project</groupId>
    <artifact>project-aggregator</artifact>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>pom</packaging>
    <name>Aggregator</name>
    
    <modules>
        <module>project-email</module>
        <module>project-register</module>
    </modules>
    • 對於聚合模塊,其打包方式packaging的值必須爲pom
    • 經過modules元素來實現聚合,用戶能夠經過在一個打包方式爲pom的Maven項目中聲明任意數量的module元素來實現模塊的聚合,這裏每一個module的值都是一個當前POM的相對目錄

      • 如aggregator的POM路徑爲.../project-aggregator/pom.xml,那麼project-email對應 的目錄爲.../project-aggregator/project-email/,而且目錄中包含了pom.xml,src/main/java,src/test/java等內容,離開了project-aggregator也能獨立建立=
    • 聚合模塊和其餘模塊的目錄結構並不是必定要父子關係,也能夠是平行關係

      <modules>
          <module>../prject-email</module>
          <module>../project-register</module>
      </modules>
    • 當在聚合模塊中執行mvn clean install時,Maven首先解析聚合模塊的POM,分析要構件的模塊,並計算出一個反應堆構件順序(Reactor Build Order),而後根據這個順序構件各個模塊
    • 聚合模塊的構件順序

      • Maven按序讀取POM文件,若是POM沒有依賴模塊,那麼就構件模塊,不然就先構件其依賴模塊

6.2 繼承

  • 從以上的聚合模塊和其餘模塊中能夠看到,多個被管理模塊的POM文件中會有大量重複的相同配置,他們有相同的groupId和version,相同的依賴,相同的插件配置,而經過POM文案的繼承能夠消除重複

    • project-parent父模塊

      <modelVersion>4.0.0</modelVersion>
      <groupId>com.lsy.project</groupId>
      <artifactId>project-parent</artifactId>
      <version>1.0.0-SNAPSHOT</version>
      <packaging>pom</packaging>
      <name>Parent</name>
      • 父模塊的POM文件使用與其餘模塊一直的groupId和version,它的packaging方式爲pom,與聚合模塊一致
      • 父模塊主要是爲了消除配置的重複,所以它自己不包含除POM文件以外的項目文件,也就不須要src/main/java之類的文件夾了
    • project-email子模塊

      <parent>
          <groupId>com.lsy.project</groupId>
          <artifactId>project-parent</artifactId>
          <version>1.0.0-SNAPSHOT</version>
          <relativePath>../project-parent/pom.xml</relativePath>
      </parent>
      
      <artifactId>project-email</artifactId>
      <name>Email</name>
      
      <dependencies>
          .....
      </dependencies>
      • 使用parent元素聲明父模塊,parent下子元素groupId,artifactId,version指定父模塊的座標,元素relativePath表示父模塊POM的相對路徑,表示Email模塊和其父模塊是在平行的目錄下

        • 當構件項目時,Maven會首先根據relativePath檢查父POM文件,若是找不到再從本地倉庫找,relativePath的默認值爲../pom.xml,也就是說,Maven默認父POM在上一層目錄
      • 子模塊沒有groupId和version,是由於子模塊隱式的從父模塊繼承了這兩個元素,從而消除了沒必要要的配置
  • 可繼承的POM元素

    • groupId:項目組ID,項目座標的核心元素
    • version:項目版本,項目座標的核心元素
    • description:項目的描述信息
    • organization:項目的組織信息
    • inceptionYear:項目的創始年份
    • url:項目的URL地址
    • develops:項目的開發者信息
    • contributors:項目貢獻者信息
    • distributionManagement:項目的部署配置
    • issueManagement:項目的缺陷跟蹤系統信息
    • ciManagement:項目的持續集成系統信息
    • scm:項目的版本控制系統信息
    • mailingList:項目的郵件列表信息
    • properties:自定義屬性
    • dependencies:項目的依賴配置
    • dependencyManagement:項目的依賴管理配置
    • repositories:項目的倉庫配置
    • pluginRepositories:項目的插件倉庫配置
    • build:包括項目的源碼目錄配置,輸出目錄配置,插件配置,插件管理配置等
    • reporting:項目的輸出目錄配置,報告插件配置等
  • 依賴管理

    • dependencies元素是能夠被繼承的,說明依賴是會被繼承的,因此咱們能夠將子模塊共有的依賴配置到父模塊中,子模塊就能夠移除這些依賴,簡化配置
    • 上述方法是可行的,可是可能未來會有新的模塊並不須要父模塊中的一些依賴,這就會產生不合理的現象,從而Maven提供了dependencyManagement元素,既能讓子模塊繼承到父模塊的依賴配置,又能保證子模塊依賴使用的靈活性

      • 在dependencyManagement元素下的依賴聲明不會引入實際的依賴,不過它可以約束dependencies下的依賴使用
      <modelVersion>4.0.0</modelVersion>
      <groupId>com.lsy.project</groupId>
      <artifactId>project-parent</artifactId>
      <version>1.0.0-SNAPSHOT</version>
      <packaging>pom</packaging>
      <name>Parent</name>
      
      <properties>
          <springframework.version>4.3.1</springframework.version>
      </properties>
      
      <dependencyManagement>
          <dependencies>
              <dependency>
                  <groupId>org.springframework</groupId>
                  <artifactId>spring-core</artifactId>
                  <version>${springframwork.version}</version>
              </dependency>
              <dependency>
                  <groupId>org.springframework</groupId>
                  <artifactId>spring-beans</artifactId>
                  <version>${springframwork.version}</version>
              </dependency>
              <dependency>
                  <groupId>org.springframework</groupId>
                  <artifactId>spring-context</artifactId>
                  <version>${springframwork.version}</version>
              </dependency>
          </dependencies>
      </dependencyManagement>
* 這裏使用dependencyManagement聲明的依賴既不會給parent模塊引入依賴,也不會給子模塊引入依賴,不過這段配置是會被繼承的

* 子模塊POM

  ```xml
  <parent>
      <groupId>com.lsy.project</groupId>
      <artifactId>project-parent</artifactId>
      <version>1.0.0-SNAPSHOT</version>
      <relativePath>../project-parent/pom.xml</relativePath>
  </parent>
  
  <artifactId>project-email</artifactId>
  <name>Email</name>
  
      <dependencies>
          <dependency>
              <groupId>org.springframework</groupId>
              <artifactId>spring-core</artifactId>
          </dependency>
          <dependency>
              <groupId>org.springframework</groupId>
              <artifactId>spring-beans</artifactId>
          </dependency>
          <dependency>
              <groupId>org.springframework</groupId>
              <artifactId>spring-context</artifactId>
          </dependency>
      </dependencies>
  ```

  * 使用這種依賴管理機制,能夠在父POM中使用dependencyManagement聲明依賴可以統一項目規範中的依賴版本,當版本在父POM中聲明以後,子模塊使用依賴時就不須要聲明瞭.也不會發生多個子模塊使用依賴版本不一致的狀況

    * scoper元素的import依賴範圍只在dependencyManagement元素下才有效果,使用該範圍的依賴一般指向一個POM文件,做用是將POM中的dependencyManagement配置導入併合併到當前POM的dependencyManagement元素中
    * 因此除了複製配置和繼承父模塊這兩種方式外,還能夠經過import範圍依賴導入這一配置

    ```xml
    <dependencyManagement>
        <dependencies>
            <dependency>com.lsy.project</dependency>
            <artifactId>project-parent</artifactId>
            <version>1.0-SNAPSHOT</version>
            <type>pom</type>
            <scope>import</scope>
        </dependencies>
    </dependencyManagement>
    ```
  • 插件管理

    • Maven提供了dependencyManagement元素幫助管理依賴,相似的,Maven也提供了pluginManagement元素管理插件,該元素中配置的依賴一樣不會形成實際的插件調用行爲,而POM文件中配置了真正的plugin元素,而且groupId和artifact一致時,纔會產生實際的插件行爲
    • 父模塊

      <build>
          <pluginManagement>
              <plugins>
                  <plugin>
                      <groupId>org.apache.maven.plugins</groupId>
                      <artifactId>maven-source-plugin</artifactId>
                      <version>3.1.1</version>
                      <executions>
                          <execution>
                              <id>attach-source</id>
                              <phase>verify</phase>
                              <goals>
                                  <goal>jar-no-fork</goal>
                              </goals>
                          </execution>
                      </executions>
                  </plugin>
              </plugins>
          </pluginManagement>
      </build>
    • 子模塊

      <build>
          <plugins>
              <plugin>
                  <groupId>org.apache.maven.plugins</groupId>
                  <artifact>maven-source-plugin</artifact>
              </plugin>
          </plugins>
      </build>
      • 子模塊使用了maven-source-plugin插件,同時又繼承了父模塊的pluginManagement配置
  • 集合與繼承的關係

    • 多模塊的聚合與繼承實際上是兩個概念,其目的是徹底不一樣的,前者爲了方便快速的構件項目,後者主要爲了消除重複配置

      • 對於聚合模塊來講:它知道有哪些模塊被聚合,可是那些被聚合的模塊並不知道這個聚合模塊的存在
      • 對於繼承關係的父POM來講:它不知道有哪些子模塊繼承於它,可是那些子模塊都必須知道本身的父模塊是什麼
    • 在實際項目中,一個POM每每便是聚合模塊,又是父模塊
  • 約定優於配置

    • Maven提倡"約定優於配置",Maven只須要一個簡單的POM文件,就能夠完成清除,構件等任務

      • 源碼目錄:src/main/java/
      • 編譯輸出目錄爲:target/classes/
      • 打包方式爲:jar
      • 包輸出目錄爲:target/
    • Maven此機制的來源就是超級POM文件,此文件在$MAVEN_HOME/lib/maven-model-builder-x.x.x.jar中的org/apache/maven/model/pom-4.0.0.xml路徑下

      <repositories>
          <repository>
              <id>central</id>
              <name>Maven Repository Swithboard</name>
              <url>http://repo1.maven.org/maven2</url>
              <layout>default</layout>
              <snapshots>
                  <enabled>false</enabled>
              </snapshots>
          </repository>
      </repositories>
      
      
      <pluginRepositories>
          <pluginRepository>
              <id>central</id>
              <name>Maven Plugin Repository</name>
              <url>http://repo1.maven.org/maven2</url>
              <layout>default</layout>
              <snapshots>
                  <enabled>false</enabled>
              </snapshots>
              <releases>
                  <updatePolicy>never</updatePolicy>
              </releases>
          </pluginRepository>
      </pluginRepositories>
      
      
      <build>
          <!--> 定義了項目的主輸出目錄 </!-->
          <directory>${project.basedir}/target</directory>
          <!--> 主代碼輸出目錄 </!-->
          <outputDirectory>${project.build.directory}/classes</outputDirectory>
          <!--> 最終構件的名稱格式 </!-->
          <finalName>${project.artifactId}-${project.version}</finalName>
          <!--> 測試代碼輸出目錄 </!-->
          <testOutputDirectory>${project.build.directory}/test-
        classes</testOutputDirectory>
          <!--> 主源碼目錄 </!-->
          <sourceDirectory>${project.basedir}/src/main/java</sourceDirectory>
          <!--> 腳本源碼目錄 </!-->
          <scriptSourceDirectory>src/main/scripts</scriptSourceDirectory>
          <!--> 測試源碼目錄 </!-->
          <testSourceDirectory>${project.basedir}/src/test/java</testSourceDirectory>
          <!--> 主資源目錄 </!-->
          <resources>
              <resource>
                  <directory>${project.basedir}/src/main/resources</directory>
              </resource>
          </resources>
          <!-- >測試資源目錄 </!-->
          <testResources>
              <testResource>
                  <directory>${project.basedir}/src/test/resources</directory>
              </testResource>
          </testResources>
          
          
          
          <!--> 核心插件版本 </!-->
      <pluginManagement>
          <plugins>
              <plugin>
                  <artifactId>maven-antrun-plugin</artifactId>
                  <version>1.3</version>
              </plugin>
              <plugin>
                  <artifactId>maven-assembly-plugin</artifactId>
                  <version>2.3</version>
              </plugin>
              <plugin>
                  <artifactId>maven-clean-plugin</artifactId>
                  <version>2.3</version>
              </plugin>
              <plugin>
                  <artifactId>maven-compiler-plugin</artifactId>
                  <version>2.0.3</version>
              </plugin>
              ......
          </plugins>
      </pluginManagement>
      </build>

七. Nexus私服

7.1 Nexus內置的倉庫

  • Nexus倉庫有四種類型

    1. group:倉庫組
    2. hosted:宿主倉庫
    3. proxy:代理倉庫
    4. virtual:虛擬倉庫

1590846416265

    • Maven能夠從宿主倉庫下載構件,Maven也能夠從代理倉庫下載構件,而代理倉庫會簡介的從遠程倉庫下載並緩存構件
    • 而通常爲了方便,咱們會從倉庫組下載構件,而倉庫組沒有實際的內容,它只是管理一組實際的倉庫,當它接收到請求時,它會轉向其包含的宿主倉庫或代理倉庫得到實際構件的內容
    • 倉庫的Policy屬性

      • Release:發佈版
      • Snapshot:快照版

    7.2 配置Maven從Nexus下載構件

    • 在POM文件中配置倉庫和插件倉庫------只對當前項目有效

      <project>
          .......
          <repositories>
              <repository>
                  <id>nexus</id>
                  <name>Nexus</name>
                  <url>http://localhost:8081/nexus/content/groupId/public/</url>
                  <snapshots>
                      <enabled>true</enabled>
                  </snapshots>
              </repository>
          </repositories>
          <pluginRepositories>
              <pluginRepository>
                  <id>nexus</id>
                  <Name>Nexus</Name>
                  <url>http://localhost:8081/nexus/content/groupId/public/</url>
                    <release>
                      <enabled>true</enabled>
                  </release>
                  <snapshots>
                      <enabled>true</enabled>
                  </snapshots>
              </pluginRepository>
          </pluginRepositories>
          .......
      </project>
    • 在settings.xml文件中配置---全局有效

      • settings.xml文件並不支持直接配置repositories和pluginRepositories,可是能夠經過Maven提供的Profile機制,讓用戶將倉庫配置到settings.xml中的Profile中
      <settings>
          ....
          <profiles>
              <profile>
                  <id>nexus</id>
                  <repositories>
                      <repository>
                            <id>nexus</id>
                          <name>Nexus</name>
                          <url>http://localhost:8081/nexus/content/groupId/public/</url>
                          <release>
                              <enabled>true</enabled>
                          </release>
                          <snapshots>
                              <enabled>ture</enabled>
                          </snapshots>
                      </repository>
                  </repositories>
                  
                  <pluginRepositories>
                      <pluginRepository>
                          <id>nexus</id>
                          <Name>Nexus</Name>
                          <url>http://localhost:8081/nexus/content/groupId/public/</url>
                          <release>
                              <enabled>true</enabled>
                          </release>
                          <snapshots>
                              <enabled>true</enabled>
                          </snapshots>
                      </pluginRepository>
                  </pluginRepositories>
              </profile>
          </profiles>
          
          <!--> 激活指定id的profile </!-->
          <activeProfiles>
              <activeProfile>nexus</activeProfile>
          </activeProfiles>
      </settings>
    • 以上配置已經能讓Maven項目從Nexus私服下載構件了,可是Maven仍然會去訪問中央倉庫,如今咱們想要將全部請求都僅僅經過私服,這就須要藉助於Maven鏡像了

      <settings>
          .....
          <mirrors>
              <mirror>
                  <id>nexus</id>
                  <mirrorOf>*</mirrorOf>
                  <url>http://localhost:8081/nexus/content/groupId/public/</url>
              </mirror>
          </mirrors>
          
          <profiles>
              <profile>
                  <id>nexus</id>
                  <repositories>
                      <repository>
                            <id>central</id>
                          <name>central</name>
                          <url>http://central</url>
                          <release>
                              <enabled>true</enabled>
                          </release>
                          <snapshots>
                              <enabled>ture</enabled>
                          </snapshots>
                      </repository>
                  </repositories>
                  
                  <pluginRepositories>
                      <pluginRepository>
                          <id>central</id>
                          <Name>Nexus</Name>
                          <url>http://central</url>
                          <release>
                              <enabled>true</enabled>
                          </release>
                          <snapshots>
                              <enabled>true</enabled>
                          </snapshots>
                      </pluginRepository>
                  </pluginRepositories>
              </profile>
          </profiles>
          
          <!--> 激活指定id的profile </!-->
          <activeProfiles>
              <activeProfile>nexus</activeProfile>
          </activeProfiles>
      </settings>
      • 這裏倉庫和插件倉庫配置的id都爲central,它們將會覆蓋POM中央倉庫的配置,而且這裏的url已經可有可無,由於全部的請求都會經過鏡像訪問私服地址

    7.3 部署構件至Nexus

    • 若是隻是爲了代理外部公共倉庫,那麼Nexus的代理倉庫就已經徹底足夠了,對於另外一類Nexus倉庫---宿主倉庫來講,他們主要做用是存儲組織內部的,或一些沒法從公共倉庫得到的第三方構件,用戶能夠經過配置Maven自動部署構件至Nexus的宿主倉庫

      • 使用Maven部署構件至Nexus

        • 平常開發生成的快照版本構件能夠直接部署到Nexus中策略爲Snapshot的宿主倉庫中,項目正式發佈的構件則應該部署到Nexus中策略爲Release的宿主倉庫中
        • POM文件配置

          <project>
              .....
              <distributeManagement>
                  <repository>
                      <id>nexus-releases</id>
                      <name>Nexus Release Repository</name>
                      <url>http://localhost:8081/nexus/content/repositories/
                          release/</url>
                  </repository>
                  <snapshotRepository>
                      <id>nexus-snapshots</id>
                      <name>Nexus Snapshots Repository</name>
                      <url>http://localhost:8081/nexus/content/repositories/
                          snapshots</url>
                  </snapshotRepository>
              </distributeManagement>
              .....
          </project>
        • Nexus的倉庫對於匿名用戶只是可讀的,爲了可以部署構件,還須要在settings.xml中配置認證信息

          <settings>
              ....
              <servers>
                  <server>
                      <id>nexus-releases</id>
                      <username>admin</username>
                      <password>admin123</password>
                  </server>
                  <server>
                      <id>nexus-snapshots</id>
                      <username>admin</username>
                      <password>admin123</password>
                  </server>
              </servers>
              ....
          </settings>

    八. Profile

    • 一個優秀的構件系統必須足夠靈活,它應該可以讓項目在不一樣的環境下都可以成功的構件,例如:開發環境,測試環境和產品環境,這些環境的數據庫配置不盡相同,那麼項目構建時就須要可以識別其所在的環境並正確使用配置

    8.1 屬性

    <properties>
        <springframework.version>4.3.1</springframework.version>
    </properties>
    
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-core</artifactId>
                <version>${springframwork.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-beans</artifactId>
                <version>${springframwork.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
    • 這是最多見的Maven屬性使用的方式,經過properties元素使得用戶自定義一個或多個Maven屬性,而後在POM的其餘地方使用${屬性名稱}的方式來引用該屬性,這種作法的最大意義是消除重複
    • Maven的屬性有六類

      1. 內置屬性

        1. ${basedir}:表示項目根目錄,即包含pom.xml文件的目錄
        2. ${version}:表示項目的版本
      2. POM屬性:用戶可使用該類屬性引用POM文件中對應元素的值

        • ${project.artifactId}:對應<project><artifactId>元素的值

          • ${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/test-classes
          • ${project.build.groupId}:項目的groupId
          • ${project.build.artifactId}:項目的artifactId
          • ${project.build.build.finalName}:項目打包輸出文件的名稱,默認爲\${project.artifactId}

            -&dollar;{project.version}

      3. 自定義屬性:<properties>元素下自定義的Maven屬性
      4. settings屬性:與POM屬性同理,用戶使用settings.來引用,如:&dollar;{settings.localRepository}指向用戶本地倉庫的地址
      5. Java系統屬性:全部Java系統屬性均可以使用Maven屬性引用,如&dollar;{user.home}指向用戶目錄

        • 能夠經過mvn help:system查看全部的java系統屬性
      6. 環境變量屬性:全部環境變量均可以用env.來引用,例如:&dollar;{env.JAVA_HOME}

        • 能夠經過 mvn help:system查看全部的環境變量

    8.2 資源過濾

    • 在不一樣的開發環境中,項目的源碼應該使用不一樣的方式進行構件,最多見的就是數據庫的配置了,在開發中,有些項目會在src/main/resources/目錄下放置不一樣環境下的數據庫配置

      database.jdbc.driverClass = com.mysql.jdbc.Driver
      database.jdbc.connectionURL = jdbc:mysql://localhost:3306/dev
      database.jdbc.username = dev
      database.jdbc.password = dev-passwd
      
      database.jdbc.driverClass = com.mysql.jdbc.Driver
      database.jdbc.connectionURL = jdbc:mysql://localhost:3306/test
      database.jdbc.username = test
      database.jdbc.password = test-passwd
    • 爲了應對環境的變化,咱們可使用Maven屬性將這些會發生變化的部分提取出來

      database.jdbc.driverClass = ${db.driver}
      database.jdbc.connectionURL = ${db.url}
      database.jdbc.username = ${db.username}
      database.jdbc.password = ${db.password}
    • 在settings.xml中經過profile定義不一樣環境下的配置數據

      <profiles>
          <profile>
              <id>dev</id>
              <properties>
                  <db.driver>com.mysql.jdbc.Driver</db.driver>
                  <db.url>jdbc:mysql://localhost:3306/dev</db.url>
                  <db.username>dev</db.username>
                  <db.password>dev-passwd</db.password>
              </properties>
          </profile>
          
              <profile>
              <id>test</id>
              <properties>
                  <db.driver>com.mysql.jdbc.Driver</db.driver>
                  <db.url>jdbc:mysql://localhost:3306/test</db.url>
                  <db.username>test</db.username>
                  <db.password>test-passwd</db.password>
              </properties>
          </profile>
      </profiles>
      • 須要注意的是:

        • Maven屬性默認只會在POM文件中才會被解析,也就是說&dollar;{db.username}放到POM文件中會變成dev或test,可是在src/main/resources/目錄下仍然仍是&dollar;{db.username},所以,須要讓Maven解析資源文件中的Maven屬性
        • 資源文件處理的是maven-reosurces-plugin作事,它默認的行爲只是將項目主資源文件複製到主代碼編譯輸出目錄
    • 開啓資源過濾

      • Maven默認的主資源目錄和測試目錄的定義是在超級POM文件中
      <resources>
          <resource>
              <directory>${project.basedir}/src/main/resources</directory>
              <filtering>true</filtering>
          </resource>
      </resources>
      <testResources>
          <testResource>
              <directory>${project.basedir}/src/test/resources</directory>
              <filtering>true</filtering>
          </testResource>
      </testResources>
    • 經過mvn clean install -Pdev : 經過mvn 的 -P參數表示在命令行激活一個profile=dev的profile

    8.3 Profile

    • 激活Pofile

      1. 命令行激活

        • 用戶可使用mvn命令行參數-P 加上profile的id來激活profile,多個id之間逗號分隔

          • mvn clean install -Pdev,-Ptest
      2. settings文件顯示激活

        • 若是用戶但願某個profile默認一直處於激活狀態,就能夠配置settings.xml文件的activeProfile元素,表示其配置的profile對全部項目都處於激活狀態

          <settings>
              .....
              <activeProfiles>
                  <activeProfile>dev</activeProfile>
              </activeProfiles>
              .....
          </settings>
      3. 系統屬性激活

        • 用戶能夠配置當前某系統屬性存在時,自動激活profile

          <profiles>
              <profile>
                  <activation>
                      <property>
                          <name>test</name>
                      </property>
                  </activation>
                  .....
              </profile>
          </profiles>
        • 用戶能夠配置當前某系統屬性存在且等於a時,自動激活profile

          <profiles>
              <profile>
                  <activation>
                      <property>
                          <name>test</name>
                          <value>a</value>
                      </property>
                  </activation>
                  .....
              </profile>
          </profiles>
        • 用戶能夠在命令行聲明系統屬性

          • mvn clean install -Dtest=x
      4. 操做系統環境變量激活

        • Profile還能夠根據操做系統環境激活

          <profiles>
              <profile>
                  <activation>
                      <os>
                          <name>Windows10</name>
                      </os>
                  </activation>
                  .....
              </profile>
          </profiles>
      5. 文件存在與否激活

        • Maven能夠根據項目中某個文件是否存在來決定是否激活profile

          <profiles>
              <profile>
                  <activation>
                      <file>
                          <missing>a.properties</missing>
                          <exists>b.properties</exists>
                      </file>
                  </activation>
                  .....
              </profile>
          </profiles>
      6. 默認激活

        • 用戶能夠在定義profile的時候指定其默認激活

          <profiles>
              <profile>
                  <id>dev</id>
                  <activation>
                      <activeByDefault>true</activeByDefault>
                  </activation>
                  .....
              </profile>
          </profiles>
    • 以上激活優先級從上之下依次減少

      • 能夠經過mvn help:active-profiles來查看當前激活的profile
      • 能夠經過mvn help:all-profiles來查看全部的profile
    • profile的種類

      • 根據具體的須要,能夠在一下位置聲明profile

        • pom.xml:很顯然,pom.xml中聲明的profile只對當前項目有效
        • 用戶settings.xml:用戶目錄下.m2/settings.xml中的profile只對本機該用戶全部的Maven項目有效
        • 全局settings.xml:Maven安裝目錄下conf/settings.xml中profile對本機全部Maven項目都有效
    • POM中profile可以使用的元素

      <project>
          <repository></repository>
          <pluginRepository></pluginRepository>
          <distributionManagement></distributionManagement>
          <dependencies></dependencies>
          <dependencyManagement></dependencyManagement>
          <modules></modules>
          <properties></properties>
          <reporting></reporting>
          <build>
              <plugins></plugins>
              <defaultGoal></defaultGoal>
              <resources></resources>
              <testResources></testResources>
              <finalName></finalName>
          </build>
      </project>
      *   pom.xml:很顯然,pom.xml中聲明的profile只對當前項目有效
            
        *   用戶settings.xml:用戶目錄下.m2/settings.xml中的profile只對本機該用戶全部的Maven項目有效
            
        *   全局settings.xml:Maven安裝目錄下conf/settings.xml中profile對本機全部Maven項目都有效
    • POM中profile可以使用的元素
    相關文章
    相關標籤/搜索