我們?nèi)粘9ぷ髦薪?jīng)常使用到maven,基本操作大家都會(huì),但是涉及到父子pom繼承和多模塊的時(shí)候,很多時(shí)候使用的就會(huì)很混亂。
本篇文章主要針對(duì)maven的父子pom繼承和多模塊展開(kāi)一些討論,默認(rèn)讀者以使用過(guò)maven(可參考:https://www.ibofine.com/mavenbook/index.html )
1 MAVEN的關(guān)于依賴的基本配置
1.1 坐標(biāo)(定位三要素)
<project>
<groupId>xxx.xxxxx.xxxx</groupId> <!-- 公司業(yè)務(wù) -->
<artifactId>xxx-xxxx</artifactId> <!-- 項(xiàng)目 -->
<version>1.0.0</version> <!-- 版本 -->
<!-- ....其他配置 -->
</project>
1.2 定義屬性
<project>
<properties>
<custom-data>hello</custom-data> <!-- 自定義一個(gè)屬性,在pom及子pom中使用${} 的形式使用 -->
<lombok-version>1.18.10</lombok-version> <!-- 定義lombok的版本號(hào) -->
</properties>
</project>
1.3 申明依賴
<project>
<dependencyManagement>
<dependencies>
<!-- 定義好可能使用的包和版本 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok-version}</version> <!-- 版本統(tǒng)一放在屬性里面管理,方便看,此處引用 -->
</dependency>
<!-- .....其他包 -->
</dependencies>
</dependencyManagement>
</project>
1.4 使用依賴
<project>
<dependencies>
<!-- 實(shí)際使用的包,dependencyManagement 定義過(guò)版本,實(shí)際使用的時(shí)候不需要寫(xiě)版本 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- .....其他包 -->
</dependencies>
</project>
1.5 排除依賴
<project>
<properties>
<custom-data>hello</custom-data> <!-- 自定義一個(gè)屬性,在pom及子pom中使用${} 的形式使用 -->
<lombok-version>1.18.10</lombok-version> <!-- 定義lombok的版本號(hào) -->
<spring.version>5.2.4.RELEASE</spring.version>
</properties>
<dependencyManagement>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
<exclusions>
<exclusion> <!-- 排除日志包 commons-logging ,也可以選擇在使用引用的時(shí)候排除 -->
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencyManagement>
</project>
注意: 排除依賴存在一些未知的風(fēng)險(xiǎn)。比如 A-->B, B-->C, 當(dāng)B的某個(gè)實(shí)現(xiàn)需要C, 而B(niǎo)的這實(shí)現(xiàn)會(huì)在A中運(yùn)行的時(shí)候,排除了C就可能出錯(cuò)。
2 MAVEN依賴特性
2.1 maven的依賴存在傳遞性
如下:
A --> B , B-->C 則 A -- > B --> C
A 依賴了B包 , B中依賴C包 ,則 A如果自動(dòng)會(huì)導(dǎo)入C ( 這個(gè)原因也是為什會(huì)有 排除依賴的存在,有時(shí)候A只想依賴B,而不想要C的時(shí)候。)
2.2 依賴順序原則
依賴的順序決定了最終使用的是哪個(gè)jar包,maven在解析依賴的時(shí)候的順序是。先按最短路徑,再按申明順序
最短路徑優(yōu)先
如 a--> b --> c1.1.0 , a-->d --> e -->c1.2.0 則最終a中用的 c1.1.0申明順序優(yōu)先
當(dāng)路徑相同的時(shí)候,不能判斷使用哪個(gè),這時(shí)候就是按照申明的順序 ,如 a-->b-->c1.1.0 a-->d-->c1.2.0 此時(shí),如果路徑c是一樣的,如果b的申明在d的前面,
在使用的是 c1.1.0, 反之則為 c1.2.0
3 MAVEN繼承特性(父子pom)
在pom文件中定義parent節(jié)點(diǎn)之后,子pom就可以繼承父pom中的依賴和屬性。
通常使用這個(gè)特性,在父pom中統(tǒng)一一些公共的東西,如jar版本,定義的一些屬性等。主要就是抽離,統(tǒng)一管理。
<project>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository , 這時(shí)候 spring-boot-starter-parent 中定義的屬性和依賴都可以在當(dāng)前pom中使用 -->
</parent>
<groupId>com.fun</groupId>
<artifactId>learn</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>learn</name>
<description>Demo project for Spring Boot</description>
</project>
4 MAVEN聚合特性(module)
module可以看做是項(xiàng)目結(jié)構(gòu)的描述,通過(guò)<modules><module>...</module></modules> 來(lái)定義,可以一起來(lái)打包的一個(gè)整體。
<project>
<groupId>com.fun</groupId>
<artifactId>learn</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>learn</name>
<description>Demo project for Spring Boot</description>
<modules>
<module>learn-api</module>
<module>learn-server</module>
</modules>
</project>
<!--
上面配置的項(xiàng)目結(jié)構(gòu)
learn
|-learn-api
|-learn-server
-->
5 MAVEN繼承與聚合的區(qū)別
繼承和聚合沒(méi)有絕對(duì)要求的對(duì)應(yīng)關(guān)系,他們目的上是不同。
繼承 講究的是提煉統(tǒng)一公共的部分,子項(xiàng)目都使用,統(tǒng)一管理公共部分。(使用時(shí)候,可以單獨(dú)定義一個(gè)父pom給所有的項(xiàng)目使用,統(tǒng)一項(xiàng)目中的jar包)
聚合 更像是定義項(xiàng)目的結(jié)構(gòu),哪些作為一個(gè)整理。
6 MAVEN最佳實(shí)踐
6.1 實(shí)踐場(chǎng)景
假設(shè)有下面一個(gè)實(shí)例,一個(gè)系統(tǒng)(fun-mall)有 用戶服務(wù)(user)、訂單服務(wù)(order)、支付服務(wù)(pay)、商品服務(wù)(product), 他們之間通過(guò)dubbo調(diào)用。那么我們的使用maven可以統(tǒng)一成如下結(jié)構(gòu)
xxx-api 提供dubbo調(diào)用的一些接口定義,**建議盡量少的依賴第三方的包。理論上他只是定義接口的和數(shù)據(jù)模型的。不然,當(dāng)別人引用你的時(shí)候,可能出現(xiàn)第三方包的沖突,需要排除依賴**
xxx-server 啟動(dòng)jvm的進(jìn)程
fun-mall
|-- user
|-- user-api
|-- user-server
|-- order
|-- order-api
|-- order-server
|-- pay
|-- pay-api
|-- pay-server
|-- product
|-- product-api
|-- product-server
針對(duì)上面的接口,我們創(chuàng)建項(xiàng)目的時(shí)候有兩種比較合理的方式
6.2 實(shí)現(xiàn)方式1
- 創(chuàng)建一個(gè)項(xiàng)目 fun-mall 一個(gè)git倉(cāng)庫(kù)地址,fun-mall下面包含四個(gè)module, 每個(gè)module下面有包含xxx-api,xxx-server 兩個(gè)module, 所有模塊的父pom都使用fun-mall(繼承和聚合的區(qū)別)
fun-mall
|-- user
|-- user-api
|-- pom.xml
|-- user-server
|-- pom.xml
|-- pom.xml
|-- order
|-- order-api
|-- pom.xml
|-- order-server
|-- pom.xml
|-- pom.xml
|-- pay
|-- pay-api
|-- pom.xml
|-- pay-server
|-- pom.xml
|-- pom.xml
|-- product
|-- product-api
|-- product-server
|-- pom.xml
|-- pom.xml
具體配置
fun-mall的 pom.xml
<project>
<groupId>com.fun</groupId>
<artifactId>fun-mall</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>fun-mall</name>
<description>fun mall</description>
<packaging>pom</packaging> <!-- 注意是pom -->
<modules>
<module>user</module>
<module>order</module>
<module>pay</module>
<module>product</module>
</modules>
<properties>
<!-- 一些通用的屬性 -->
</properties>
<dependencyManagement>
<dependency>
<!-- 依賴的jar和版本申明 -->
</dependency>
</dependencyManagement>
<!-- 其他配置 -->
</project>
user 的pom.xml
<project>
<parent>
<groupId>com.fun</groupId>
<artifactId>fun-mall</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>user</artifactId>
<description>user module</description>
<packaging>pom</packaging> <!-- 注意是pom, 另外groupId和version繼承父類 -->
<modules>
<module>user-api</module>
<module>order-server</module>
</modules>
<dependencies>
<dependency>
<!-- 實(shí)際依賴的jar和版本 -->
</dependency>
</dependencies>
<!-- 其他配置 -->
</project>
user-api 的pom.xml
<project>
<parent>
<groupId>com.fun</groupId>
<artifactId>fun-mall</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>user-api</artifactId>
<description>user server api define </description>
<packaging>jar</packaging> <!-- 打包成jar提供出去給別的服務(wù)使用,另外groupId和version繼承父類,也可自己指定新的 -->
<dependencies>
<dependency>
<!-- 實(shí)際依賴的jar和版本 -->
</dependency>
</dependencies>
<!-- 其他配置 -->
</project>
user-server的pom.xml
<project>
<parent>
<groupId>com.fun</groupId>
<artifactId>fun-mall</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>user-server</artifactId>
<description>user server impl</description>
<packaging>jar</packaging> <!-- 打包成可運(yùn)行的jar或者war,或者zip等格式 。另外groupId和version繼承父類,也可自己指定新的-->
<dependencies>
<dependency>
<!-- 實(shí)際依賴的jar和版本 -->
</dependency>
</dependencies>
<!-- 添加打包可運(yùn)行的jar或者war或者zip的配置 -->
<!-- 其他配置 -->
</project>
order、pay、product的配置,同user模塊類似即可。
6.3 實(shí)現(xiàn)方式2
- 創(chuàng)建一個(gè)fun-mall 做個(gè)父項(xiàng)目,沒(méi)有模塊。 然后分別創(chuàng)建四個(gè) 項(xiàng)目 user、order、pay、product 繼承這個(gè)fun-mall 拱為5個(gè)git地址,所有項(xiàng)目的parent都是 fun-mall(繼承和聚合的區(qū)別)
fun-mall
|-- pom.xml
--------
|-- user
|-- user-api
|-- pom.xml
|-- user-server
|-- pom.xml
|-- pom.xml
--------
|-- order
|-- order-api
|-- pom.xml
|-- order-server
|-- pom.xml
|-- pom.xml
--------
|-- pay
|-- pay-api
|-- pom.xml
|-- pay-server
|-- pom.xml
|-- pom.xml
--------
|-- product
|-- product-api
|-- product-server
|-- pom.xml
這種方式的pom和上面在一個(gè)工程里面的方式是沒(méi)有區(qū)別的,只是代碼放在不同的倉(cāng)庫(kù)地址里面而已。
考慮到不同模塊迭代速度不同,每個(gè)服務(wù)有自己的代碼和分支進(jìn)行開(kāi)發(fā),相比上面在整個(gè)一個(gè)git而言,更加靈活,不用對(duì)其他的服務(wù)產(chǎn)生分支。
其次,如果涉及到不同服務(wù)給不同團(tuán)隊(duì)開(kāi)發(fā),或者不同服務(wù)不需要看別人的實(shí)現(xiàn)的。只關(guān)心自己,則分開(kāi)倉(cāng)庫(kù)很好的滿足了
7 MAVEN deploy時(shí)父pom的問(wèn)題
我們?cè)赿eploy jar的時(shí)候,經(jīng)常遇到一些因?yàn)楦竝om沒(méi)有推導(dǎo)致推包失敗的情況。所有一般推包看分為兩種情況來(lái)處理
7.1 子pom未使用父pom的變量
這種情況下
方式1:因?yàn)樽觩om沒(méi)有使用父pom的變量,可能考慮單獨(dú)deploy, 注釋掉<parent>, 然后確保自己有artfactId和version ,直接deploy.
方式2:先推一下父pom,在推子模塊。如果pom有很多子模塊(fun-mall的第一種),可考慮只推父pom(
mvn clean deploy -N),
不要推子模塊(所有的模塊都推,連實(shí)現(xiàn)server可能都推了,沒(méi)必要),然后再推需要的jar(如,user-spi,)
7.2 子pom有使用父pom的變量
這種情況下, 因?yàn)槭褂酶改K的變量,不能使用注釋parent(不然不識(shí)別),所有只能先推一下父pom, 再推當(dāng)前jar, 如上面的方式二
7.3 記錄
- 跳過(guò)Assembly:
clean deploy -DskipAssembly=true - 只推父pom:
mvn clean package deploy -Dmaven.test.skip=true -Drepository:snapshots -N - 分析maven的依賴樹(shù):
mvn dependency:tree >text.txt