什麼是POM?
POM是項目對象模型(Project Object Model)的簡稱,它是Maven項目中的檔案,使用XML表示,名稱叫做pom.xml。作用類似ant的build.xml檔案,功能更強大。該檔案用于管理:源代碼、配置檔案、開發者的資訊和角色、問題追蹤系統、組織資訊、項目授權、項目的url、項目的依賴關系等等。事實上,在Maven世界中,project可以什麼都沒有,甚至沒有代碼,但是必須包含pom.xml檔案。
下面是一個POM項目中的pom.xml檔案中包含的元素。注意,其中的modelVersion是4.0.0,這是目前僅有的可以被Maven2&3同時支援的POM版本,它是必須的。
一,基本配置
一個最簡單的pom.xml的定義必須包含modelVersion、groupId、artifactId和version這四個元素,當然這其中的元素也是可以從它的父項目中繼承的。在Maven中,使用groupId、artifactId和version組成groupdId:artifactId:version的形式來唯一确定一個項目:
我們知道Maven在建立項目的時候是基于Maven項目下的pom.xml進行的,我們項目依賴的資訊和一些基本資訊都是在這個檔案裡面定義的。那如果當我們有多個項目要進行,這多個項目有些配置内容是相同的,有些是要彼此關聯的,那如果按照傳統的做法的話我們就需要在多個項目中都定義這些重複的内容。這無疑是非常耗費時間和不易維護的。好在Maven給我們提供了一個pom的繼承和聚合的功能。
對于使用java的人而言,繼承這個詞大家應該都不陌生。要繼承pom就需要有一個父pom,在Maven中定義了超級pom.xml,任何沒有申明自己父pom.xml的pom.xml都将預設繼承自這個超級pom.xml。
位置:在Maven 2.xxx版本中,比如maven-2.0.9-uber.jar,打開此Jar檔案後,在包包org.apache.maven.project下會有pom-4.0.0.xml的檔案。但是到了3.xxx版本之後在: aven安裝目錄下的:/lib/maven-model-builder-version.jar中 \org\apache\maven\mode目錄中的pom-4.0.0.xml。
先來看一下這個超級pom.xml的定義:
對于一個pom.xml來說有幾個元素是必須定義的,一個是project根元素,然後就是它裡面的modelVersion、groupId、artifactId和version。由上面的超級pom.xml的内容我們可以看到pom.xml中沒有groupId、artifactId和version的定義,是以我們在建立自己的pom.xml的時候就需要定義這三個元素。和java裡面的繼承類似,子pom.xml會完全繼承父pom.xml中所有的元素,而且對于相同的元素,一般子pom.xml中的會覆寫父pom.xml中的元素,但是有幾個特殊的元素它們會進行合并而不是覆寫。這些特殊的元素是:
dependencies
developers
contributors
plugin清單,包括plugin下面的reports
resources
現在假設我們有一個項目projectA,它的pom.xml定義如下:
然後我們有另一個項目projectB,而且projectB是跟projectA的pom.xml檔案處于同一個目錄下,這時候如果projectB需要繼承自projectA的話我們可以這樣定義projectB的pom.xml檔案。
由projectB的pom.xml檔案的定義我們可以知道,當需要繼承指定的一個Maven項目時,我們需要在自己的pom.xml中定義一個parent元素,在這個元素中指明需要繼承項目的groupId、artifactId和version。
當被繼承項目與繼承項目的目錄結構不是父子關系的時候,我們再利用上面的配置是不能實作Maven項目的繼承關系的,這個時候我們就需要在子項目的pom.xml檔案定義中的parent元素下再加上一個relativePath元素的定義,用以描述父項目的pom.xml檔案相對于子項目的pom.xml檔案的位置。
假設我們現在還是有上面兩個項目,projectA和projectB,projectB還是繼承自projectA,但是現在projectB不在projectA的子目錄中,而是與projectA處于同一目錄中。這個時候projectA和projectB的目錄結構如下:
------projectA
------pom.xml
------projectB
這個時候我們可以看出projectA的pom.xml相對于projectB的pom.xml的位置是“../projectA/pom.xml”,是以這個時候projectB的pom.xml的定義應該如下所示:
對于聚合這個概念搞java的人應該都不會陌生。先來說說我對聚合和被聚合的了解,比如說如果projectA聚合到projectB,那麼我們就可以說projectA是projectB的子子產品, projectB是被聚合項目,也可以類似于繼承那樣稱為父項目。對于聚合而言,這個主體應該是被聚合的項目。是以,我們需要在被聚合的項目中定義它的子子產品,而不是像繼承那樣在子項目中定義父項目。具體做法是:
修改被聚合項目的pom.xml中的packaging元素的值為pom
在被聚合項目的pom.xml中的modules元素下指定它的子子產品項目
對于聚合而言,當我們在被聚合的項目上使用Maven指令時,實際上這些指令都會在它的子子產品項目上使用。這就是Maven中聚合的一個非常重要的作用。假設這樣一種情況,你同時需要打包或者編譯projectA、projectB、projectC和projectD,按照正常的邏輯我們一個一個項目去使用mvn compile或mvn package進行編譯和打包,對于使用Maven而言,你還是這樣使用的話是非常麻煩的。因為Maven給我們提供了聚合的功能。我們隻需要再定義一個超級項目,然後在超級項目的pom.xml中定義這個幾個項目都是聚合到這個超級項目的。之後我們隻需要對這個超級項目進行mvn compile,它就會把那些子子產品項目都進行編譯。
還拿上面定義的projectA和projectB來舉例子,現在假設我們需要把projectB聚合到projectA中。projectA和projectB的目錄結構如下所示:
------projectB
-----pom.xml
這個時候projectA的pom.xml應該這樣定義:
由上面的定義我們可以看到被聚合的項目的packaging類型應該為pom,而且一個項目可以有多個子子產品項目。對于聚合這種情況,我們使用子子產品項目的artifactId來作為module的值,表示子子產品項目相對于被聚合項目的位址,在上面的示例中就表示子子產品projectB是處在被聚合項目的子目錄下,即與被聚合項目的pom.xml處于同一目錄。這裡使用的module值是子子產品projectB對應的目錄名projectB,而不是子子產品對應的artifactId。這個時候當我們對projectA進行mvn package指令時,實際上Maven也會對projectB進行打包。
那麼當被聚合項目與子子產品項目在目錄結構上不是父子關系的時候,我們應該怎麼來進行聚合呢?還是像繼承那樣使用relativePath元素嗎?答案是非也,具體做法是在module元素中指定以相對路徑的方式指定子子產品。我們來看下面一個例子。
繼續使用上面的projectA和projectB,還是需要把projectB聚合到projectA,但是projectA和projectB的目錄結構不再是父子關系,而是如下所示的這種關系:
這個時候projectA的pom.xml檔案就應該這樣定義:
注意看module的值是“../projectB”,我們知道“..”是代表目前目錄的上層目錄,是以它表示子子產品projectB是被聚合項目projectA的pom.xml檔案所在目錄(即projectA)的上層目錄下面的子目錄,即與projectA處于同一目錄層次。注意,這裡的projectB對應的是projectB這個項目的目錄名稱,而不是它的artifactId。
假設有這樣一種情況,有兩個項目,projectA和projectB,現在我們需要projectB繼承projectA,同時需要把projectB聚合到projectA。然後projectA和projectB的目錄結構如下:
------projectA
------pom.xml
------projectB
那麼這個時候按照上面說的那樣,projectA的pom.xml中需要定義它的packaging為pom,需要定義它的modules,是以projectA的pom.xml應該這樣定義:
而projectB是繼承自projectA的,是以我們需要在projectB的pom.xml檔案中新增一個parent元素,用以定義它繼承的項目資訊。是以projectB的pom.xml檔案的内容應該這樣定義:
項目之間的依賴是通過pom.xml檔案裡面的dependencies元素下面的dependency元素進行的。一個dependency元素定義一個依賴關系。在dependency元素中我們主要通過依賴項目的groupId、artifactId和version來定義所依賴的項目。
先來看一個簡單的項目依賴的示例吧,假設我現在有一個項目projectA,然後它裡面有對junit的依賴,那麼它的pom.xml就類似以下這個樣子:
groupId, artifactId, version:描述了依賴的項目唯一标志。
type:對應于依賴項目的packaging類型,預設是jar。
scope:用于限制相應的依賴範圍、傳播範圍。scope的主要取值範圍如下(還有一個是在Maven2.0.9以後版本才支援的import,關于import作用域将在後文《Dependency介紹》中做介紹):
test:在測試範圍有效,它在執行指令test的時候才執行,并且它不會傳播給其他子產品進行引入,比如 junit,dbunit 等測試架構。
compile(default 預設):這是它的預設值,這種類型很容易讓人産生誤解,以為隻有在編譯的時候才是需要的,其實這種類型表示所有的情況都是有用的,包括編譯和運作時。而且這種類型的依賴性是可以傳遞的。
runtime:在程式運作的時候依賴,在編譯的時候不依賴。
provided:這個跟compile很類似,但是它表示你期望這個依賴項目在運作時由JDK或者容器來提供。這種類型表示該依賴隻有在測試和編譯的情況下才有效,在運作時将由JDK或者容器提供。這種類型的依賴性是不可傳遞的。比如 javaee:
eclipse開發web環境中是沒有javaee必須要手動添加。
myeclipse建立web項目會有JavaEE(servlet-api.jar,jsp-api.jar...)web容器依賴的jar包,一般都是做開發的時候才使用。但是myeclipse不會把這些 jar包釋出的,lib下你是找不到javaee引入的jar包,因為myeclipse釋出項目的時候會忽略它。為什麼?因為tomcat容器bin/lib已經存在了這個jar包了。
system:這種類型跟provided類似,唯一不同的就是這種類型的依賴我們要自己提供jar包,這需要與另一個元素systemPath來結合使用。systemPath将指向我們系統上的jar包的路徑,而且必須是給定的絕對路徑。
systemPath:上面已經說過了這個元素是在scope的值為system的時候用于指定依賴的jar包在系統上的位置的,而且是絕對路徑。該元素必須在依賴的 jar包的scope為system時才能使用,否則Maven将報錯。
optional:當該項目本身作為其他項目的一個依賴時标記該依賴為可選項。假設現在projectA有一個依賴性projectB,我們把projectB這個依賴項設為optional,這表示projectB在projectA的運作時不一定會用到。這個時候如果我們有另一個項目projectC,它依賴于projectA,那麼這個時候因為projectB對于projectA是可選的,是以Maven在建立projectC的時候就不會安裝projectB,這個時候如果projectC确實需要使用到projectB,那麼它就可以定義自己對projectB的依賴。當一個依賴是可選的時候,我們把optional元素的值設為true,否則就不設定optional元素。
exclusions:考慮這樣一種情況,我們的projectA依賴于projectB,然後projectB又依賴于projectC,但是在projectA裡面我們不需要projectB依賴的projectC,那麼這個時候我們就可以在依賴projectB的時候使用exclusions元素下面的exclusion排除projectC。這個時候我們可以這樣定義projectA對projectB的依賴:
在pom.xml檔案中我們可以使用${propertyName}的形式引用屬性。是值的占位符,類似EL,類似ant的屬性,比如${X},可用于pom檔案任何指派的位置。有以下分類:
env.propertyName:這種形式表示引用的是環境變量,比如我們需要引用目前系統的環境變量PATH的時候,就可以使用${env.PATH}。
project.propertyName:這種形式表示引用的是目前這個pom.xml中project根元素下面的子元素的值。比如我們需要引用目前project下面的version的時候,就可以使用${project.version}。
settings.propertyName:這種形式引用的是Maven本地配置檔案settings.xml或本地Maven安裝目錄下的settings.xml檔案根元素settings下的元素。比如我們需要引用settings下的本地倉庫localRepository元素的值時,我們可以用${settings.localRepository}
Java System Properties: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。