Maven 的聚合與繼承
通常情況下,我們在實際開發(fā)過程中,會對項目進行模塊(module)劃分,來提供項目的清晰度并且能夠更加方便的重用代碼。但是,在這種時候,我們在構(gòu)建項目的時候就需要分別構(gòu)建不同的模塊,Maven 的聚合特性能夠?qū)⒏鱾€不同的模塊聚合到一起來進行構(gòu)建。而繼承的特性,則能夠幫助我們抽取各個模塊公用的依賴、插件等,實現(xiàn)配置統(tǒng)一。
1. 聚合
這里我們以一個簡單的 mall 項目作為例子。先來看一下這個項目的結(jié)構(gòu),整個項目包括 mall-core 和 mall-account 兩個功能模塊和 mall-aggregator 一個聚合模塊。其中, mall-core 處理商城項目的核心業(yè)務(wù)邏輯, mall-account 用于管理商城的賬戶信息。
一般來說,對于只有一個模塊的項目,我們可以在該模塊下直接執(zhí)行 mvn clean package
命令來進行項目構(gòu)建,但是,對于這樣的多模塊項目,我們?nèi)绻獦?gòu)建不同模塊的話,需要分別在對應(yīng)模塊下執(zhí)行 Maven 的相關(guān)命令,這樣看起來是非常繁瑣的。這個時候,Maven 的聚合特性就能夠起到作用。
我們來分析一下這個項目整體的結(jié)構(gòu),首先,我們看一下 mall-aggregator 模塊。這個模塊作為整個工程的聚合模塊,并沒有實際的代碼,但是其本身也是一個 Maven 項目,所以,也會存在 pom.xml 文件。那我們首先來看一下這個 pom.xml 文件有什么特點。
我們可以看到這里面也會有相對應(yīng)的 groupId , artifactId , version ,packaging 信息,其中 packaging 的值必須是 pom,否則聚合項目無法構(gòu)建。我們從 modules 中可以看到整個項目包含兩個模塊,分別是 mall-core 和 mall-account 。通常情況下,我們將不同的模塊放到聚合模塊下,其中 module 的值分別對應(yīng)不同模塊的 artifactId 值。
在這個時候,我們 mall-aggregator 模塊下,使用 mvn clean package
來進行構(gòu)建,可以將兩個模塊同時打包完成。
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO]
[INFO] mall-aggregator [pom]
[INFO] mall-core [jar]
[INFO] mall-account [jar]
[INFO]
[INFO] ----------------------< com.mic:mall-aggregator >-----------------------
[INFO] Building mall-aggregator 1.0.0-SNAPSHOT [1/3]
[INFO] --------------------------------[ pom ]---------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ mall-aggregator ---
[INFO]
[INFO] -------------------------< com.mic:mall-core >--------------------------
[INFO] Building mall-core 1.0.0-SNAPSHOT [2/3]
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] ...
[INFO] ------------------------< com.mic:mall-account >------------------------
[INFO] Building mall-account 1.0.0-SNAPSHOT [3/3]
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] ...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary for mall-aggregator 1.0.0-SNAPSHOT:
[INFO]
[INFO] mall-aggregator .................................... SUCCESS [ 0.494 s]
[INFO] mall-core .......................................... SUCCESS [ 2.152 s]
[INFO] mall-account ....................................... SUCCESS [ 0.145 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3.002 s
[INFO] Finished at: 2020-05-05T11:32:54+08:00
[INFO] ------------------------------------------------------------------------
從這次構(gòu)建的過程來看,我們可以看出,Maven 會首先解析聚合模塊的 pom.xml 文件,分析出有哪些模塊需要構(gòu)建,進而計算出一個反應(yīng)堆構(gòu)建順序(Reactor Build Order),并且根據(jù)這個順序來依次進行模塊的構(gòu)建。
2. 繼承
2.1 繼承特性
現(xiàn)在我們解決了同時構(gòu)建不同模塊同時構(gòu)建的問題,但是,對于多模塊項目來說,還是會有些其他的問題存在,例如,不同模塊間有相同的 groupId,version ;有時候,也會需要引入相同的依賴。這個時候,如果每個模塊都重復(fù)引入的話,結(jié)果就會造成冗余。作為一個遵循面向?qū)ο蟮某绦騿T來講,這樣的做法顯然是不合理的。
因此,Maven 也引入了類似的機制來解決這個問題,就是繼承的特性。
類似于 Java 中的父類與子類,我們也可以創(chuàng)建一個父模塊,讓其他的模塊作為子模塊來繼承該模塊,從而繼承父模塊中聲明的依賴以及配置。
對于父模塊來說,只是作為配置的公共模塊,是不需要代碼的,而且 pom.xml 文件中的 packaging 方式也是 pom,因此,我們可以將聚合模塊同時作為父模塊來使用,沒有必要再創(chuàng)建一個父模塊。(當(dāng)然,這里也是可以單獨創(chuàng)建父模塊的)
此時,我們查看 mall-core 或者 mall-account 的 pom.xml 文件。
我們可以看到 mall-core 模塊繼承了父模塊的坐標(biāo)信息,并重新定義了 artifactId 。這時候,我們在父模塊引入一個依賴,然后查看 mall-core 模塊的 pom.xml 文件,會發(fā)現(xiàn),在 mall-core 模塊中也會引入這個依賴。但是,實際上,我們并沒有在 mall-core 模塊中顯式的聲明這個依賴。
2.2 依賴管理
其實問題并沒有完全解決,并不是所有的子模塊都需要引入 fastjson-1.2.49.jar 這個依賴,那要怎么辦呢?
在 POM 中,我們可以在父模塊中聲明 dependencyManagement 元素,讓子模塊來繼承。dependencyManagement 元素并不會實際的引入依賴,但是可以起到很好的約束依賴的作用。
首先,我們在父模塊的 pom.xml 文件中聲明這個元素,并加入 fastjson-1.2.49.jar 這個依賴。
<properties>
<fastjson.version>1.2.49</fastjson.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
然后,我們在 mall-core 模塊中添加這個依賴,但是我們并不需要再聲明version。
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
</dependencies>
這時候,我們分別查看父模塊與子模塊所引入的依賴,會發(fā)現(xiàn),只有子模塊中有引入這個依賴,而父模塊中,并沒有。

