十分鐘解惑,讓你真正用好MAVEN

我們?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

  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

  1. 創(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
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容