Maven構建的生命週期詳解
一、構建生命週期基礎:
Maven基於構建生命週期的中心概念。這意味著構建和分發特定工件(專案)的過程被明確定義。
對於構建專案的人員,這意味著只需要學習一小堆命令即可構建任何Maven專案,POM將確保他們獲得所需的結果。
有三個內建的生命週期:預設(default),清潔(clean)和站點(site)。在預設(default)的生命週期處理你的專案部署,將清潔(clean)的生命週期處理專案的清理,而網站(site)的生命週期處理你的專案站點文件的建立。
二、構建生命週期是由階段組成的:
這些構建生命週期中的每一個由構建階段的不同列表定義,其中構建階段表示生命週期中的階段。
例如,預設(default)的生命週期包括以下階段(注意:這裡是簡化的階段,用於生命週期階段的完整列表,請參閱下方生命週期參考):
驗證(validate) - 驗證專案是否正確,所有必要的資訊可用
編譯(compile) - 編譯專案的原始碼
測試(test) - 使用合適的單元測試框架測試編譯的原始碼。這些測試不應該要求程式碼被打包或部署
打包(package) - 採用編譯的程式碼,並以其可分配格式(如JAR)進行打包。
驗證(verify) - 對整合測試的結果執行任何檢查,以確保滿足質量標準
安裝(install) - 將軟體包安裝到本地儲存庫中,用作本地其他專案的依賴項
部署(deploy) - 在構建環境中完成,將最終的包複製到遠端儲存庫以與其他開發人員和專案共享。
這些生命週期階段(以及此處未顯示的其他生命週期階段)依次執行,以完成預設生命週期。給定上述生命週期階段,這意味著當使用預設生命週期時,Maven將首先驗證專案,然後嘗試編譯原始碼,執行這些原始碼,打包二進位制檔案(例如jar),執行整合測試軟體包,驗證整合測試,將驗證的軟體包安裝到本地儲存庫,然後將安裝的軟體包部署到遠端儲存庫。
換句話說,在生命週期裡面階段是連續的,在不出錯的前提下,比如執行打包(package)時就一定是執行了測試(test)之後再執行。
三、生命週期參考:
以下列出了預設(default),清潔(clean)和站點(site)生命週期的所有構建階段,它們按照指定的順序執行的順序執行。
清潔(clean):生命週期
預清潔(pre-clean): 執行實際專案清理之前所需的流程
清潔(clean): 刪除以前構建生成的所有檔案
後清潔(post-clean): 執行完成專案清理所需的流程
預設(default)生命週期
驗證(validate):驗證專案是正確的,所有必要的資訊可用。
初始化(initialize):
產生來源(generate-sources): 生成包含在編譯中的任何原始碼。
流程源(process-sources): 處理原始碼,例如過濾任何值。
生成資源(generate-resources):生成包含在包中的資源。
流程資源(process-resources): 將資源複製並處理到目標目錄中,準備打包。
編譯(compile): 編譯專案的原始碼。
工藝類(process-classes): 從編譯後處理生成的檔案,例如對Java類進行位元組碼增強。
生成測試來源(generate-test-sources): 生成包含在編譯中的任何測試原始碼。
流程測試來源(process-test-sources): 處理測試原始碼,例如過濾任何值。
生成測試資源(generate-test-resources):建立測試資源。
流程測試資源(process-test-resources):將資源複製並處理到測試目標目錄中。
測試編譯(test-compile):將測試原始碼編譯到測試目標目錄中
流程檢驗類(process-test-classes):從測試編譯中處理生成的檔案,例如對Java類進行位元組碼增強。對於Maven 2.0.5及以上版本。
測試(test): 使用合適的單元測試框架執行測試。這些測試不應該要求程式碼被打包或部署。
製備包(prepare-package):在實際包裝之前,執行必要的準備包裝的操作。這通常會導致打包的處理版本的包。(Maven 2.1及以上)
打包(package):採取編譯的程式碼,並以其可分發的格式(如JAR)進行打包。
預整合測試(pre-integration-test):在執行整合測試之前執行所需的操作。這可能涉及諸如設定所需環境等。
整合測試(integration-test):如果需要,可以將該包過程並部署到可以執行整合測試的環境中。
整合後的測試(post-integration-test):執行整合測試後執行所需的操作。這可能包括清理環境。
校驗(verify): 執行任何檢查以驗證包裝是否有效並符合質量標準。
安裝(install):將軟體包安裝到本地儲存庫中,以作為本地其他專案的依賴關係。
部署(deploy):在整合或釋出環境中完成,將最終軟體包複製到遠端儲存庫,以與其他開發人員和專案共享。
站點(site)生命週期
預網站(pre-site): 在實際的專案現場生成之前執行所需的程序
網站(site):生成專案的站點文件
後網站(post-site):執行完成站點生成所需的程序,並準備站點部署
網站部署(site-deploy): 將生成的站點文件部署到指定的Web伺服器
四、生命週期階段在命令列中的呼叫
在開發環境中,使用以下呼叫構建並將工件安裝到本地儲存庫中。
mvn install
此命令在執行安裝之前按順序(驗證(validate),編譯(compile),打包(package)等)執行每個預設生命週期階段。在這種情況下,您只需要呼叫最後一個構建階段來執行,安裝(install)。
在構建環境中,使用以下呼叫將工件清理地構建並部署到共享儲存庫中。
mvn clean deploy
相同的命令可以在多模組場景(即具有一個或多個子專案的專案)中使用。Maven遍歷每個子專案並執行清潔(clean),然後執行部署(deploy)(包括所有之前的構建階段步驟)。
注意:在我們開發階段,有一些生命週期的階段,比如驗證(validate)這些,基本很少用到。只要使用關鍵的幾個基本能滿足需求。
五、通常情況在命令列只調用某些特定的階段
以連字元(pre-,post-或process-*)命名的階段通常不會從命令列直接呼叫。這些階段對構建進行排序,生成在構建之外無用的中間結果。在呼叫整合測試的情況下,環境可能處於掛起狀態。
諸如Jacoco和執行容器外掛(如Tomcat,Cargo和Docker)的程式碼覆蓋工具將目標繫結到預整合測試(pre-integration-test)階段以準備整合測試容器環境。這些外掛還將目標繫結到整合後的測試(post-integration-test)階段,以收集覆蓋統計資訊或停止整合測試容器。
故障安全和程式碼覆蓋外掛將目標繫結到整合測試(integration-test)和驗證(verify)階段。最終結果是在驗證(verify)階段後可以使用測試和覆蓋率報告。如果從命令列呼叫整合測試(integration-test)階段,則不會生成任何報告。整合測試容器環境處於更糟糕的懸掛狀態; Tomcat網路伺服器或Docker例項將保持執行,並且Maven本身甚至可能不會終止。
提示:再次明確,在生命週期的階段上,只有特定的幾個階段對於構建有意義。一些無用的階段只起到了中間階段的作用,換句話說只是一個過客。
六、呼叫由外掛目標(Plugin Goals)組成的構建階段
然而,即使構建階段負責構建生命週期中的特定步驟,其執行這些職責的方式可能會有所不同。這是通過宣告繫結到這些構建階段的外掛目標來完成的。
外掛目標代表一個特定的任務(比構建階段更精細),有助於專案的構建和管理。它可能被限制在零個或多個構建階段。不限於任何構建階段的目標可以通過直接呼叫在構建生命週期之外執行。執行順序取決於呼叫目標和構建階段的順序。例如,考慮下面的命令。該清潔(clean)和打包(package)是建立階段,而dependency:copy-dependencies是(一個外掛)的目標。
mvn clean dependency:copy-dependencies package
如果要執行此操作,則將首先執行清潔(clean)階段(意味著它將執行清潔生命週期的所有先前階段以及清潔(clean)階段本身),然後執行dependency:copy-dependencies目標,然後才能最終執行打包(package)階段(以及預設生命週期的所有之前的構建階段)。
而且,如果一個目標被繫結到一個或者多個構建階段,那麼在所有這些階段都會呼叫這個目標。
此外,構建階段也可以有零個或多個目標。如果構建階段沒有繫結目標,則構建階段將不會執行。但是,如果它有一個或多個目標,它將執行所有這些目標(注意:在Maven 2.0.5及更高版本中,繫結到階段的多個目標的執行順序與POM中宣告的順序相同,但不支援同一外掛的多個例項,同一個外掛的多個例項被分組一起執行要在Maven 2.0.11及以上)。
提示:其實簡單點理解就是說dependency是一個外掛,在我們執行生命週期階段時,可以呼叫這個外掛做特定的事,其中copy-dependencies就是特定的事,那麼上面的命令可以這麼理解,在執行clean後就會執行dependecy這個外掛,最後再執行package;如果dependecy這個外掛執行過程異常,package就不會執行到。還有就是一個命令可以有多個外掛,也可以一個外掛都沒有。
提示2:dependency:copy-dependencies這樣的形式最標準的官方說法:左邊dependency為外掛,右邊copy-dependencies為外掛的目標,通常還有一種說法就是命令列引數。
七、使用構建生命週期來設定專案
構建生命週期足夠簡單,但是當您為專案配置Maven構建時,您如何將任務分配到每個構建階段?
打包
第一個也是最常見的方法是通過同樣命名的POM元素為您的專案設定打包。一些有效的打包值是jar,war,ear和pom。如果沒有指定包裝值,它將預設為jar。
每個不同型別的打包都包含要繫結到特定階段的目標列表。例如,jar包將繫結以下目標來構建預設生命週期的階段。
流程資源(process-resources): resources:resources
**編譯(compile):**compiler:compile
流程測試資源(process-test-resources): resources:testResources
測試編譯(test-compile): compiler:testCompile
測試(test): surefire:test
打包(package) : jar:jar
安裝(install) : install:install
部署(deploy): deploy:deploy
這幾乎是一個標準的繫結 ; 然而,一些包裝處理它們不同。例如,純粹的元資料(包裝值是pom)的專案只將目標繫結到安裝(install)和部署(deploy)階段(對於某些包裝型別的目標到構建階段繫結的完整列表,請參閱生命週期參考)。
請注意,對於某些可用的打包型別,您可能還需要在POM的部分中包含一個特定的外掛,併為該外掛指定true。需要這種外掛的一個例子是Plexus外掛,它提供plexus-application和plexus-service打包。
提示:這裡簡單點可以說不同的包對應不同的生命週期階段,比如jar包和war包的區別可以參考:https://maven.apache.org/ref/3.5.0/maven-core/default-bindings.html。上方列表可以這麼理解,左邊是簡化的命令,右側是詳細的外掛加目標(命令列引數)的形式;切記,Maven都是以外掛的形式存在的,包括生命週期的階段同樣也是一個個不同的外掛組成,比如上面的編譯(compile)就是由compiler外掛提供,其中compile為這個外掛的目標,也可以說是外掛的命令列引數。
外掛
將目標新增到階段的第二種方法是在專案中配置外掛。外掛是為Maven提供目標的工件。此外,外掛可以具有一個或多個目標,其中每個目標代表該外掛的能力。例如,編譯器(compiler)外掛有兩個目標:compile和testCompile。前者編譯主程式碼的原始碼,後者編譯測試程式碼的原始碼。
如稍後部分所示,外掛可以包含指示將目標繫結到的生命週期階段的資訊。請注意,自己新增外掛是不夠的,您還必須指定要作為構建的一部分執行的目標。
配置的目標將被新增到已經從選定的打包繫結到生命週期的目標。如果將多個目標繫結到特定階段,則使用的順序是首先執行來自打包裝的順序,然後執行在POM中配置。請注意,您可以使用元素來獲得對特定目標的順序更多的控制。
例如,Modello外掛預設將目標modello:java繫結到generate-sources階段(注意:modello:java目標生成Java原始碼)。因此,要使用Modello外掛,並從模型生成原始碼並將其合併到構建中,您可以在的部分中將以下內容新增到POM中:
<plugin>
<groupId>org.codehaus.modello</groupId>
<artifactId>modello-maven-plugin</artifactId>
<version>1.8.1</version>
<executions>
<execution>
<configuration>
<models>
<model>src/main/mdo/maven.mdo</model>
</models>
<version>4.0.0</version>
</configuration>
<goals>
<goal>java</goal>
</goals>
</execution>
</executions>
</plugin>
提示:這裡所說的更直白的意思就是Modello外掛有預設的生命週期階段,而無需自己手動配置這些階段。
你可能會想知道為什麼這個元素在那裡。這樣,如果需要,您可以使用不同的配置多次執行相同的目標。還可以使用單獨的執行標識,以便在繼承或應用配置檔案期間,您可以控制目標配置是合併還是轉為額外的執行。
當給出與特定階段匹配的多個執行時,它們按照POM中指定的順序執行,繼承的執行首先執行。
現在,在modello:java的情況下,它只在generate-sources階段才有意義。但是一些目標可以在一個以上的階段中使用,也可能沒有合理的預設。對於那些,您可以自己指定階段。例如,假設您有一個目標display:time當前時間到命令列的時間,並希望它在process-test-resources階段執行以指示測試何時開始。這將被配置如下:
<plugin>
<groupId>com.mycompany.example</groupId>
<artifactId>display-maven-plugin</artifactId>
<version>1.0</version>
<executions>
<execution>
<phase>process-test-resources</phase>
<goals>
<goal>time</goal>
</goals>
</execution>
</executions>
</plugin>
提示:這裡已經是換了一個外掛了,與上面的外掛不同的是,可以在中指定生命週期階段。可以說是兩個做了一個明顯對比。
八、內建生命週期的繫結
預設情況下,某些階段的目標與之相關。對於預設生命週期,這些繫結取決於包裝值。以下是一些目標到構建階段的繫結。
清潔生命週期繫結
清潔(clean): clean:clean
預設生命週期繫結-打包ejb/ejb3/jar/par/rar/war
流程資源(process-resources) : resources:resources
編譯(compile): compiler:compile
流程測試資源(process-test-resources): resources:testResources
測試編譯(test-compile) : compiler:testCompile
測試(test): surefire:test
打包(package): ejb:ejb or ejb3:ejb3 or jar:jar or par:par or rar:rar or war:war
安裝(install) : install:install
部署(deploy) : deploy:deploy
預設生命週期繫結-打包ear
生成資源(generate-resources): ear:generate-application-xml
流程資源(process-resources): resources:resources
打包(package): ear:ear
安裝(install) : install:install
部署(deploy) : deploy:deploy
預設生命週期繫結-打包maven-plugin
生成資源(generate-resources): plugin:descriptor
流程資源(process-resources): resources:resources
編譯(compile) : compiler:compile
流程測試資源(process-test-resources): resources:testResources
測試編譯(test-compile) : compiler:testCompile
測試(test): surefire:test
打包(package): jar:jar and plugin:addPluginArtifactMetadata
安裝(install): install:install
部署(deploy): deploy:deploy
預設生命週期繫結-包裝pom
打包(package): site:attach-descriptor
安裝(install) : install:install
部署(deploy) : deploy:deploy
網站生命週期繫結
網站(site): site:site
網站部署(site-deploy): site:deploy
九、一些參考
完整的Maven生命週期由maven-core模組中的components.xml檔案定義,並附有相關文件供參考。
Maven中2.x中,預設的生命週期的繫結被納入的components.xml,但在Maven的3.x中,它們在一個單獨的被定義default-bindings.xml描述符。
請參閱生命週期參考和外掛繫結,以獲取直接從原始碼獲取的最新文件的預設生命週期參考。
總結:
1、整體來自官方文件,中文來自谷歌翻譯,文中難免有一些直譯的錯誤,但是不影響理解。
2、文中的提示為自己的個人理解。
3、在理解生命週期時市面上的說法沒有官方來的更直接更明瞭。
4、切記所有的生命週期的階段都是以外掛的形式存在的,或者這麼說吧,Maven的一切就是一個大外掛包集合。
5、文中提到的目標,我理解成了命令列引數,比如dependency:copy-dependencies整體就是叫做目標,而我喜歡把copy-dependencies叫做目標,dependency叫做外掛。