pom.xml文件是Maven進(jìn)行工作的主要配置文件。在這個(gè)文件中我們可以配置Maven項目的groupId、artifactId和version等Maven項目必須的元素;可以配置Maven項目需要使用的遠程倉庫;可以定義Maven項目打包的形式;可以定義Maven項目的資源依賴(lài)關(guān)系等等。對于一個(gè)最簡(jiǎn)單的pom.xml的定義必須包含modelVersion、groupId、artifactId和version這四個(gè)元素,當然這其中的元素也是可以從它的父項目中繼承的。在Maven中,使用groupId、artifactId和version組成groupdId:artifactId:version的形式來(lái)唯一確定一個(gè)項目。
我們知道Maven在建立項目的時(shí)候是基于Maven項目下的pom.xml進(jìn)行的,我們項目依賴(lài)的信息和一些基本信息都是在這個(gè)文件里面定義的。那如果當我們有多個(gè)項目要進(jìn)行,這多個(gè)項目有些配置內容是相同的,有些是要彼此關(guān)聯(lián)的,那如果按照傳統的做法的話(huà)我們就需要在多個(gè)項目中都定義這些重復的內容。這無(wú)疑是非常耗費時(shí)間和不易維護的。好在Maven給我們提供了一個(gè)pom的繼承和聚合的功能。
對于使用java的人而言,繼承這個(gè)詞大家應該都不陌生。要繼承pom就需要有一個(gè)父pom,在Maven中定義了超級pom.xml,任何沒(méi)有申明自己父pom.xml的pom.xml都將默認繼承自這個(gè)超級pom.xml。
先來(lái)看一下這個(gè)超級pom.xml的定義:
對于一個(gè)pom.xml來(lái)說(shuō)有幾個(gè)元素是必須定義的,一個(gè)是project根元素,然后就是它里面的modelVersion、groupId、artifactId和version。由上面的超級pom.xml的內容我們可以看到pom.xml中沒(méi)有groupId、artifactId和version的定義,所以我們在建立自己的pom.xml的時(shí)候就需要定義這三個(gè)元素。和java里面的繼承類(lèi)似,子pom.xml會(huì )完全繼承父pom.xml中所有的元素,而且對于相同的元素,一般子pom.xml中的會(huì )覆蓋父pom.xml中的元素,但是有幾個(gè)特殊的元素它們會(huì )進(jìn)行合并而不是覆蓋。這些特殊的元素是:
dependencies
developers
contributors
plugin列表,包括plugin下面的reports
resources
現在假設我們有一個(gè)項目projectA,它的pom.xml定義如下:
然后我們有另一個(gè)項目projectB,而且projectB是跟projectA的pom.xml文件處于同一個(gè)目錄下,這時(shí)候如果projectB需要繼承自projectA的話(huà)我們可以這樣定義projectB的pom.xml文件。
由projectB的pom.xml文件的定義我們可以知道,當需要繼承指定的一個(gè)Maven項目時(shí),我們需要在自己的pom.xml中定義一個(gè)parent元素,在這個(gè)元素中指明需要繼承項目的groupId、artifactId和version。
當被繼承項目與繼承項目的目錄結構不是父子關(guān)系的時(shí)候,我們再利用上面的配置是不能實(shí)現Maven項目的繼承關(guān)系的,這個(gè)時(shí)候我們就需要在子項目的pom.xml文件定義中的parent元素下再加上一個(gè)relativePath元素的定義,用以描述父項目的pom.xml文件相對于子項目的pom.xml文件的位置。
假設我們現在還是有上面兩個(gè)項目,projectA和projectB,projectB還是繼承自projectA,但是現在projectB不在projectA的子目錄中,而是與projectA處于同一目錄中。這個(gè)時(shí)候projectA和projectB的目錄結構如下:
------projectA
------pom.xml
------projectB
------pom.xml
這個(gè)時(shí)候我們可以看出projectA的pom.xml相對于projectB的pom.xml的位置是“../projectA/pom.xml”,所以這個(gè)時(shí)候projectB的pom.xml的定義應該如下所示:
對于聚合這個(gè)概念搞java的人應該都不會(huì )陌生。先來(lái)說(shuō)說(shuō)我對聚合和被聚合的理解,比如說(shuō)如果projectA聚合到projectB,那么我們就可以說(shuō)projectA是projectB的子模塊, projectB是被聚合項目,也可以類(lèi)似于繼承那樣稱(chēng)為父項目。對于聚合而言,這個(gè)主體應該是被聚合的項目。所以,我們需要在被聚合的項目中定義它的子模塊,而不是像繼承那樣在子項目中定義父項目。具體做法是:
修改被聚合項目的pom.xml中的packaging元素的值為pom
在被聚合項目的pom.xml中的modules元素下指定它的子模塊項目
對于聚合而言,當我們在被聚合的項目上使用Maven命令時(shí),實(shí)際上這些命令都會(huì )在它的子模塊項目上使用。這就是Maven中聚合的一個(gè)非常重要的作用。假設這樣一種情況,你同時(shí)需要打包或者編譯projectA、projectB、projectC和projectD,按照正常的邏輯我們一個(gè)一個(gè)項目去使用mvn compile或mvn package進(jìn)行編譯和打包,對于使用Maven而言,你還是這樣使用的話(huà)是非常麻煩的。因為Maven給我們提供了聚合的功能。我們只需要再定義一個(gè)超級項目,然后在超級項目的pom.xml中定義這個(gè)幾個(gè)項目都是聚合到這個(gè)超級項目的。之后我們只需要對這個(gè)超級項目進(jìn)行mvn compile,它就會(huì )把那些子模塊項目都進(jìn)行編譯。
還拿上面定義的projectA和projectB來(lái)舉例子,現在假設我們需要把projectB聚合到projectA中。projectA和projectB的目錄結構如下所示:
------projectA
------projectB
-----pom.xml
------pom.xml
這個(gè)時(shí)候projectA的pom.xml應該這樣定義:
由上面的定義我們可以看到被聚合的項目的packaging類(lèi)型應該為pom,而且一個(gè)項目可以有多個(gè)子模塊項目。對于聚合這種情況,我們使用子模塊項目的artifactId來(lái)作為module的值,表示子模塊項目相對于被聚合項目的地址,在上面的示例中就表示子模塊projectB是處在被聚合項目的子目錄下,即與被聚合項目的pom.xml處于同一目錄。這里使用的module值是子模塊projectB對應的目錄名projectB,而不是子模塊對應的artifactId。這個(gè)時(shí)候當我們對projectA進(jìn)行mvn package命令時(shí),實(shí)際上Maven也會(huì )對projectB進(jìn)行打包。
那么當被聚合項目與子模塊項目在目錄結構上不是父子關(guān)系的時(shí)候,我們應該怎么來(lái)進(jìn)行聚合呢?還是像繼承那樣使用relativePath元素嗎?答案是非也,具體做法是在module元素中指定以相對路徑的方式指定子模塊。我們來(lái)看下面一個(gè)例子。
繼續使用上面的projectA和projectB,還是需要把projectB聚合到projectA,但是projectA和projectB的目錄結構不再是父子關(guān)系,而是如下所示的這種關(guān)系:
------projectA
------pom.xml
------projectB
------pom.xml
這個(gè)時(shí)候projectA的pom.xml文件就應該這樣定義:
注意看module的值是“../projectB”,我們知道“..”是代表當前目錄的上層目錄,所以它表示子模塊projectB是被聚合項目projectA的pom.xml文件所在目錄(即projectA)的上層目錄下面的子目錄,即與projectA處于同一目錄層次。注意,這里的projectB對應的是projectB這個(gè)項目的目錄名稱(chēng),而不是它的artifactId。
假設有這樣一種情況,有兩個(gè)項目,projectA和projectB,現在我們需要projectB繼承projectA,同時(shí)需要把projectB聚合到projectA。然后projectA和projectB的目錄結構如下:
------projectA
------pom.xml
------projectB
------pom.xml
那么這個(gè)時(shí)候按照上面說(shuō)的那樣,projectA的pom.xml中需要定義它的packaging為pom,需要定義它的modules,所以projectA的pom.xml應該這樣定義:
而projectB是繼承自projectA的,所以我們需要在projectB的pom.xml文件中新增一個(gè)parent元素,用以定義它繼承的項目信息。所以projectB的pom.xml文件的內容應該這樣定義:
項目之間的依賴(lài)是通過(guò)pom.xml文件里面的dependencies元素下面的dependency元素進(jìn)行的。一個(gè)dependency元素定義一個(gè)依賴(lài)關(guān)系。在dependency元素中我們主要通過(guò)依賴(lài)項目的groupId、artifactId和version來(lái)定義所依賴(lài)的項目。
先來(lái)看一個(gè)簡(jiǎn)單的項目依賴(lài)的示例吧,假設我現在有一個(gè)項目projectA,然后它里面有對junit的依賴(lài),那么它的pom.xml就類(lèi)似以下這個(gè)樣子:
在dependency元素中除了可以指定依賴(lài)項目的groupId、artifactId和version之外,還可以指定以下元素:
type:對應于依賴(lài)項目的packaging類(lèi)型,默認是jar
scope:表示依賴(lài)項目的一個(gè)作用范圍。scope的主要取值范圍如下(還有一個(gè)是在Maven2.0.9以后版本才支持的import,關(guān)于import作用域將在后文《Dependency介紹》中做介紹):
n compile:這是它的默認值,這種類(lèi)型很容易讓人產(chǎn)生誤解,以為只有在編譯的時(shí)候才是需要的,其實(shí)這種類(lèi)型表示所有的情況都是有用的,包括編譯和運行時(shí)。而且這種類(lèi)型的依賴(lài)性是可以傳遞的。
n provided:這個(gè)跟compile很類(lèi)似,但是它表示你期望這個(gè)依賴(lài)項目在運行時(shí)由JDK或者容器來(lái)提供。這種類(lèi)型表示該依賴(lài)只有在測試和編譯的情況下才有效,在運行時(shí)將由JDK或者容器提供。這種類(lèi)型的依賴(lài)性是不可傳遞的。
n runtime:這種類(lèi)型表示該依賴(lài)在編譯的時(shí)候不是必須的,只有在運行的時(shí)候才是必須的。
n test:這表示這種依賴(lài)只有測試的時(shí)候才需要,正常情況下是不需要的。
n system:這種類(lèi)型跟provided類(lèi)似,唯一不同的就是這種類(lèi)型的依賴(lài)我們要自己提供jar包,這需要與另一個(gè)元素systemPath來(lái)結合使用。systemPath將指向我們系統上的jar包的路徑,而且必須是給定的絕對路徑。
systemPath:上面已經(jīng)說(shuō)過(guò)了這個(gè)元素是在scope的值為system的時(shí)候用于指定依賴(lài)的jar包在系統上的位置的,而且是絕對路徑。該元素必須在依賴(lài)的 jar包的scope為system時(shí)才能使用,否則Maven將報錯。
optional:當該項目本身作為其他項目的一個(gè)依賴(lài)時(shí)標記該依賴(lài)為可選項。假設現在projectA有一個(gè)依賴(lài)性projectB,我們把projectB這個(gè)依賴(lài)項設為optional,這表示projectB在projectA的運行時(shí)不一定會(huì )用到。這個(gè)時(shí)候如果我們有另一個(gè)項目projectC,它依賴(lài)于projectA,那么這個(gè)時(shí)候因為projectB對于projectA是可選的,所以Maven在建立projectC的時(shí)候就不會(huì )安裝projectB,這個(gè)時(shí)候如果projectC確實(shí)需要使用到projectB,那么它就可以定義自己對projectB的依賴(lài)。當一個(gè)依賴(lài)是可選的時(shí)候,我們把optional元素的值設為true,否則就不設置optional元素。
exclusions:考慮這樣一種情況,我們的projectA依賴(lài)于projectB,然后projectB又依賴(lài)于projectC,但是在projectA里面我們不需要projectB依賴(lài)的projectC,那么這個(gè)時(shí)候我們就可以在依賴(lài)projectB的時(shí)候使用exclusions元素下面的exclusion排除projectC。這個(gè)時(shí)候我們可以這樣定義projectA對projectB的依賴(lài):
在pom.xml文件中我們可以使用${propertyName}的形式引用屬性。這個(gè)propertyName有以下幾種形式:
env.propertyName:這種形式表示引用的是環(huán)境變量,比如我們需要引用當前系統的環(huán)境變量PATH的時(shí)候,就可以使用${env.PATH}。
project.propertyName:這種形式表示引用的是當前這個(gè)pom.xml中project根元素下面的子元素的值。比如我們需要引用當前project下面的version的時(shí)候,就可以使用${project.version}。
settings.propertyName:這種形式引用的是Maven本地配置文件settings.xml或本地Maven安裝目錄下的settings.xml文件根元素settings下的元素。比如我們需要引用settings下的本地倉庫localRepository元素的值時(shí),我們可以用${settings.localRepository}
java的系統屬性,所有在java中使用java.lang.System.getProperties()能夠獲取到的屬性都可以在pom.xml中引用,比如${java.home}。
pom.xml中properties元素下面的子元素作為屬性。假如在pom.xml中有如下一段代碼<properties><hello.world>helloWorld</hello.world></properties>,那么我們就可以使用${hello.world}引用到對應的helloWorld。
聯(lián)系客服