為了減少重復代碼的編寫,我們需要創建POM的父子結構,然后在POM中申明一些配置供子POM繼承,以實現“一處申明,多處使用的”目的。以之前的模塊中的結構為基礎,在account-aggregator下創建一個account-parent的子目錄,然后在該子目錄下創建除account-aggregator模塊之外的模塊的父模塊。為此在該子目錄下創建一個pom.xml文件如下:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.juvenxu.mvnbook.account</groupId> <artifactId>account-parent</artifactId> <version>1.0.0-SNAPSHOT</version> <packaging>pom</packaging> <name>Account Parent</name> </project>
需要注意的是它的packaging的值必須為pom,這一點與模塊聚合一樣,作為負模塊的POM,其打包類型也必須為pom。由於父模塊只是為了消除配置的重復,因此也就不需要src/main/java等目錄了。
有了父模塊就讓其他子模塊來繼承它,修改account-email的pom文件如下:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.juvenxu.mvnbook.account</groupId> <artifactId>account-parent</artifactId> <version>1.0.0-SNAPSHOT</version> <relativePath>../account-parent/pom.xml</relativePath> </parent> <artifactId>account-email</artifactId> <name>Account Email</name> <dependencies> ...... </dependencies> <build> <plugins> ...... </plugins> </build> </project>
parent下的子元素groupId、artifactId和version指定了父模塊的坐標,這三個元素是必須的。元素relativePath表示父模塊POM的相對路徑。當項目構建時,Maven會首先根據relativePath檢查父POM,如果找不到,再從本地倉庫查找。relativePath的默認值是../pom.xml,也就是說,Maven默認父POM在上一層目錄下。
這個更新過的POM文件並沒為account-email設置artifactId和version,實際上這個子模塊隱式的從父模塊繼承了這兩個元素。如果子模塊中遇到與父模塊中不一樣的artifactId、version這個時候應該在子模塊顯示聲明。
最后還要把父模塊加入到聚合模塊account-aggregator中如下:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.juvenxu.mvnbook.account</groupId> <artifactId>account-aggregator</artifactId> <version>1.0.0-SNAPSHOT</version> <packaging>pom</packaging> <name>Account Aggregator</name> <modules> <module>account-parent</module> <module>account-email</module> <module>account-persist</module> <modules> </project>
此時的目錄結構是account-parent、account-email、account-persist在同一級目錄,account-aggregator為三個目錄的父目錄。
可繼承的POM元素如下:
- groupId:項目組ID,項目坐標的核心元素
- version:項目版本,項目坐標的核心因素
- description:項目的描述信息
- organization:項目的組織信息
- inceptionYear:項目的創始年份
- url:項目的URL地址
- developers:項目的開發者信息
- contributors:項目的貢獻者信息
- distributionManagement:項目的部署配置
- issueManagement:項目的缺陷跟蹤系統信息
- ciManagement:項目的持續集成系統信息
- scm:項目的版本控制系統西溪
- malilingLists:項目的郵件列表信息
- properties:自定義的Maven屬性
- dependencies:項目的依賴配置
- dependencyManagement:項目的依賴管理配置
- repositories:項目的倉庫配置
- build:包括項目的源碼目錄配置、輸出目錄配置、插件配置、插件管理配置等
- reporting:包括項目的報告輸出目錄配置、報告插件配置等
依賴管理
因為dependencies元素也是可繼承的,因此我們可以把公共的jar放到父模塊account-parent中,比如org.springframework:spring-core:2.5.6在account-email,account-persist模塊中都會用到,因此可以將這個依賴配置放到父模塊中,兩個子模塊就能移除這些依賴從而簡化配置。
上述做法是可行的,但是存在問題。因為所有的子模塊都會繼承以上父模塊的依賴項,不管子模塊是否真的需要此依賴項。這樣相當於子模塊失去了這些依賴項繼承的選擇權。假設后面加了模塊acount-util與springframework沒有半毛錢關系,讓他依賴spring的依賴項顯然是不合理的。
為此Maven提供了dependencyManagement元素既能讓子模塊繼承到父模塊的依賴配置,又能保證子模塊依賴的使用靈活性。在dependencyManagement元素下的依賴聲明不會引入到實際的依賴,不過它能約束dependencies下的依賴使用。
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.juvenxu.mvnbook.account</groupId> <artifactId>account-parent</artifactId> <version>1.0.0-SNAPSHOT</version> <packaging>pom</packaging> <name>Account Parent</name> <properties> <springframework.version>2.5.6</springframework.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework </groupId> <artifactId>spring-core</artifactId> <version>${springframework.version}</version> </dependency> ...... </dependencies> </dependencyManagement> </project>
這里使用了dependencyManagement聲明的依賴既不會給account-parent引入依賴項,也不會給他的子模塊引入依賴,不過這段配置會被繼承。現在修改account-email的POM文件如下:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.juvenxu.mvnbook.account</groupId> <artifactId>account-parent</artifactId> <version>1.0.0-SNAPSHOT</version> <relativePath>../account-parent/pom.xml</relativePath> </parent> <artifactId>account-email</artifactId> <name>Account Email</name> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> </dependency> ...... </dependencies> <build> <plugins> ...... </plugins> </build> </project>
上面的配置較原來簡單了一些,只配置了groupId和artifactId,省去了version,如果父模塊配置了依賴的scope也是可以省略的。這些信息可以省略是因為account-email繼承了account-parent中的dependencyManagement配置,完整的依賴聲明已經包含在父POM中了,子模塊只需簡單的配置groupId和artifactId。
使用這種以來管理機制雖然不能減少太多的配置項,但是經過別人實踐后強烈推薦的方法。如果子模塊不聲明依賴的使用,即使該依賴已經在父POM文件dependencyManagement中聲明了,也不會產生任何實際的效果。
插架管理
Maven提供了dependencyManagement元素幫助管理依賴,類似的,Maven也提供了pluginManagement元素幫助管理插件。當子模塊POM中配置了plugin,並且groupId和artifactId與pluginManagement中的配置的插件匹配時,pluginManagement的配置才會真正影響實際插件的行為。
父POM文件:
<build> <pluginManagement> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-source-plugin</artifactId> <version>2.1.2</version> <executions> <execution> <id>attach-sources</id> <phase>verify</phase> <goals> <goal>jar-no-fork</goal> </goals> </execution> </executions> </plugin> </plugins> </pluginManagement> </build>
在子模塊繼承pluginManagement后的插件配置
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-source-plugin</artifactId> </plugin> </plugins> </build>
聚合與繼承的關系
區別 :
1.對於聚合模塊來說,它知道有哪些被聚合的模塊,但那些被聚合的模塊不知道這個聚合模塊的存在。
2.對於繼承關系的父 POM來說,它不知道有哪些子模塊繼承與它,但那些子模塊都必須知道自己的父 POM是什么。
共同點 :
1.聚合 POM與繼承關系中的父POM的 packaging都是pom
2.聚合模塊與繼承關系中的父模塊除了 POM之外都沒有實際的內容。
<modules> <module>account-email</module> <module>account-persist</module> <module>account-parent</module> </modules>
實際的構建順序為: ~parent, ~email, ~persist。
可見構建的順序並不是聲明的順序。
該例中account-email和account-persist都依賴於account-parent,那么account-parent就必須在另外兩個模塊之前構建。實際的構建順序是這樣的:Maven按聲明順序讀取POM,如果POM沒有依賴模塊,那么久構建該模塊,否則就先構建其依賴的模塊。,如果依賴的模塊還依賴於其他模塊,則進一步構建依賴的依賴。
模塊之間的關系將反應堆構成一個有向非循環圖,各個模塊是該圖的節點,依賴關系構成又向邊,這個圖不允許出現循環,因此當出現A依賴於B,B又依賴於A的情況時Maven就會報錯。