我們通過 Maven 繼承的特性,來進行依賴管理,可以更好的控制依賴的引入。而 Maven 對于插件的管理,也存在類似的元素可以使用(pluginmanagement 元素),可以做到相同的效果。
3. 反應(yīng)堆
反應(yīng)堆指的是整個項目中所有模塊的構(gòu)建結(jié)構(gòu)。在本節(jié)的例子中,整個構(gòu)建結(jié)構(gòu)包括三個模塊。反應(yīng)堆不僅僅包括模塊本身,還包括了這三個模塊直接的相互依賴關(guān)系。
現(xiàn)在,我們的示例項目中,mall-core 和 mall-account 是不存在依賴關(guān)系的。父模塊中模塊的配置順序如下:
<!-- 模塊配置 -->
<modules>
<module>mall-core</module>
<module>mall-account</module>
</modules>
這里我們稍微做一下調(diào)整,用戶管理模塊可以提供接口給核心業(yè)務(wù)模塊來調(diào)用,因此,在 mall-core 模塊中引入 mall-account 依賴。重新進行項目構(gòu)建。
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO]
[INFO] mall-aggregator [pom]
[INFO] mall-account [jar]
[INFO] mall-core [jar]
[INFO]
[INFO] ----------------------< com.mic:mall-aggregator >-----------------------
[INFO] Building mall-aggregator 1.0.0-SNAPSHOT [1/3]
[INFO] --------------------------------[ pom ]---------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ mall-aggregator ---
[INFO]
[INFO] ------------------------< com.mic:mall-account >------------------------
[INFO] Building mall-account 1.0.0-SNAPSHOT [2/3]
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] ...
[INFO] -------------------------< com.mic:mall-core >--------------------------
[INFO] Building mall-core 1.0.0-SNAPSHOT [3/3]
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] ...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary for mall-aggregator 1.0.0-SNAPSHOT:
[INFO]
[INFO] mall-aggregator .................................... SUCCESS [ 0.220 s]
[INFO] mall-account ....................................... SUCCESS [ 1.006 s]
[INFO] mall-core .......................................... SUCCESS [ 0.132 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.533 s
[INFO] Finished at: 2020-05-05T14:25:48+08:00
[INFO] ------------------------------------------------------------------------
從構(gòu)建的結(jié)果來看,模塊的構(gòu)建順序并不是按照我們在父模塊中配置的順序進行的,而是 Maven 在經(jīng)過分析之后,生成反應(yīng)堆,根據(jù)反應(yīng)堆的構(gòu)建順序來進行構(gòu)建的。
實際上,Maven 會根據(jù)模塊間繼承與依賴的關(guān)系來形成一個有向非循環(huán)圖,并根據(jù)圖中標(biāo)記的順序,來生成反應(yīng)堆構(gòu)建順序,在構(gòu)建的時候,根據(jù)這個順序來進行構(gòu)建。本節(jié)中的實例項目的有向非循環(huán)圖如下:
在這個圖中,是不能出現(xiàn)循環(huán)的,假如我們在 mall-account 模塊中也添加入 mall-core 的依賴,再進行項目構(gòu)建的時候,Maven 則會報錯出來,提示我們這個反應(yīng)堆中存在循環(huán)引用。
[INFO] Scanning for projects...
[ERROR] [ERROR] The projects in the reactor contain a cyclic reference: Edge between 'Vertex{label='com.mic:mall-account:1.0.0-SNAPSHOT'}' and 'Vertex{label='com.mic:mall-core:1.0.0-SNAPSHOT'}' introduces to cycle in the graph com.m
ic:mall-core:1.0.0-SNAPSHOT --> com.mic:mall-account:1.0.0-SNAPSHOT --> com.mic:mall-core:1.0.0-SNAPSHOT @
[ERROR] The projects in the reactor contain a cyclic reference: Edge between 'Vertex{label='com.mic:mall-account:1.0.0-SNAPSHOT'}' and 'Vertex{label='com.mic:mall-core:1.0.0-SNAPSHOT'}' introduces to cycle in the graph com.mic:mall-
core:1.0.0-SNAPSHOT --> com.mic:mall-account:1.0.0-SNAPSHOT --> com.mic:mall-core:1.0.0-SNAPSHOT -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/ProjectCycleException
4. 小結(jié)
在本節(jié)的學(xué)習(xí)中,我們使用一個示例項目演示了 Maven 聚合與繼承兩個特性,以及兩者之間的關(guān)系,最后介紹了 Maven 構(gòu)建項目時候,反應(yīng)堆構(gòu)建順序的生成,以及一些注意事項。