何爲Maven的生命週期?html
1、Maven從大量項目和構建工具中學習和反思,而後總結了一套高度完善的、易擴展的生命週期java
2、這個生命週期包含了項目的清理、初始化、編譯、測試、打包、集成測試、驗證、部署和站點生成等幾乎全部的構建步驟算法
3、Maven的生命週期是抽象的,這意味着生命週期自己不作任何實際的工做,實際的任務(如編譯源代碼)都是交由插件來完成的apache
Maven的這種思想與設計模式的模板方法很是類似設計模式
模板方法模式在父類中定義算法的總體結構,子類能夠經過實現或者重寫父類的方法來控制實際的行爲,這樣既保證了算法有足夠的可擴展性,又可以嚴格控制算法的總體結構服務器
模擬生命週期的模板方法抽象類框架
public abstract class AbstractBuild {maven
public void build() {ide
initialize();工具
compile();
test();
packages();
integrationTest();
deploy();
}
protected abstract void initialize();
protected abstract void compile();
protected abstract void test();
protected abstract void packages();
protected abstract void integrationTest();
protected abstract void deploy();
}
對上面的代碼進行解釋:
1、build()方法定義了整個構建的過程,依次初始化、編譯、測試、打包(因爲package 與Java關鍵字衝突,這裏使用了單詞packages)、集成測試和部署
2、可是這個類中沒有具體實現初始化、編譯、測試等行爲,它們都交由子類去實現
Maven定義生命週期和插件機制的好處是?
1、保證了全部Maven項目有一致的構建標準
2、經過默認插件簡化和穩定了實際項目的構建
3、此外,該機制還提供了足夠的擴展空間,用戶能夠經過配置現有插件或者自行編寫插件來自定義構建行爲
Maven的三套生命週期
初學者每每會覺得Maven的生命週期是一個總體,其實否則,Maven擁有三套相互獨立的生命週期,它們分別爲clean、default和site
1、clean生命週期的目的是清理項目
2、default生命週期的目的是構建項目
3、site的生命週期的目的是創建項目站點
聲明週期與階段(phase)
每一個生命週期包含一些階段 (phase),這些階段是有順序的,而且後面的階段依賴於前面的階段,用戶和Maven最直接的交互方式就是調用這些生命週期階段
clean生命週期的階段有哪些?
clean生命週期,主要包含的階段有pre-clean、clean和post-clean
當用戶調用pre-clean的時候,只有pre-clean階段得以執行
當用戶調用clean的時候,pre-clean和clean階段會以順序執行
當用戶調用post-clean的時候,pre-clean、clean和post-clean會得以順序執行
注:生命週期階段的先後是依賴關係,但三套生命週期自己是相互獨立的,用戶能夠僅僅調用clean生命週期的某個階段,或者僅僅調用default生命週期的某個階段,而不會對其餘生命週期產生任何影響,例如,當用戶調用clean生命週期的clean階段的時候,不會觸發default生命週期的任何階段,反之亦然,當用戶調用default生命週期的compile階段的時候,也不會觸發clean生命週期的任何階段
clean生命週期的詳細信息
clean生命週期的目的是清理項目,它包含三個階段:
1、pre-clean執行一些清理前須要完成的工做
2、clean清理上一次構建生成的文件
3、post-clean執行一些清理後須要完成的工做
default生命週期的詳細信息
default生命週期定義了真正構建時所須要執行的全部步驟,它是全部生命週期中最核心的部分,其包含的階段以下,只對重要的階段進行解釋:
1、validate
2、initialize
3、generate-sources
4、process-sources 處理項目主資源文件,通常來講,是對src/main/resources目錄的內容進行變量替換等工做後,複製到項目輸出的主classpath目錄中
5、generate-resources
6、process-resources
7、compile 編譯項目的主源碼。通常來講,是編譯src/main/java目錄下的Java文件至項目輸出的主classpath目錄中
8、process-classes
9、generate-test-sources
10、process-test-sources 處理項目測試資源文件。通常來講,是對src/test/resources 目錄的內容進行變量替換等工做後,複製到項目輸出的測試classpath目錄中
11、generate-test-resources
12、process-test-resources
13、test-compile 編譯項目的測試代碼。通常來講,是編譯src/test/java 目錄下的Java文件至項目輸出的測試classpath目錄中
14、process-test-classes
15、test 使用單元測試框架運行測試,測試代碼不會被打包或部署
16、prepare-package
17、package 接受編譯好的代碼,打包成可發佈的格式,如JAR
18、pre-integration-test
19、integration-test
20、post-integration-test
21、verify
22、install 將包安裝到Maven本地倉庫,供本地其餘Maven項目使用
23、deploy 將最終的包複製到遠程倉庫,供其餘開發人員和Maven項目使用
對於上面未解釋的階段,請參閱官方的解釋:
http://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html
site生命週期的詳細信息
site生命週期的目的是創建和發佈項目站點,Maven可以基於POM所包含的信息,自動生成一個友好的站點,方便團隊交流和發佈項目信息
該生命週期包含以下階段:
1、pre-site 執行一些在生成項目站點以前須要完成的工做
2、site 生成項目站點文檔
3、post-site 執行一些在生成項目站點以後須要完成的工做
4、site-deploy 將生成的項目站點發布到服務器上
命令行與生命週期
從命令行執行Maven任何的最主要方式就是調用Maven的生命週期階段,須要注意的是,各個生命週期是相互獨立的,而一個生命週期的階段是有先後依賴關係的。
下面以一些常見的Maven命令爲例,解釋其執行的生命週期階段:
1、mvn clean:該命令調用clean生命週期的clean階段,實際執行的階段爲clean生命週期的pre-clean和clean階段
2、mvn test:該命令調用default生命週期的test階段,實際執行的階段爲default生命週期的validate、initialize等,直到test的全部階段,這也解釋了爲何在執行測試的時候,項目的代碼可以自動得以編譯
3、mvn clean install:該命令調用clean生命週期的clean階段和default生命週期的install階段。實際執行的階段爲clean生命週期的pre-clean、clean階段,以及default生命週期的從validate至install的全部階段。該命令結合了兩個生命週期,在執行真正的項目構建以前清理項目是一個很好的實踐
4、mvn clean deploy site-deploy:該命令調用clean生命週期的clean階段、default生命週期的deploy階段,以及site生命週期的site-deploy階段。實際執行的階段爲clean生命週期的pre-clean、clean階段,default生命週期的全部階段,以及site生命週期的全部階段。該命令結合了Maven全部三個生命週期,且deploy爲default生命週期的最後一個階段,site-deploy爲site生命週期的最後階段
Maven的核心
Maven的核心僅僅定義了抽象的生命週期,具體的任務是交由插件完成的,插件以獨立的構件形式存在,所以,Maven核心的分發包只有不到3MB的大小,Maven會在須要的時候下載並使用插件
舉例說明插件maven-dependency-plugin
對於插件自己,爲了可以複用代碼,它每每可以完成多個任務
maven-dependency-plugin插件可以基於項目依賴作不少事情:
1、它可以分析項目依賴,幫助找出潛在的無用依賴
2、它能列出項目的依賴樹,幫助分析依賴來源
3、它可以列出項目全部已解析的依賴等等
那麼,爲何這樣設計,一個插件有不少功能呢?
爲每一個這樣的功能編寫一個獨立的插件顯然是不可取的,由於這些任務背後有不少能夠複用的代碼,所以,這些功能彙集在一個插件裏,每一個功能就是一個插件目標
maven-dependency-plugin插件的目標是指?
maven-dependency-plugin有十多個目標,每一個目標對應了一個功能,上述提到幾個功能分別對應的插件目標爲dependency:analyze、dependency:tree和dependency:list,這是一種通用的解法,冒號前面是插件前綴,冒號後面是該插件的目標。相似地,還能夠寫出compile:compile (這是maven-compiler-plugin的compile目標) 和surefire:test (這是maven-surefire-plugin的test目標)
什麼是插件綁定?
Maven的生命週期與插件相互綁定,用以完成實際的構建任務。具體而言,是生命週期的階段與插件的目標相互綁定,以完成某個具體的構建任務
例如:項目編譯這一任務,它對應了default生命週期的compile這一階段,而maven-compile-plugin這一插件的compile目標可以完成該任務,所以,將它們綁定,就能實現項目編譯的目:
生命週期階段與插件目標綁定
爲何須要內置綁定?
爲了能讓用戶幾乎不用任何配置就能構建Maven項目,Maven的核心爲一些主要的生命週期階段綁定了不少插件的目標,當用戶經過命令行調用生命週期階段的時候,對應的插件目標就會執行相應的任務
clean生命週期階段與插件目標的綁定關係
clean生命週期僅有pre-clean、clean和post-clean三個階段,其中的clean與maven-clean綁定。maven-clean-plugin僅有clean這一個目標,其做用就是刪除項目的輸出目錄
生命週期階段 |
插件目標 |
Pre-clean |
|
Clean |
Maven-clean-plugin:clean |
Post-clean |
site生命週期階段與插件目標的綁定關係
site生命週期有pre-site、site、post-site和site-deploy四個階段,其中,site和maven-site-plugin:site相互綁定,site-deploy和maven-site-plugin:depoy相互綁定。maven-site-plugin有不少目標,其中,site目標用來生成項目站點,deploy目標用來將項目站點部署到遠程服務器上
生命週期階段 |
插件目標 |
Pre-site |
|
Site |
Maven-site-plugin:site |
Post-site |
|
Site-deploy |
Maven-site-plugin:deploy |
default生命週期與插件目標的綁定關係
default生命週期與插件目標的綁定關係要稍微複雜一些,這是由於對於任何項目,例如jar項目和war項目,它們的項目清理和站點生成任務是同樣的,不過構建過程會有區別,jar項目須要打成jar包,而war項目須要打包war包
因爲項目的打包類型會影響構建的具體過程,所以,default生命週期的階段與插件目標的綁定關係由項目打包類型決定,打包類型是經過POM中的packing元素定義的。最多見、最重要的打包類型是jar,它也是默認的打包類型
default生命週期的內置插件綁定歡喜及具體任務 (打包類型:jar)
生命週期階段 |
插件目標 |
執行任務 |
Process-resources |
Maven-resources-plugin:resources |
複製主資源文件至主輸出目錄 |
Compile |
Maven-compiler-plugin:compile |
編譯主代碼至主輸出目錄 |
Process-test-resources |
Maven-resources-plugin:testResources |
複製測試資源文件至測試輸出目錄 |
Test-compile |
Maven-compile-plugin:testCompile |
編譯測試代碼至測試輸出目錄 |
Test |
Maven-surefire-plugin:test |
執行測試用例 |
Package |
Maven-jar-plugin:jar |
建立項目jar包 |
Install |
Maven-install-plugin:install |
將項目輸出構件安裝到本地倉庫 |
Deploy |
Maven-deploy-plugin:deploy |
將項目輸出構件部署到遠程倉庫 |
上表只列出了擁有插件的綁定關係的階段,default生命週期還有不少其餘階段
若是沒有綁定任何插件的目標呢?
有些生命週期階段沒有綁定任何插件的目標,所以也沒有任何實際行爲
除了默認的打包類型jar,還有其餘說明你的嗎?
除了默認的打包類型jar,常見的打包類型還有war、pom、maven-plugin、ear等
他們的default生命週期與插件目標的綁定關係可參閱Maven官方文檔:
http://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html#Built-in_Lifecycle_Bindings
什麼是自定義綁定方式?
除了內置綁定之外,用戶還可以本身選擇將某個插件目標綁定到生命週期的某個階段上,這種自定義綁定方式能讓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>
對上述配置進行解釋:
1、在POM的build元素下的plugins子元素中聲明插件的使用,該例中用到的是maven-source-plugin
2、其groupId爲org.apache.maven.plugins,這也是Maven官方插件的groupId,緊接着artifactId爲maven-source-plugin,version爲2.1.1,對於自定義綁定的插件,用戶老是應該聲明一個非快照版本,這樣能夠避免因爲插件版本變化形成的構建不穩定性
3、除了基本的插件座標聲明外,還有插件執行配置,executions下每一個execution子元素能夠用來配置執行一個任務
4、該例中配置了一個id爲attach-sources的任務,經過phrase配置,將其綁定到verify生命週期階段上,再經過goals配置指定要執行的插件目標
對於上述配置的一個細節須要注意
有時候,即便不經過phase元素配置生命週期階段,插件目標也可以綁定到生命週期中去。例如,能夠嘗試刪除上述配置中的phase一行,再次執行mvn verify,仍然能夠看到maven-source-plugin:jar-no-fork得以執行,出現這種現象的緣由是:有不少插件的目標在編寫時已經定義了默認綁定階段。可使用maven-help-plugin查看插件詳細信息,瞭解插件目標的默認綁定階段
如何使用maven-help-plugin插件查看插件目標的默認綁定階段?
執行命令:
mvn help:describe -Dplugin = org.apache.maven.plugins:maven-source-plugin:2.1.1-Ddetail
須要在pom.xml目錄執行此命令
在列出的信息中可能會出現,Bound to phase:package一行,它表示該目標默認綁定的生命週期階段(這裏是package),也就是說,當用戶配置使用maven-source-plugin的jar-no-fork目標的時候,若是不指定phase參數,該目標就會被綁定到package階段
若是多個目標被綁定到同一個階段,它們的執行順序會是怎樣?
當多個目標被綁定到同一個階段,這些插件聲明的前後順序決定了目標的執行順序
關於插件目標的配置
完成了插件和生命週期的綁定,用戶還能夠配置插件目標的參數,進一步調整插件目標所執行的任務,以知足項目的需求。幾乎全部的Maven插件的目標都有一些可配置的參數,用戶能夠經過命令行和POM配置等方式來配置這些參數
如何使用命令行的方式修改插件的配置?
在平常Maven使用中,咱們會常常從命令行輸入並執行Maven命令。在這種狀況下,若是能方便地更改某些插件的行爲,無疑會十分方便
不少插件目標的參數都支持從命令行配置,用戶能夠在Maven命令中使用-D參數,並伴隨一個參數鍵 = 參數值的形式,來配置插件目標的參數
例如,maven-surefire-plugin提供了一個maven.test.skip參數,當其值爲true的時候,就會跳過執行測試,因而,在運行命令的時候,加上以下-D參數就能跳過測試:
mvn install -Dmaven.test.skip=true
這裏的-D參數是什麼意思?
參數-D是Java自帶的,其功能是經過命令行設置一個Java系統屬性,Maven簡單地重用了該參數,在準備插件的時候檢查系統屬性,便實現了插件參數的配置
POM中插件全局配置
並非全部的插件參數都適合從命令行配置,有些參數的值從項目建立到項目發佈都不會改變,或者說不多改變,對於這種狀況,在POM文件中一次性配置就顯然比重複的命令行輸入要方便
好比,咱們一般會須要配置maven-compiler-plugin告訴它編譯Java 1.7版本的源文件,生成與JVM 1.5兼容的字節碼文件
在POM中對插件進行全局配置
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</build>
這樣,無論綁定到compile階段的maven-compiler-plugin:compile任務,仍是綁定到test-compile階段的maven-compiler-plugin:testCompiler任務,就都可以使用該配置,基於Java1.7版本進行編譯
POM中插件任務配置
除了爲插件配置全局的參數,用戶還能夠爲某個插件任務配置特定的參數。以maven-antrun-plugin爲例,它有一個目標run,能夠用來在Maven中調用Ant任務。用戶將maven-antrun-plugin:run綁定到多個生命週期階段上,再加以不一樣的配置,就可讓Maven在不一樣的生命階段執行不一樣的任務
在POM中對插件進行任務配置
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.3</version>
<executions>
<execution>
<id>ant-validate</id>
<phase>validate</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<tasks>
<echo>I'm bound to validate phase.</echo>
</tasks>
</configuration>
</execution>
<execution>
<id>ant-verify</id>
<phase>verify</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<tasks>
<echo>I'm bound to verify phase.</echo>
</tasks>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
對上述配置進行解釋:
1、maven-antrun-plugin:run與validate階段綁定,從而構成一個id爲ant-validate的任務
2、插件全局配置中的configuration元素位於plugin元素下面,而這裏的configuration元素則位於execution元素下,表示這是特定任務的配置,而非插件總體的配置
3、這個ant-validate任務配置了一個echo Ant任務,向命令行輸出一段文字,表示該任務是綁定到validate階段的,第二個任務的id爲ant-verify,它綁定到了verify階段,一樣它也輸出一段文字到命令行,告訴該任務綁定到了verrify階段
關於插件存在的問題說明
僅僅理解如何配置使用插件是不夠的,當遇到一個構建任務的時候,用戶還須要知道去哪裏尋找合適的插件,以幫助完成任務。找到正確的插件以後,還要詳細瞭解該插件的配置點。因爲Maven的插件很是多,並且這其中的大部分沒有完善的文檔,所以,使用正確的插件並進行正確的配置,其實並非一件容易的事
如何在線查找插件信息?
基本上全部的主要的Maven插件都來自Apache和Codehaus,因爲Maven自己是屬於Apache軟件基金會的,所以它有不少官方的插件,天天都有成千上萬的Maven用戶在使用這些插件,它們具備很是好的穩定性
詳細的列表地址:
http://maven.apache.org/plugins/index.html
全部官方插件下載地址:
http://repo1.maven.org/maven2/org/apache/maven/plugins
託管於Codehaus上的Mojo項目
除了Apache上的官方插件以外,託管於Codehaus上的Mojo項目也提供了大量的Maven插件,須要注意的是,這些插件的文檔和可靠性相對較差
詳細列表能夠訪問地址:
http://mojo.codehaus.org/plugins.html
Codehaus的Maven插件下載地址:
http://repository.codehaus.org/org/code-haus/mojo/
另外,上述兩個站點提供的插件很是多,而實際使用中經常使用的插件遠不會是這個數量
使用maven-help-plugin描述插件
除了訪問在線的插件文檔以外,還能夠藉助maven-help-plugin來獲取插件的詳細信息
能夠運行以下命令來獲取maven-compiler-plugin 2.1版本的信息:
mvn help:describe -Dplugin=org.apache.maven.plugins:maven-compiler-plugin:2.1
你可能會發現一行Goal Prefix: compiler,這是目標前綴 (Goal Prefix),其做用是方便在命令行直接運行插件,maven-compiler-plugin的目標前綴是compiler
在描述插件的時候,還能夠省去版本信息,讓Maven自動獲取最新版原本進行表述:
mvn help:describe -Dplugin=org.apache.maven.plugins:maven-compiler-plugin
進一步簡化
mvn help:describe -Dplugin= compiler
若是想僅僅描述某個插件目標的信息,能夠加上goal參數
mvn help:describe -Dplugin= compiler -Dgoal=compile
若是想讓maven-help-plugin輸出更詳細的信息,能夠加上detail參數:
mvn help:describe -Dplugin= compiler -Ddetail
使用mvn -h來顯示mvn命令幫助
你可能會看到以下信息:
usage:mvn [options] [<goals>] [<phase(s)>]
Options:
...
該信息告訴咱們mvn命令的基本用法,options表示可用的選項,mvn命令有20多個選項
除了選項外,mvn命令後面能夠添加一個或者多個goal和phase,它們分別是指插件目標和生命週期階段
爲何Maven支持直接從命令行調用插件目標?
Maven支持這種方式是由於有些任務不適合綁定在聲明週期上
例如maven-help-plugin:describe,咱們不須要在構建項目的時候描述插件信息
又如maven-dependency-plugin:tree,咱們也不須要在構建項目的時候去顯示依賴樹
那麼如何使用上述兩個插件目標?
你能夠在命令行輸入:
mvn help:describe -Dplugin=compiler
mvn dependency:tree
上面的兩條命令有個疑問?
describe是maven-help-plugin的目標沒錯,但冒號前面的help是什麼呢?它既不是groupId、也不是artifactId?
Maven是如何根據該信息找到對應版本插件的呢?
爲何不是maven-dependency-plugin:tree,而是dependency:tree?
嘗試回答上述問題,執行下面兩條命令:
mvn org.apache.maven.plugins:maven-help-plugin:2.1:describe -Dplugin=compiler
mvn org.apache.maven.plugins:maven-dependency-plugin:2.1:tree
這兩條命令比較好理解,插件的groupId、artifactId、version以及goal都得以清晰描述
它們的效果與以前的兩個命令基本是同樣的,但顯然前面的命令更加簡潔
爲了達到該目的,Maven引入了目標前綴的概念
help是maven-help-plugin的目標前綴
dependency是maven-dependency-plugin的前綴
有了插件前綴,Maven就能找到對應的artifactId,不過,除了artifactId,Maven還須要獲得groupId和version才能精肯定位到某個插件,如何解釋這個過程?
Maven插件解析機制
爲了方便用戶使用和配置插件,Maven不須要用戶提供完整的插件座標信息,就能夠解析獲得正確的插件,Maven的這一特性是一把雙刃劍,雖然它簡化了插件的使用和配置,可一旦插件的行爲出現異常,用戶就很難快速定位處處問題的插件構件
例如mvn help:system這樣一條命令,它到底執行了什麼插件?該插件的groupId、artifactId和version分別是什麼?這個構件是從哪裏來的?等等問題
插件倉庫
與依賴構件同樣,插件構件一樣基於座標存儲在Maven倉庫中,在須要的時候,Maven會從本地倉庫尋找插件,若是不存在,則從遠程倉庫查找,找到插件以後,再下載到本地倉庫使用
Maven會區別對待依賴的遠程倉庫和插件的遠程倉庫
1、當Maven須要的依賴在本地倉庫不存在時,它會去所配置的遠程倉庫查找
2、當Maven須要的插件在本地倉庫不存在時,它就不會去這些遠程倉庫查找
3、不一樣於repositories及其repository子元素,插件的遠程倉庫使用pluginRepositories和PluginRepository配置
Maven內置的插件倉庫配置
<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>
關於插件倉庫的說明
1、通常中央倉庫所包含的插件徹底夠用,不須要配置其餘插件倉庫
插件的默認groupId
1、在POM中配置插件的時候,若是該插件是Maven的官方插件,即(若是其groupId爲org.apache.maven,plugins),就能夠省略groupId配置。好比像下面這樣:
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</build>
2、不推薦使用這一機制
Maven的解析插件版本機制
一樣是爲了簡化插件的配置和使用,在用戶沒有提供插件版本的狀況下,Maven會自動解析插件版本
1、Maven在超級POM中爲全部核心插件設定了版本,超級POM是全部Maven項目的父POM,全部項目都繼承這個超級POM的配置,所以,即便用戶不加任何配置,Maven使用核心插件的時候,它們的版本就已經肯定了。這些插件包括maven-clean-plugin、maven-compiler-plugin、maven-surefire-plugin等
2、若是用戶使用某個插件時沒有設定版本,而這個插件又不屬於核心插件的範疇,Maven就會去檢查全部的倉庫中可用的版本,而後作出選擇,以maven-compiler-plugin爲例,它在中央倉庫的倉庫元數據爲:
http://repo1.maven.org/maven2/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.0</version>
<version>2.0.2</version>
<version>2.1</version>
</versions>
<lastUpdated>20100102092331</lastUpdated>
</versioning>
</metadata>
3、Maven遍歷本地倉庫和全部的遠程插件倉庫,將該路徑下的倉庫元數據歸併後,就能計算出lastest和release的值。lastest表示全部倉庫中該構件的最新版本,而release表示最新的非快照版本。在Maven 2中,插件的版本會被解析至latest,也就是說,當用戶使用某個非核心插件且沒有聲明版本的時候,Maven會將版本解析爲全部可用倉庫中的最新版本,而這個版本也多是快照版本
4、當插件的版本爲快照版本時,就會出現潛在的問題。Maven會基於更新的策略,檢查並使用快照的更新。某個插件可能昨天還用的好好的,今天就出錯了,其緣由就是這個快照版本的插件發生了變化。爲了防止這類問題,Maven3調整了解析機制,當插件沒有聲明版本的時候,再也不解析至latest,而是使用release。這樣就能夠避免因爲快照頻繁更新而致使的插件行爲不穩定
5、依賴Maven解析插件版本實際上是不推薦的作法,即便Maven 3將版本解析到最新的非快照版,也仍是會有潛在的不穩定性。例如,可能某個插件發佈了一個新的版本,而這個版本的行爲與以前的版本發生了變化,這種變化可能致使項目構建失敗。所以,使用插件的時候,應該一直顯式地設定版本,這也解釋了Maven爲何要在超級POM中爲核心插件設定版本
Maven如何解析插件前綴?
前面說到mvn命令行支持使用插件前綴來簡化插件的調用,如今解釋Maven如何根據插件前綴解析獲得插件的座標
插件前綴與groupId:artifactId是一一對應的,這種匹配關係存儲在倉庫元數據中。與以前提到的groupId/artifactId/maven-metadata.xml不一樣,這裏的倉庫元數據爲groupId/maven-metadata.xml,那麼這裏的groupId是什麼呢?
咱們知道,插件都位於如下兩個地址:
http://repo1.maven.org/maven2/org/apache/maven/plugins
http://repository.codehaus.org/org/code-haus/mojo/
相應地,Maven在解析插件倉庫元數據的時候,會默認使用org.apache.maven.plugins和org.codehaus.mojo兩個groupId,也能夠經過配置settings.xml讓Maven檢查其餘groupId上的插件倉庫元數據:
<settings>
<pluginGroups>
<pluginGroup>com.your.plugins</pluginGroup>
</pluginGroups>
</settings>
基於該配置,Maven就不只僅會檢查org/apache/maven/plugins/maven-metadata.xml和org/codehaus/mojo/maven-metadata.xml,還會檢查com/your/plugins/maven-metadata.xml
插件倉庫元數據
<metadata>
<plugins>
<plugin>
<name>Maven Clean Plugin</name>
<prefix>clean</prefix>
<artifactId>maven-clean-plugin</artifactId>
</plugin>
<plugin>
<name>Maven Compiler Plugin</name>
<prefix>compiler</prefix>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<name>Maven Dependency Plugin</name>
<prefix>dependency</prefix>
<artifactId>maven-dependency-plugin</artifactId>
</plugin>
</plugins>
</metadata>
對於上述配置的解釋:
1、上述內容是從中央倉庫的org.apache.maven.plugins.groupId下插件倉庫元數據中截取的一些片斷,從這段數據中就能看到maven-clean-plugin的前綴爲clean,maven-compiler-plugin的前綴爲compiler,maven-dependency-plugin的前綴爲dependency
Maven是如何解析dependency:tree這樣的命令的?
1、當Maven解析到dependency:tree這樣的命令後,它首先基於默認的groupId歸併全部插件倉庫的元數據org/apache/maven/plugins/maven-metadata.xml
2、其次檢查歸併後的元數據,找到對應的artifactId爲maven-dependency-plugin;而後結合當前元數據的groupId org.apache.maven.plugins,而後經過上面的解析插件版本,解析到version,這時就獲得了完整的插件座標
3、若是org/apache/maven/plugins/maven-metadata.xml沒有記錄該插件的前綴,則接着檢查其餘groupId下的元數據,如org/codehaus/mojo/maven-metadata.xml,以及用戶自定義的插件組,若是全部的元數據中都不含該前綴,則報錯