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

我們通過 Maven 繼承的特性,來進(jìn)行依賴管理,可以更好的控制依賴的引入。而 Maven 對(duì)于插件的管理,也存在類似的元素可以使用(pluginmanagement 元素),可以做到相同的效果。
3. 反應(yīng)堆
反應(yīng)堆指的是整個(gè)項(xiàng)目中所有模塊的構(gòu)建結(jié)構(gòu)。在本節(jié)的例子中,整個(gè)構(gòu)建結(jié)構(gòu)包括三個(gè)模塊。反應(yīng)堆不僅僅包括模塊本身,還包括了這三個(gè)模塊直接的相互依賴關(guān)系。
現(xiàn)在,我們的示例項(xiàng)目中,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 依賴。重新進(jìn)行項(xiàng)目構(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)建順序并不是按照我們?cè)诟改K中配置的順序進(jìn)行的,而是 Maven 在經(jīng)過分析之后,生成反應(yīng)堆,根據(jù)反應(yīng)堆的構(gòu)建順序來進(jìn)行構(gòu)建的。
實(shí)際上,Maven 會(huì)根據(jù)模塊間繼承與依賴的關(guān)系來形成一個(gè)有向非循環(huán)圖,并根據(jù)圖中標(biāo)記的順序,來生成反應(yīng)堆構(gòu)建順序,在構(gòu)建的時(shí)候,根據(jù)這個(gè)順序來進(jìn)行構(gòu)建。本節(jié)中的實(shí)例項(xiàng)目的有向非循環(huán)圖如下:
在這個(gè)圖中,是不能出現(xiàn)循環(huán)的,假如我們?cè)?mall-account 模塊中也添加入 mall-core 的依賴,再進(jìn)行項(xiàng)目構(gòu)建的時(shí)候,Maven 則會(huì)報(bào)錯(cuò)出來,提示我們這個(gè)反應(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í)中,我們使用一個(gè)示例項(xiàng)目演示了 Maven 聚合與繼承兩個(gè)特性,以及兩者之間的關(guān)系,最后介紹了 Maven 構(gòu)建項(xiàng)目時(shí)候,反應(yīng)堆構(gòu)建順序的生成,以及一些注意事項(xiàng)。