除了座標、依賴和倉庫以外,Maven另外兩個核心概念是聲明週期和插件。Maven的生命週期是抽象的,其實際行爲都由插件來完成,如package階段的任務可能有maven-jar-plugin完成。生命週期和插件二者協同工做,密不可分。 ###7.1 何爲生命週期### Maven的生命週期就是爲了對全部的構建過程進行抽象和統一,生命週期包含了項目的清理,初始化,編譯,測試,打包,集成測試,驗證和站點生成等幾乎全部構建步驟,幾乎全部項目的構建,可以映射到這樣一個生命週期上。生命週期是抽象的,這也意味着生命週期自己不作任何實際的工做,在Maven設計中,實際的任務都是交由插件完成的,這種思想和設計模式中的模板方法很是相似,模板方法模式在父類中定義算法的總體結構,子類能夠經過實現或者重寫父類的方法來控制實際的行爲,這樣既保證了算法有足夠的可擴展性,又可以嚴格控制算法的總體結構,以下的模板方法抽象類可以很好的體現Maven生命週期的概念:html
Public void build(){ initialize(); compile(); test(); packagee(); integrationTest();deploy(); } protected abstract void initialize(); protected abstract void compile(); protected abstract void test(); protected abstract void packagee(); protected abstract void integrationTest(); protected abstract void deploy();
build()方法定義了整個構建的過程,依次是初始化,編譯,測試,打包,集成測試和部署,可是這個類沒有具體實現,他們交由之類去實現。Maven爲大多數構建步驟編寫並綁定了默認插件,例如:針對編譯的插件有maven-compiler-plugin,針對測試的插件有maven-surefire-plugin等。用戶幾乎察覺不到插件的存在,但實際上編譯由maven-compiler-plugin完成的,而測試由maven-surefire-plugin完成的,當有特殊須要的時候,也能夠配置插件定製構建行爲,甚至本身編寫插件。
Maven定義的生命週期和插件機制一方面保證了全部Maven項目有一致的構建標準,另外一方面又經過默認插件簡化和穩定了實際項目的構建,此外,該機制還提供了足夠的擴展空間,用戶能夠經過配置現有插件或者自行編寫插件來自定義構建行爲。 ###7.2 生命週期詳解### ####7.2.1 三套生命週期#### 生命週期分爲相互獨立的三大生命週期:分別是clean,default和site,clean是清理項目,default是構建項目,site是創建項目站點,每一個生命週期包含一些階段,這些階段是有順序的,後面階段依賴於前面的階段,Maven就是調用了這些生命週期階段。 ####7.2.2 clean生命週期#### clean生命週期的目的是清理項目,它包括以下三個階段:java
<bulid> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-source-plugin</artifactId> <version>2.1.1</version> <executons> <execution> <id>attach-sources</id> <phase>verify</phase> <goals> <goal>jar-no-fork</goal> </goals> </execution> </executons> </plugin> </plugins> </bulid>
在POM的build元素下的plugins子元素中聲明插件的使用,該例中用到的是maven-source-plugin,其groupId爲org.apache.maven.plugins,而artifactId爲maven-source-plugin,version爲2.1.1,對於自定義綁定的插件,用戶老是應該聲明一個非快照版本,這樣能夠避免因爲插件版本變化形成的構建不穩定性。executions下每個execution子元素能夠用來配置執行一個任務,id爲attach-sources的任務,phrase配置將其綁定到verify生命週期階段上,再經過goals配置指定要執行的插件目標,接着運行mvn verify結果會建立一個-sources.jar結尾的源碼文件包。有時候無需配置verify也能將jar-no-fork綁定到verify中,執行mvn verify也沒有問題,出現這樣的緣由是在不少插件的目標在編寫時已經定義默認綁定階段,可使用maven-help-plugin查看插件詳細信息,查看插件的默認綁定規範,運行:mvn help:describe - Dplugin = org.apache.maven.plugins: maven-source-plugin:2.1.1-Ddetail 該命令輸出對應的插件的詳細信息,在輸出中能夠看到關於目標jar-no-fork信息以下:Bound to phase爲package,也就是若是不指定則默認綁定到package階段。當插件目標綁定到不一樣的生命週期階段的時候,其執行順序會由生命週期的前後順序決定,若是多個目標綁定到同一階段,他們的順序爲插件聲明的前後順序。 ###7.5 插件配置### 用戶還能夠配置插件目標的參數,進一步調整插件目標所執行的任務,幾乎全部的Maven插件目標均可以配置參數。經過命令行和POM配置這些參數。 ####7.5.1 命令行插件配置#### 在Maven命令行中使用-D參數,並伴隨一個參數鍵=參數值的形式,來配置插件目標的參數。例如maven-surefire-plugin提供了maven.test.skip參數,爲true則跳過執行測試,命令爲:$mvn install -Dmaven.test.skip = true ####7.5.2 POM中插件全局變量配置#### 有些參數的值從項目建立到發佈都不會改變,此時在POM文件中一次性配置就比重複輸入來的方便,用戶也能夠對此插件作一個全局的配置,那麼全部該插件目標的任務都會使用這些配置,例如咱們一般須要配置maven-compiler-plugin告訴他編譯java1.5版本的源文件,生成與JVM1.5兼容的字節碼文件:算法
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>2.1</version> <configuration> <source>1.5</source> <target>1.5</target> </configuration> </plugin> </plugins> </build>
此時不論是主代碼編譯仍是測試代碼編譯都可以使用該配置,基於java1.5版本進行編譯。 ####7.5.3 POM中插件任務配置#### 用戶能夠爲某個插件任務配置特定的參數,以maven-antrun-plugin爲例,他有一個目標run,能夠用來在Maven中調用Ant任務,用戶將這個插件的run綁定到多個生命週期階段上,再加以不一樣的配置,就可讓Maven在不一樣的生命階段執行不一樣的任務。apache
<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 validate phase</echo> </tasks> </configuration> </execution> </executions> </plugin> </plugins> </build>
插件全局配置中的configuration元素位於plugin元素下面,而這裏的configration元素則位於execution元素下,表示這是特定任務的配置,而非插件總體的配置。這個ant-validate任務配置了一個echo Ant任務,向命令行輸出一段文字,表示該任務是綁定到validate階段的。第二個任務的id爲ant-verify,它綁定到了verify階段,一樣它也輸出一段文字到命令行,告訴該任務綁定到了verify階段。 ###7.6 獲取插件信息### 當用戶遇到一個構建任務的時候,用戶須要知道去哪裏尋找合適的插件,找到以後還要了解該插件的配置點,因爲插件文檔很少,使用正確的插件進行配置很不容易。 ####7.6.1 在線插件信息#### 基本上Maven插件的信息來自Apache和Codehaus,地址爲: http://maven.apache.org/plugins/index.html, 插件下載地址: http://repol.maven.org/maven2/org/apache/maven/plugins。還有就是Codehaus的 http://mojo.codehaus.org/plugins.html,下載地址爲 http://repository.codehaus.org/org/codehaus/mojo/
####7.6.2 使用maven-help-plugin插件描述#### 除了文檔還能夠藉助maven-help-plugin來獲取插件的詳細信息,能夠運行以下命令:$mvn help:describe -Dplugin = org.apache.maven.plugins:maven-compiler-plugin:2.1,也就是執行maven-help-plugin的describe目標,結果中列出了插件的座標,目標前綴和目標等,目標前綴爲了在命令行直接運行插件,maven-compiler-plugin的前綴爲compiler,在描述插件時能夠省去版本信息,進一步簡化爲 $mvn help:describe -Dplugin = compiler,只想要了解某個目標信息能夠加上goal參數:如$mvn help:describe -Dplugin = compiler -Dgoal = compile 想要更詳細信息則加上detail參數:$mvn help:describe -Dplugin = compiler -Ddetail ###7.7 從命令行調用插件### 經過命令行運行mvn -h 來顯示mvn命令幫助,就會看到以下信息 usage :mvn [options] [<goal(s)>] [<phase(s)>] options表示可用的選項,命令能夠添加一個或多個goal和phase分別指插件目標和生命週期階段,咱們知道能夠經過mvn命令激活生命週期階段,從而執行綁定在生命週期階段上插件目標,Maven還支持命令行調用插件目標,由於有些任務不適合綁定在生命週期上,如maven-help-plugin:describe不須要描述插件信息,又如maven-dependency-plugin:tree不須要顯示依賴樹,所以插件目標應該經過以下方式使用$mvn help:describe-Dplugin = compiler 等同於$mvn org.apache.maven.plugins:maven-help-plugin:2.1:describe -Dplugin = compiler $mvn dependency: tree 等同於 $mvn org.apache.maven.plugins:maven-dependency-plugin:2.1:tree上面兩行中前面的命令比較整潔,這就是Maven引入了前綴的概念,help是maven-help-plugin的目標前綴,dependency是maven-dependency-plugin的前綴,有了前綴Maven就能找到對應的artifactId,除了artifactIdMaven還須要獲得groupId和version才能精肯定位到某個插件,雖然簡化了命令可是當執行mvn help:system這樣的命令,就不知道他到底執行了什麼插件,該插件的groupId,artifactId和version分別是什麼,接下來介紹其插件倉庫的機制 ###7.8 插件解析機制### ####7.8.1 插件倉庫#### 與依賴構件同樣,插件構件一樣基於座標存儲在Maven倉庫中。在須要的時候,Maven會從本地倉庫尋找插件,若是不存在,則從遠程插件倉庫查找。找到插件以後,再下載到本地倉庫使用。
注:依賴的遠程倉庫 != 插件的遠程倉庫,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>
####7.8.2 插件的默認groupId#### Maven針對其官方的插件提供了一種簡單的配置策略,其官方的groupId爲:org.apache.maven.plugins,在配置的時候能夠省略該配置,Maven在解析該插件的時候,會自動用默認的groupId補齊。可是通常狀況下不推薦此種用法,由於只剩下了一行配置,並且容易對新手形成費解。 ####7.8.3 解析插件版本### 在用戶沒有提供插件版本時,Maven會自動解析插件版本,覺得Maven項目的POM默認的父類都是超級POM,超級POM中全部核心插件都設定了版本,即便用戶不輸入,也會默認繼承父類的值。那若是這個插件沒有設定版本同時也不屬於核心插件的範疇,Maven就會去檢查倉庫中可用的版本,倉庫的元數據爲插件目錄下的maven-metadata.xml,Maven遍歷本地和全部遠程倉庫,進行合併,能算出latest和release,前者表示最新版本後者表示最新非快照版本,Maven會解析爲倉庫中的最新版本,而這個版本可能就是快照版,此時就會又潛在問題,爲了解決這個問題,Maven調整了解析機制,當插件沒有聲明版本,則使用release,即便這樣若是舊的版本和新的版本發生了很大變化,此時也可能致使構建失敗,全部建議在使用插件的時候一直顯示的設定版本,這也解釋了Maven爲何要在超級POM中爲核心插件設定版本。 ####7.8.4 解析插件前綴#### 解釋Maven如何經過插件前綴解析獲得插件的座標 插件前綴與groupId:artifactId是一一對應的,這種匹配關係儲存在倉庫元數據中,與前面提到的groupId/artifactId/maven-metadata.xml不一樣,這裏的倉庫元數據是groupId/maven-metadata.xml,這裏的groupId是什麼?由於主要的插件位於apache/maven/plugins/和repository.codehaus.org/org/codehaus/mojo/相應的,Maven在解析插件倉庫元數據時候,會默認使用org.apache.maven/plugins和org.codehaus.mojo兩個groupId,也能夠經過配置settings.xml讓Maven檢查其餘groupId上的插件倉庫元數據:服務器
<settings> <pluginGroups> <plugGroup>com.your.plugins</plugGroup> </pluginGroups> </settings>
此時Maven不只僅會查詢以前的兩個插件地址,還會檢查com/your/plugins/maven-metadata.xml,倉庫元數據內容:框架
<metadata> <plugins> <plugin> <name> Maven Dependency Plugin</name> <prefix>dependency</prefix> <artifactId>maven-dependency-plugin<artifactId> </plugin> </plugins> </metadata>
上面那行的內容能夠看到maven-dependency-plugin的前綴就是dependency,當執行dependency:tree命令時,首先會基於默認的groupId歸併全部插件倉庫的元數據org/apache/maven/plugins/maven-metadata.xml,其次找到對應的artifactId爲maven-dependency-plugin,而後結合當前元數據groupId org.apache.maven.plugins,最後解析獲得version,這時候就獲得了完整的插件座標,若是沒有記錄則接着去org/codehaus/mojo/maven-metadata.xml,以及用戶自定義的插件組,若是全部元數據中都不包含該前綴,就會報錯。maven