1. 程式人生 > >Maven內部運行原理

Maven內部運行原理

多少 作者 nsh out 再看 type 哪些 1.0 ...

原作者zlwen 原文鏈接 內部運行原理(二) 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這個對象:
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來描述(表達有不規範之處,理解其中的含義即可):

<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語言表示一下:

<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:

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之間的關系: 技術分享

Maven內部運行原理