maven至今還是Java編程語(yǔ)言構(gòu)建的事實(shí)標(biāo)準(zhǔn),大部分項(xiàng)目還在使用maven來(lái)進(jìn)行構(gòu)建敷鸦,因此了解maven內(nèi)部運(yùn)行的原理對(duì)定位和分析問(wèn)題還是很有裨益的。本篇文章主要介紹一些maven內(nèi)部運(yùn)行過(guò)程中的一些基本概念,相信看完后急鳄,對(duì)那么些剛剛接觸maven的讀者來(lái)說(shuō)maven將不再陌生谤民。
??在具體分析項(xiàng)目構(gòu)建的過(guò)程前,需要了解maven的一些基本概念疾宏,這些概念十分重要张足,請(qǐng)務(wù)必理解清楚后再看下文】裁辏基本概念主要有:POM为牍,Lifecycle。這兩個(gè)概念又會(huì)包含一些小的概念岩馍,下文會(huì)逐步講解碉咆。
??POM: 注意這里的POM不是maven中構(gòu)建過(guò)程使用的配置文件pom.xml,但他們之間還是有聯(lián)系的蛀恩。POM的全稱是Project Object Model吟逝,用通俗點(diǎn)的話說(shuō)就是對(duì)要構(gòu)建的項(xiàng)目進(jìn)行建模,將要構(gòu)建的項(xiàng)目看成是一個(gè)對(duì)象(Object)赦肋,后文就使用PO來(lái)指代這個(gè)對(duì)象块攒。既然是一個(gè)對(duì)象,那么PO有哪些屬性呢佃乘?在maven中一個(gè)項(xiàng)目都是用一個(gè)唯一的坐標(biāo)(coordinate)來(lái)表示囱井,坐標(biāo)由groupId, artifactId, version, classifier, type這五部分組成。這樣來(lái)說(shuō)PO應(yīng)該也要具備坐標(biāo)屬性趣避。另外一方面庞呕,一個(gè)項(xiàng)目肯定不是孤立存在的,可能依賴于其他的一些項(xiàng)目程帕,那么也就是說(shuō)PO應(yīng)該具備dependencies這個(gè)屬性住练,用來(lái)表示其所依賴的外部項(xiàng)目。我們可以嘗試一下用Java代碼來(lái)描述下PO這個(gè)對(duì)象:
class PO{
private String groupId;
private String artifactId;
private String version;
private String classifier;
private String type;
private Set<PO> dependencies;
}
我們知道XML的表達(dá)能力是很強(qiáng)大的愁拭,一個(gè)Java中的對(duì)象是可以用XML來(lái)描述讲逛,例如一個(gè)上面定義的PO對(duì)象則可以用下面的XML來(lái)描述(表達(dá)有不規(guī)范之處,理解其中的含義即可):
<PO>
<groupId></groupId>
<artifactId></artifactId>
<version></version>
<classifier><classifier>
<type></type>
<dependencies>
<PO></PO>
<PO></PO>
...
</dependencies>
</PO>
是不是好像和什么有點(diǎn)類似岭埠?對(duì)盏混,就是pom.xml。pom.xml就是PO對(duì)象的XML描述惜论。上面的PO定義還不完整许赃,我們繼續(xù)看下PO還有什么其他的屬性。我們知道在Java中類是可以繼承的馆类,一個(gè)對(duì)象在創(chuàng)建的時(shí)候會(huì)同時(shí)創(chuàng)建其父類對(duì)象混聊,類似的,PO對(duì)象也有其父對(duì)象乾巧,用parent屬性來(lái)表示句喜,并且PO對(duì)象會(huì)繼承其父對(duì)象的所有屬性僵闯。另外一方面,一個(gè)項(xiàng)目可能根據(jù)不同職責(zé)分為多個(gè)模塊(module)藤滥,所有模塊其實(shí)也就是一個(gè)單獨(dú)的項(xiàng)目鳖粟,只不過(guò)這些項(xiàng)目會(huì)使用其父對(duì)象的一些屬性來(lái)進(jìn)行構(gòu)建。我們將這些新的屬性加到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;
}
再將這個(gè)定義用XML語(yǔ)言表示一下:
<PO>
<parent></parent>
<groupId></groupId>
<artifactId></artifactId>
<version></version>
<classifier><classifier>
<type></type>
<dependencies>
<PO></PO>
<PO></PO>
...
</dependencies>
<modules>
...
</modules>
</PO>
是不是越來(lái)越像pom.xml了拙绊?對(duì)向图,這就是pom.xml的由來(lái)。再說(shuō)一遍:pom.xml就是PO對(duì)象的XML描述标沪。到此為止榄攀,相信你再看pom.xml文件時(shí),會(huì)有一個(gè)全新的認(rèn)識(shí)金句。
上面說(shuō)的這些PO屬性是項(xiàng)目的一些固有屬性檩赢,到目前為止,我們還沒(méi)有涉及項(xiàng)目的構(gòu)建過(guò)程违寞。構(gòu)建過(guò)程對(duì)應(yīng)的是PO對(duì)象的build屬性贞瞒,那么你應(yīng)該馬上想到,在pom.xml中就是<build>元素中的內(nèi)容趁曼。這里就有引入maven中第二個(gè)核心概念:Lifecycle军浆。Lifecycle直譯過(guò)來(lái)就是生命周期。我們平常會(huì)接觸到哪些周期呢挡闰?一年中春夏秋冬就是一個(gè)周期乒融。一個(gè)周期中可能分為多個(gè)階段,比如這里的春夏秋冬摄悯。在maven中一個(gè)構(gòu)建過(guò)程就對(duì)應(yīng)一個(gè)Lifecycle赞季,這個(gè)Lifecycle也分為多個(gè)階段,每個(gè)階段叫做phase奢驯。你可能會(huì)問(wèn)申钩,那這個(gè)Lifecycle中包含多少個(gè)phase呢?一個(gè)標(biāo)準(zhǔn)的構(gòu)建Lifecycle包含了如下的phase:
validate: 用于驗(yàn)證項(xiàng)目的有效性和其項(xiàng)目所需要的內(nèi)容是否具備
initialize:初始化操作叨橱,比如創(chuàng)建一些構(gòu)建所需要的目錄等典蜕。
generate-sources:用于生成一些源代碼,這些源代碼在compile phase中需要使用到
process-sources:對(duì)源代碼進(jìn)行一些操作罗洗,例如過(guò)濾一些源代碼
generate-resources:生成資源文件(這些文件將被包含在最后的輸入文件中)
process-resources:對(duì)資源文件進(jìn)行處理
compile:對(duì)源代碼進(jìn)行編譯
process-classes:對(duì)編譯生成的文件進(jìn)行處理
generate-test-sources:生成測(cè)試用的源代碼
process-test-sources:對(duì)生成的測(cè)試源代碼進(jìn)行處理
generate-test-resources:生成測(cè)試用的資源文件
process-test-resources:對(duì)測(cè)試用的資源文件進(jìn)行處理
test-compile:對(duì)測(cè)試用的源代碼進(jìn)行編譯
process-test-classes:對(duì)測(cè)試源代碼編譯后的文件進(jìn)行處理
test:進(jìn)行單元測(cè)試
prepare-package:打包前置操作
package:打包
pre-integration-test:集成測(cè)試前置操作
integration-test:集成測(cè)試
post-integration-test:集成測(cè)試后置操作
install:將打包產(chǎn)物安裝到本地maven倉(cāng)庫(kù)
deploy:將打包產(chǎn)物安裝到遠(yuǎn)程倉(cāng)庫(kù)
在maven中,你執(zhí)行任何一個(gè)phase時(shí)钢猛,maven會(huì)將其之前的phase都執(zhí)行伙菜。例如 mvn install,那么maven會(huì)將deploy之外的所有phase按照他們出現(xiàn)的順序一次執(zhí)行命迈。
??Lifecycle還牽涉到另外一個(gè)非常重要的概念:goal贩绕。注意上面Lifecycle的定義火的,也就是說(shuō)maven為程序的構(gòu)建定義了一套規(guī)范流程:第一步需要validate,第二步需要initialize... ... compile淑倾,test馏鹤,package,... ... install娇哆,deploy湃累,但是并沒(méi)有定義每一個(gè)phase具體應(yīng)該如何操作。這里的phase的作用有點(diǎn)類似于Java語(yǔ)言中的接口碍讨,只協(xié)商了一個(gè)契約治力,但并沒(méi)有定義具體的動(dòng)作。比如說(shuō)compile這個(gè)phase定義了在構(gòu)建流程中需要經(jīng)過(guò)編譯這個(gè)階段勃黍,但沒(méi)有定義應(yīng)該怎么編譯(編譯的輸入是什么宵统?用什么編譯javac/gcc?)。這里具體的動(dòng)作就是由goal來(lái)定義覆获,一個(gè)goal在maven中就是一個(gè)Mojo(Maven old java object)马澈。Mojo抽象類中定義了一個(gè)execute()方法,一個(gè)goal的具體動(dòng)作就是在execute()方法中實(shí)現(xiàn)弄息。實(shí)現(xiàn)的Mojo類應(yīng)該放在哪里呢箭券?答案是maven plugin里,所謂的plugin其實(shí)也就是一個(gè)maven項(xiàng)目疑枯,只不過(guò)這個(gè)項(xiàng)目會(huì)引用maven的一些API辩块,plugin項(xiàng)目也具備maven坐標(biāo)。
??在執(zhí)行具體的構(gòu)建時(shí)荆永,我們需要為lifecycle的每個(gè)phase都綁定一個(gè)goal废亭,這樣才能夠在每個(gè)步驟執(zhí)行一些具體的動(dòng)作。比如在lifecycle中有個(gè)compile phase規(guī)定了構(gòu)建的流程需要經(jīng)過(guò)編譯這個(gè)步驟具钥,而maven-compile-plugin這個(gè)plugin有個(gè)compile goal就是用javac來(lái)將源文件編譯為class文件的豆村,我們需要做的就是將compile這個(gè)phase和maven-compile-plugin中的compile這個(gè)goal進(jìn)行綁定,這樣就可以實(shí)現(xiàn)Java源代碼的編譯了骂删。有點(diǎn)繞掌动,多看幾遍。那么有人就會(huì)問(wèn)宁玫,在哪里綁定呢粗恢?答案是在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這個(gè)goal綁定到了test這個(gè)phase欧瘪,后續(xù)在maven執(zhí)行到test phase時(shí)就會(huì)執(zhí)行query goal眷射。還有有人可能會(huì)問(wèn),我都沒(méi)有指定Java源文件的位置,編譯啥妖碉?這就引出了maven的design principle涌庭。在maven中,有一個(gè)非常著名的principle就是convention over configuration(約定優(yōu)于配置)欧宜。這一點(diǎn)和ant有非常大的區(qū)別坐榆,例如使用ant來(lái)進(jìn)行編譯時(shí),我們需要指定源文件的位置冗茸,輸出文件的位置席镀,javac的位置,classpath... ...在maven中這些都是不需要蚀狰,若沒(méi)有手動(dòng)配置愉昆,maven默認(rèn)從<項(xiàng)目根目錄>/src/main/java這個(gè)目錄去查找Java源文件,編譯后的class文件會(huì)保存在<項(xiàng)目根目錄>/target/classes目錄麻蹋。在maven中跛溉,所有的PO都有一個(gè)根對(duì)象,就是Super POM扮授。Super POM中定義了所有的默認(rèn)的配置項(xiàng)芳室。Super POM對(duì)應(yīng)的pom.xml文件可以在maven安裝目錄下lib/maven-model-builder-3.0.3.jar:org/apache/maven/model/pom-4.0.0.xml中找到。用一張圖來(lái)表示maven Lifecycle刹勃,phase堪侯,goal之間的關(guān)系:
到此為止,相信對(duì)于POM, pom.xml荔仁,Lifecycle伍宦, phase, goal這些概念應(yīng)該十分清楚了。還有疑問(wèn)乏梁?請(qǐng)留言次洼。