我們?nèi)粘9ぷ髦薪?jīng)常使用到maven擂错,基本操作大家都會味滞,但是涉及到父子pom繼承和多模塊的時候,很多時候使用的就會很混亂钮呀。
本篇文章主要針對maven的父子pom繼承和多模塊展開一些討論剑鞍,默認(rèn)讀者以使用過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> <!-- 項目 -->
<version>1.0.0</version> <!-- 版本 -->
<!-- ....其他配置 -->
</project>
1.2 定義屬性
<project>
<properties>
<custom-data>hello</custom-data> <!-- 自定義一個屬性,在pom及子pom中使用${} 的形式使用 -->
<lombok-version>1.18.10</lombok-version> <!-- 定義lombok的版本號 -->
</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>
<!-- 實際使用的包,dependencyManagement 定義過版本蚂四,實際使用的時候不需要寫版本 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- .....其他包 -->
</dependencies>
</project>
1.5 排除依賴
<project>
<properties>
<custom-data>hello</custom-data> <!-- 自定義一個屬性光戈,在pom及子pom中使用${} 的形式使用 -->
<lombok-version>1.18.10</lombok-version> <!-- 定義lombok的版本號 -->
<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 ,也可以選擇在使用引用的時候排除 -->
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencyManagement>
</project>
注意: 排除依賴存在一些未知的風(fēng)險。比如 A-->B, B-->C遂赠, 當(dāng)B的某個實現(xiàn)需要C久妆, 而B的這實現(xiàn)會在A中運(yùn)行的時候,排除了C就可能出錯跷睦。
2 MAVEN依賴特性
2.1 maven的依賴存在傳遞性
如下:
A --> B , B-->C 則 A -- > B --> C
A 依賴了B包 筷弦, B中依賴C包 ,則 A如果自動會導(dǎo)入C ( 這個原因也是為什會有 排除依賴的存在,有時候A只想依賴B烂琴,而不想要C的時候爹殊。)
2.2 依賴順序原則
依賴的順序決定了最終使用的是哪個jar包,maven在解析依賴的時候的順序是奸绷。先按最短路徑梗夸,再按申明順序
最短路徑優(yōu)先
如 a--> b --> c1.1.0 , a-->d --> e -->c1.2.0 則最終a中用的 c1.1.0申明順序優(yōu)先
當(dāng)路徑相同的時候,不能判斷使用哪個号醉,這時候就是按照申明的順序 反症,如 a-->b-->c1.1.0 a-->d-->c1.2.0 此時,如果路徑c是一樣的扣癣,如果b的申明在d的前面惰帽,
在使用的是 c1.1.0, 反之則為 c1.2.0
3 MAVEN繼承特性(父子pom)
在pom文件中定義parent節(jié)點之后,子pom就可以繼承父pom中的依賴和屬性父虑。
通常使用這個特性该酗,在父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 , 這時候 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可以看做是項目結(jié)構(gòu)的描述爵嗅,通過<modules><module>...</module></modules>
來定義,可以一起來打包的一個整體笨蚁。
<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>
<!--
上面配置的項目結(jié)構(gòu)
learn
|-learn-api
|-learn-server
-->
5 MAVEN繼承與聚合的區(qū)別
繼承和聚合沒有絕對要求的對應(yīng)關(guān)系睹晒,他們目的上是不同。
繼承 講究的是提煉統(tǒng)一公共的部分括细,子項目都使用伪很,統(tǒng)一管理公共部分。(使用時候奋单,可以單獨定義一個父pom給所有的項目使用锉试,統(tǒng)一項目中的jar包)
聚合 更像是定義項目的結(jié)構(gòu),哪些作為一個整理览濒。
6 MAVEN最佳實踐
6.1 實踐場景
假設(shè)有下面一個實例呆盖,一個系統(tǒng)(fun-mall)有 用戶服務(wù)(user)、訂單服務(wù)(order)贷笛、支付服務(wù)(pay)应又、商品服務(wù)(product), 他們之間通過dubbo調(diào)用乏苦。那么我們的使用maven可以統(tǒng)一成如下結(jié)構(gòu)
xxx-api 提供dubbo調(diào)用的一些接口定義丁频,**建議盡量少的依賴第三方的包。理論上他只是定義接口的和數(shù)據(jù)模型的。不然席里,當(dāng)別人引用你的時候,可能出現(xiàn)第三方包的沖突拢驾,需要排除依賴**
xxx-server 啟動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
針對上面的接口奖磁,我們創(chuàng)建項目的時候有兩種比較合理的方式
6.2 實現(xiàn)方式1
- 創(chuàng)建一個項目 fun-mall 一個git倉庫地址,fun-mall下面包含四個module繁疤, 每個module下面有包含xxx-api,xxx-server 兩個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>
<!-- 實際依賴的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>
<!-- 實際依賴的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>
<!-- 實際依賴的jar和版本 -->
</dependency>
</dependencies>
<!-- 添加打包可運(yùn)行的jar或者war或者zip的配置 -->
<!-- 其他配置 -->
</project>
order吞彤、pay、product的配置叹放,同user模塊類似即可饰恕。
6.3 實現(xiàn)方式2
- 創(chuàng)建一個fun-mall 做個父項目,沒有模塊井仰。 然后分別創(chuàng)建四個 項目 user埋嵌、order、pay俱恶、product 繼承這個fun-mall 拱為5個git地址雹嗦,所有項目的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和上面在一個工程里面的方式是沒有區(qū)別的,只是代碼放在不同的倉庫地址里面而已合是。
考慮到不同模塊迭代速度不同了罪,每個服務(wù)有自己的代碼和分支進(jìn)行開發(fā),相比上面在整個一個git而言端仰,更加靈活捶惜,不用對其他的服務(wù)產(chǎn)生分支。
其次荔烧,如果涉及到不同服務(wù)給不同團(tuán)隊開發(fā)吱七,或者不同服務(wù)不需要看別人的實現(xiàn)的。只關(guān)心自己鹤竭,則分開倉庫很好的滿足了
7 MAVEN deploy時父pom的問題
我們在deploy jar的時候踊餐,經(jīng)常遇到一些因為父pom沒有推導(dǎo)致推包失敗的情況。所有一般推包看分為兩種情況來處理
7.1 子pom未使用父pom的變量
這種情況下
方式1:因為子pom沒有使用父pom的變量臀稚,可能考慮單獨deploy, 注釋掉<parent>, 然后確保自己有artfactId和version 吝岭,直接deploy.
方式2:先推一下父pom,在推子模塊。如果pom有很多子模塊(fun-mall的第一種)窜管,可考慮只推父pom(
mvn clean deploy -N
)散劫,
不要推子模塊(所有的模塊都推,連實現(xiàn)server可能都推了幕帆,沒必要)获搏,然后再推需要的jar(如,user-spi失乾,)
7.2 子pom有使用父pom的變量
這種情況下, 因為使用父模塊的變量常熙,不能使用注釋parent(不然不識別),所有只能先推一下父pom, 再推當(dāng)前jar, 如上面的方式二
7.3 記錄
- 跳過Assembly:
clean deploy -DskipAssembly=true
- 只推父pom:
mvn clean package deploy -Dmaven.test.skip=true -Drepository:snapshots -N
- 分析maven的依賴樹:
mvn dependency:tree >text.txt