裁剪反應堆:
mvn -h可以看到這樣一些選項:
Options: -am,--also-make If project list is specified, also build projects required by the list -amd,--also-make-dependents If project list is specified, also -pl,--projects <arg> Build specified reactor projects instead of all projects. A project can be specified by [groupId]:artifactId or by its relative path. -rf,--resume-from <arg> Resume reactor from specified project
有的時候因為工程巨大,模塊很多,只想構建指定的模塊,這時候就會用到反應堆裁剪。
-pl 構建指定模塊
mvn clean install -pl accouont-email,account-persist后會得到如下幾個模塊:~email和~persist。
-am 選項可以同時構建所列模塊的依賴模塊。
當執行mvn clean install -pl account-email -am 后會得到如下幾個模塊:~parent和~email。
-amd 選項可以同時構建依賴於所列模塊的模塊。
mvn clean install -pl account-parent -amd 后會得到如下模塊:~parent、~email和~persist。
-rf 選項可以在完整的反應堆構建順序基礎上,指定從哪個模塊開始構建。
mvn clean install -rf account-email ,由於~email位於第三,它之后有~persist,因此會得到如下輸出模塊:~email和~persist。
在-pl -am或者-pl -amd的基礎上,還能應用-rf參數,以對裁剪后的反應堆再次裁剪。
mvn clean install -pl account-parent -amd -rf account-email
其中,-pl 和 -amd參數會得到:~parent, ~email和~persist模塊,在此基礎上 -rf參數制定從~email開始構建,於是會得到:~email和~persist。
