maven至今仍是Java編程語言構建的事實標準,大部分項目還在使用maven來進行構建,所以瞭解maven內部運行的原理對定位和分析問題仍是頗有裨益的。本篇文章主要介紹一些maven內部運行過程當中的一些基本概念,相信看完後,對那麼些剛剛接觸maven的讀者來講maven將再也不陌生。
在具體分析項目構建的過程前,須要瞭解maven的一些基本概念,這些概念十分重要,請務必理解清楚後再看下文。基本概念主要有:POM,Lifecycle。這兩個概念又會包含一些小的概念,下文會逐步講解。
POM: 注意這裏的POM不是maven中構建過程使用的配置文件pom.xml,但他們之間仍是有聯繫的。POM的全稱是Project Object Model,用通俗點的話說就是對要構建的項目進行建模,將要構建的項目當作是一個對象(Object),後文就使用PO來指代這個對象。既然是一個對象,那麼PO有哪些屬性呢?在maven中一個項目都是用一個惟一的座標(coordinate)來表示,座標由groupId, artifactId, version, classifier, type這五部分組成。這樣來講PO應該也要具有座標屬性。另一方面,一個項目確定不是孤立存在的,可能依賴於其餘的一些項目,那麼也就是說PO應該具有dependencies這個屬性,用來表示其所依賴的外部項目。咱們能夠嘗試一下用Java代碼來描述下PO這個對象:java
class PO{ private String groupId; private String artifactId; private String version; private String classifier; private String type; private Set<PO> dependencies; }
咱們知道XML的表達能力是很強大的,一個Java中的對象是能夠用XML來描述,例如一個上面定義的PO對象則能夠用下面的XML來描述(表達有不規範之處,理解其中的含義便可):apache
<PO> <groupId></groupId> <artifactId></artifactId> <version></version> <classifier><classifier> <type></type> <dependencies> <PO></PO> <PO></PO> ... </dependencies> </PO>
是否是好像和什麼有點相似?對,就是pom.xml。pom.xml就是PO對象的XML描述。上面的PO定義還不完整,咱們繼續看下PO還有什麼其餘的屬性。咱們知道在Java中類是能夠繼承的,一個對象在建立的時候會同時建立其父類對象,相似的,PO對象也有其父對象,用parent屬性來表示,而且PO對象會繼承其父對象的全部屬性。另一方面,一個項目可能根據不一樣職責分爲多個模塊(module),全部模塊其實也就是一個單獨的項目,只不過這些項目會使用其父對象的一些屬性來進行構建。咱們將這些新的屬性加到PO的定義中去:編程
class PO{ private String groupId; private String artifactId; private String version; private String classifier; private String type; private Set<PO> dependencies; private PO parent; private Set<PO> modules; }
再將這個定義用XML語言表示一下:maven
<PO> <parent></parent> <groupId></groupId> <artifactId></artifactId> <version></version> <classifier><classifier> <type></type> <dependencies> <PO></PO> <PO></PO> ... </dependencies> <modules> ... </modules> </PO>
是否是愈來愈像pom.xml了?對,這就是pom.xml的由來。再說一遍:pom.xml就是PO對象的XML描述。到此爲止,相信你再看pom.xml文件時,會有一個全新的認識。編程語言
上面說的這些PO屬性是項目的一些固有屬性,到目前爲止,咱們尚未涉及項目的構建過程。構建過程對應的是PO對象的build屬性,那麼你應該立刻想到,在pom.xml中就是<build>元素中的內容。這裏就有引入maven中第二個核心概念:Lifecycle。Lifecycle直譯過來就是生命週期。咱們日常會接觸到哪些週期呢?一年中春夏秋冬就是一個週期。一個週期中可能分爲多個階段,好比這裏的春夏秋冬。在maven中一個構建過程就對應一個Lifecycle,這個Lifecycle也分爲多個階段,每一個階段叫作phase。你可能會問,那這個Lifecycle中包含多少個phase呢?一個標準的構建Lifecycle包含了以下的phase:post
validate: 用於驗證項目的有效性和其項目所須要的內容是否具有 initialize:初始化操做,好比建立一些構建所須要的目錄等。 generate-sources:用於生成一些源代碼,這些源代碼在compile phase中須要使用到 process-sources:對源代碼進行一些操做,例如過濾一些源代碼 generate-resources:生成資源文件(這些文件將被包含在最後的輸入文件中) process-resources:對資源文件進行處理 compile:對源代碼進行編譯 process-classes:對編譯生成的文件進行處理 generate-test-sources:生成測試用的源代碼 process-test-sources:對生成的測試源代碼進行處理 generate-test-resources:生成測試用的資源文件 process-test-resources:對測試用的資源文件進行處理 test-compile:對測試用的源代碼進行編譯 process-test-classes:對測試源代碼編譯後的文件進行處理 test:進行單元測試 prepare-package:打包前置操做 package:打包 pre-integration-test:集成測試前置操做 integration-test:集成測試 post-integration-test:集成測試後置操做 install:將打包產物安裝到本地maven倉庫 deploy:將打包產物安裝到遠程倉庫
在maven中,你執行任何一個phase時,maven會將其以前的phase都執行。例如 mvn install,那麼maven會將deploy以外的全部phase按照他們出現的順序一次執行。
Lifecycle還牽涉到另一個很是重要的概念:goal。注意上面Lifecycle的定義,也就是說maven爲程序的構建定義了一套規範流程:第一步須要validate,第二步須要initialize... ... compile,test,package,... ... install,deploy,可是並無定義每個phase具體應該如何操做。這裏的phase的做用有點相似於Java語言中的接口,只協商了一個契約,但並無定義具體的動做。好比說compile這個phase定義了在構建流程中須要通過編譯這個階段,但沒有定義應該怎麼編譯(編譯的輸入是什麼?用什麼編譯javac/gcc?)。這裏具體的動做就是由goal來定義,一個goal在maven中就是一個Mojo(Maven old java object)。Mojo抽象類中定義了一個execute()方法,一個goal的具體動做就是在execute()方法中實現。實現的Mojo類應該放在哪裏呢?答案是maven plugin裏,所謂的plugin其實也就是一個maven項目,只不過這個項目會引用maven的一些API,plugin項目也具有maven座標。
在執行具體的構建時,咱們須要爲lifecycle的每一個phase都綁定一個goal,這樣纔可以在每一個步驟執行一些具體的動做。好比在lifecycle中有個compile phase規定了構建的流程須要通過編譯這個步驟,而maven-compile-plugin這個plugin有個compile goal就是用javac來將源文件編譯爲class文件的,咱們須要作的就是將compile這個phase和maven-compile-plugin中的compile這個goal進行綁定,這樣就能夠實現Java源代碼的編譯了。有點繞,多看幾遍。那麼有人就會問,在哪裏綁定呢?答案是在pom.xml<build>元素中配置便可。例如:單元測試
<build> <plugins> <plugin> <artifactId>maven-myquery-plugin</artifactId> <version>1.0</version> <executions> <execution> <id>execution1</id> <phase>test</phase> <configuration> <url>http://www.foo.com/query</url> <timeout>10</timeout> <options> <option>one</option> <option>two</option> <option>three</option> </options> </configuration> <goals> <goal>query</goal> </goals> </execution> </executions> </plugin> </plugins> </build>
就將maven-myquery-plugin中的query這個goal綁定到了test這個phase,後續在maven執行到test phase時就會執行query goal。還有有人可能會問,我都沒有指定Java源文件的位置,編譯啥?這就引出了maven的design principle。在maven中,有一個很是著名的principle就是convention over configuration(約定優於配置)。這一點和ant有很是大的區別,例如使用ant來進行編譯時,咱們須要指定源文件的位置,輸出文件的位置,javac的位置,classpath... ...在maven中這些都是不須要,若沒有手動配置,maven默認從<項目根目錄>/src/main/java這個目錄去查找Java源文件,編譯後的class文件會保存在<項目根目錄>/target/classes目錄。在maven中,全部的PO都有一個根對象,就是Super POM。Super POM中定義了全部的默認的配置項。Super POM對應的pom.xml文件能夠在maven安裝目錄下lib/maven-model-builder-3.0.3.jar:org/apache/maven/model/pom-4.0.0.xml中找到。用一張圖來表示maven Lifecycle,phase,goal之間的關係:測試
Lifecyle-phase-plugin-goal關係圖ui
到此爲止,相信對於POM, pom.xml,Lifecycle, phase, goal這些概念應該十分清楚了。url