Maven的依賴管理、傳遞、沖突、父子工程的繼承和聚合方式
一、基于IDEA 進行Maven依賴管理
(一)依賴管理概念
我們在從網(wǎng)上下載需要的依賴的時候,不同的jar包與jar包之間有依賴關(guān)系,且不同的版本之間也需要注意是否有沖突,使用Maven就可以幫助我們?nèi)ス芾砗妥詣咏鉀Q軟件包依賴問題,我們只需要關(guān)注我們需要的jar包和版本即可。
通過定義 POM 文件,Maven 能夠自動解析項目的依賴關(guān)系,并通過 Maven 倉庫自動下載和管理依賴,從而避免了手動下載和管理依賴的繁瑣工作和可能引發(fā)的版本沖突問題。
總之,Maven 的依賴管理使得軟件包依賴的管理,和使用更加智能和方便,簡化了開發(fā)過程中的工作,并提高了軟件質(zhì)量和可維護性。
(二)Maven工程核心信息配置和解讀(GAVP)
位置:pom.xml
<!-- 模型版本 -->
<modelVersion>4.0.0</modelVersion>
<!-- 公司或者組織的唯一標(biāo)志,并且配置時生成的路徑也是由此生成,
如com.companyname.project-group,maven會將該項目打成的jar包放本地路徑:
/com/companyname/project-group -->
<groupId>com.companyname.project-group</groupId>
<!-- 項目的唯一ID,一個groupId下面可能多個項目,就是靠artifactId來區(qū)分的 -->
<artifactId>project</artifactId>
<!-- 版本號 -->
<version>1.0.0</version>
<!--打包方式
默認:jar
jar指的是普通的java項目打包方式, 項目打成jar包
war指的是web項目打包方式,項目打成war包
pom不會將項目打包,這個項目作為父工程,被其他工程聚合或者繼承
-->
<packaging>jar/pom/war</packaging>(三)Maven工程依賴管理配置
位置:pom.xml
1.依賴管理和依賴添加
<!--
通過編寫依賴jar包的gav必要屬性,引入第三方依賴!
scope屬性是可選的,可以指定依賴生效范圍!
依賴信息查詢方式:
1. maven倉庫信息官網(wǎng) https://mvnrepository.com/
2. mavensearch插件搜索
-->
<dependencies>
<!-- 引入具體的依賴包 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
<!-- 依賴范圍 -->
<scope>runtime</scope>
</dependency>
</dependencies>2.依賴版本統(tǒng)一提取和維護
Maven中,有個<properties>標(biāo)簽,該標(biāo)簽用于定義項目構(gòu)建過程中可以使用的屬性。
通過在properties標(biāo)簽中自定義管理依賴的版本號,方便我們統(tǒng)一管理各種依賴的版本,當(dāng)需要升級或更改依賴版本時,只需在<properties>標(biāo)簽中修改一次,而不需要逐個查找并替換POM文件中的每個依賴標(biāo)簽,這大大提高了維護的效率。
<properties>
<!-- 通過maven規(guī)定的固定的key,配置maven的參數(shù):-->
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!--聲明版本:命名隨便,內(nèi)部制定版本號即可-->
<junit.version>5.9.2</junit.version>
</properties>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<!--引用properties聲明版本 -->
<version>${junit.version}</version>
<!--引用依賴的運行范圍(盡量不要更改) -->
<scope>test</scope>
</dependency>
</dependencies>(四)依賴范圍
通過設(shè)置坐標(biāo)的依賴范圍(scope),可以設(shè)置對應(yīng)jar包的作用范圍(三種classpath):編譯環(huán)境(在main包下)、測試環(huán)境(在test包下)、運行環(huán)境(打包后的jar包或war包是否有對應(yīng)的依賴包)。
scope標(biāo)簽中的依賴范圍可以修改,但是不建議,在https://mvnrepository.com/中搜索出來的依賴范圍是什么,就是什么,盡量不要修改!
| 依賴范圍 | 描述 |
|---|---|
| compile | 編譯依賴范圍,scope 元素的缺省值,即如果下載的依賴沒有scope標(biāo)簽,默認是compile范圍。使用此依賴范圍的 Maven 依賴,對于三種 classpath 均有效,即該 Maven 依賴在上述三種 classpath 均會被引入。例如,log4j 在編譯、測試、運行過程都是必須的。 |
| test | 測試依賴范圍。使用此依賴范圍的 Maven 依賴,只對測試 classpath 有效。例如,Junit 依賴只有在測試階段才需要。 |
| provided | 已提供依賴范圍。使用此依賴范圍的 Maven 依賴,只對編譯 classpath 和測試 classpath 有效。例如,servlet-api 依賴對于編譯、測試階段而言是需要的,但是運行階段,由于外部容器已經(jīng)提供,故不需要 Maven 重復(fù)引入該依賴。 |
| runtime | 運行時依賴范圍。使用此依賴范圍的 Maven 依賴,只對測試 classpath、運行 classpath 有效。例如,JDBC 驅(qū)動實現(xiàn)依賴,其在編譯時只需 JDK 提供的 JDBC 接口即可,只有測試、運行階段才需要實現(xiàn)了 JDBC 接口的驅(qū)動。 |
| system | 系統(tǒng)依賴范圍,其效果與 provided 的依賴范圍一致。其用于添加非 Maven 倉庫的本地依賴,通過依賴元素 dependency 中的 systemPath 元素指定本地依賴的路徑。鑒于使用其會導(dǎo)致項目的可移植性降低,一般不推薦使用。 |
| import | 導(dǎo)入依賴范圍,該依賴范圍只能與 dependencyManagement 元素配合使用,其功能是將目標(biāo) pom.xml 文件中 dependencyManagement 的配置導(dǎo)入合并到當(dāng)前 pom.xml 的 dependencyManagement 中。 |
(五)Maven工程依賴下載失敗錯誤解決(重點)
1.依賴下載失敗的主要原因
- 下載依賴時出現(xiàn)網(wǎng)絡(luò)故障或倉庫服務(wù)器宕機等原因,導(dǎo)致無法連接至 Maven 倉庫,從而無法下載依賴。
- 依賴項的版本號或配置文件中的版本號錯誤,或者依賴項沒有正確定義,導(dǎo)致 Maven 下載的依賴項與實際需要的不一致,從而引發(fā)錯誤。
- 本地 Maven 倉庫或緩存被污染或損壞,導(dǎo)致 Maven 無法正確地使用現(xiàn)有的依賴項。
2.解決方案
- 檢查網(wǎng)絡(luò)連接和 Maven 倉庫服務(wù)器狀態(tài)。
- 確保依賴項的版本號與項目對應(yīng)的版本號匹配,并檢查 POM 文件中的依賴項是否正確。
- 清除本地 Maven 倉庫緩存(lastUpdated 文件),因為只要存在lastupdated緩存文件,刷新也不會重新下載。本地倉庫中,根據(jù)依賴的gav屬性依次向下查找文件夾,最終刪除內(nèi)部的文件,刷新重新下載即可!

(六)Maven工程Build構(gòu)建配置
項目構(gòu)建是指將源代碼、依賴庫和資源文件等轉(zhuǎn)換成可執(zhí)行或可部署的應(yīng)用程序的過程,在這個過程中包括編譯源代碼、鏈接依賴庫、打包和部署等多個步驟。(將一系列的原材料生產(chǎn)為一個產(chǎn)品,即打成jar包或war包的過程)
默認情況下,構(gòu)建不需要額外配置,都有對應(yīng)的缺省配置。當(dāng)然了,我們也可以在pom.xml定制一些配置,來修改默認構(gòu)建的行為和產(chǎn)物。
例如:
- 指定構(gòu)建打包文件的名稱,非默認名稱
- 制定構(gòu)建打包時,指定包含文件格式和排除文件
- 打包插件版本過低,配置更高版本插件
構(gòu)建配置是在pom.xml / build標(biāo)簽中指定!
1.指定打包命名
<!-- 默認的打包名稱:artifactid+verson.打包方式 --> <build> <finalName>定義打包名稱</finalName> </build>
使用案例:不指定打包名稱默認是maven_web-1.0-SNAPSHOT.war

2.指定打包文件
如果在java文件夾中添加java類,resources文件夾中添加配置文件,即默認情況下,按照maven工程結(jié)構(gòu)放置的文件,會默認被編譯和打包到classes文件夾下:

但是如果我們不按照要求將文件放在對應(yīng)的文件夾下,又想要將文件打包到classes中,例如mybatis中有時會將用于編寫SQL語句的映射文件,和mapper接口都寫在src/main/java下的某個包中,此時映射文件也不會被打包。
解決方案:使用resources標(biāo)簽,指定要打包資源的文件夾,要把哪些靜態(tài)資源打包到 classes根目錄下。
<build>
<!--設(shè)置要打包的資源位置-->
<resources>
<resource>
<!--設(shè)置資源所在目錄-->
<directory>src/main/java</directory>
<includes>
<!--設(shè)置包含的資源類型-->
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</build>此時,不在resources文件夾下的文件也被成功打包!

3.配置依賴插件
dependencies標(biāo)簽下引入開發(fā)需要的jar包,我們可以在build/plugins/plugin標(biāo)簽引入插件。常用的插件:修改jdk版本、tomcat插件、mybatis分頁插件、mybatis逆向工程插件等等。pom.xml文件中配置了tomcat插件,就可以省略在Edit Configurations中配置tomcat了。
具體步驟參考我的另一篇文章:《Maven3.8.1使用Tomcat8插件啟動項目的方法(親測有效)》
二、Maven依賴傳遞
(一)Maven依賴傳遞特性
概念
假如有Maven項目A,項目B依賴A,項目C依賴B。那么我們可以說 C依賴A。也就是說,依賴的關(guān)系為:C—>B—>A, 那么我們執(zhí)行項目C時,會自動把B、A都下載導(dǎo)入到C項目的jar包文件夾中,這就是依賴的傳遞性。
作用
- 簡化依賴導(dǎo)入過程
- 確保依賴版本正確
案例演示:
創(chuàng)建2個項目,maven_A和maven_B:

在maven_A的pom.xml文件中引入maven_B的坐標(biāo):

在maven_B的pom.xml文件中配置druid依賴,更新maven,發(fā)現(xiàn)maven_A項目中也能使用druid

使用mvn dependency:tree命令也可以查看依賴關(guān)系:

也可以使用下面的命令在文件中查看依賴關(guān)系:
mvn dependency:build-classpath > classpath.txt

(二)傳遞的原則
在 A 依賴 B,B 依賴 C 的前提下,C 是否能夠傳遞到 A,取決于 B 依賴 C 時使用的依賴范圍以及配置:只有scope標(biāo)簽是compile的才能傳遞,test和provided依賴范圍都不能傳遞,需要手動配置!scope標(biāo)簽中的依賴范圍可以修改,但是不建議,在https://mvnrepository.com/中搜索出來的依賴范圍是什么,就是什么,盡量不要修改!

另外,還有依賴傳遞的終止標(biāo)簽:<optional>true</optional>
若配置了optional標(biāo)簽,則不能傳遞
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
<optional>true</optional>
</dependency>
(三)依賴傳遞終止的三個條件
- 非compile范圍進行依賴傳遞
- 使用optional配置終止傳遞
- 依賴沖突(傳遞的依賴已經(jīng)存在)
三、Maven依賴沖突
當(dāng)直接引用或者間接引用出現(xiàn)了相同的jar包! 這時呢,一個項目就會出現(xiàn)相同的重復(fù)jar包,這就算作沖突!依賴沖突避免出現(xiàn)重復(fù)依賴,并且終止依賴傳遞!

maven自動解決依賴沖突問題能力,會按照自己的原則,進行重復(fù)依賴選擇。同時也提供了手動解決的沖突的方式,不過不推薦!
解決依賴沖突(如何選擇重復(fù)依賴)方式
1.自動選擇原則
1.1依賴沖突之短路優(yōu)先原則
- 短路優(yōu)先原則(第一原則)
- A—>B—>C—>D—>E—>X(version 0.0.1)
- A—>F—>X(version 0.0.2)
- 則A依賴于X(version 0.0.2)。
示例:
maven_B的pom.xml:
<dependencies>
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
</dependency>
</dependencies>maven_A的pom.xml:
<dependencies>
<dependency>
<groupId>com.alibaba.maven</groupId>
<artifactId>maven_B</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.7</version>
</dependency>
</dependencies>查看maven_A的依賴樹:maven_B中的druid 1.2.8版本被排除,maven_A中使用的是druid 1.2.7


如果你的重復(fù)依賴沒有置灰,把maven_A的pom.xml文件中,dependencies下的所有dependency清空,刷新后重新添加依賴,就能看到置灰的依賴了,重復(fù)依賴沒有置灰是IDEA的顯示問題。
1.2依賴沖突之聲明優(yōu)先原則
- 依賴路徑長度相同情況下,則“先聲明優(yōu)先”(第二原則)
- A—>E—>X(version 0.0.1)
- A—>F—>X(version 0.0.2)
- 在<depencies></depencies>中,先聲明的,路徑相同,會優(yōu)先選擇!
- 在A的pom.xml文件中,如果先配置E,那么就使用的是0.0.1版本!
- 如果先配置F,那么就使用的是0.0.2版本!
示例:
創(chuàng)建項目maven_C,其pom.xml文件如下:
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.7</version>
</dependency>
</dependencies>maven_B的pom.xml:
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
</dependency>
</dependencies>maven_A的pom.xml:
<dependencies>
<dependency>
<groupId>com.alibaba.maven</groupId>
<artifactId>maven_B</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.alibaba.maven</groupId>
<artifactId>maven_C</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>查看maven_A的依賴樹:maven_B先聲明,就使用maven_B的druid 1.2.8版本


調(diào)換一下:
<dependencies>
<dependency>
<groupId>com.alibaba.maven</groupId>
<artifactId>maven_C</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.alibaba.maven</groupId>
<artifactId>maven_B</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>查看maven_A的依賴樹:maven_C先聲明,就使用maven_C的druid 1.2.7版本


2.手動排除
我們知道,先聲明的就會被先使用,如果只想使用maven_C中的druid 1.2.7的依賴,可以手動排除依賴,使用exclusions標(biāo)簽,里面可以嵌套多個exclusion標(biāo)簽,exclusion標(biāo)簽中只需寫明要排除的G和A坐標(biāo)即可:
<dependencies>
<dependency>
<groupId>com.alibaba.maven</groupId>
<artifactId>maven_B</artifactId>
<version>1.0-SNAPSHOT</version>
<!--依賴排除-->
<exclusions>
<exclusion>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.alibaba.maven</groupId>
<artifactId>maven_C</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>查看依賴樹:maven_B中的druid 1.2.8被成功排除!


四、Maven工程繼承和聚合關(guān)系
(一)Maven工程繼承關(guān)系
1.繼承概念
Maven 繼承是指在 Maven 的項目中,讓一個項目從另一個項目中繼承配置信息的機制。繼承可以讓我們在多個項目中共享同一配置信息,簡化項目的管理和維護工作。
2.繼承作用與背景
在父工程中統(tǒng)一管理項目中的依賴信息。
它的背景是:
- 對一個比較大型的項目進行了模塊拆分。
- 一個 project 下面,創(chuàng)建了很多個 module。
- 每一個 module 都需要配置自己的依賴信息。
它背后的需求是:
- 對一個比較大型的項目進行了模塊拆分。
- 一個 project 下面,創(chuàng)建了很多個 module。
- 每一個 module 都需要配置自己的依賴信息。
- 在每一個 module 中各自維護各自的依賴信息很容易發(fā)生出入,不易統(tǒng)一管理。
- 使用同一個框架內(nèi)的不同 jar 包,它們應(yīng)該是同一個版本,所以整個項目中使用的框架版本需要統(tǒng)一。
- 使用框架時所需要的 jar 包組合(或者說依賴信息組合)需要經(jīng)過長期摸索和反復(fù)調(diào)試,最終確定一個可用組合。這個耗費很大精力總結(jié)出來的方案不應(yīng)該在新的項目中重新摸索。 通過在父工程中為整個項目維護依賴信息的組合既保證了整個項目使用規(guī)范、準(zhǔn)確的 jar 包;又能夠?qū)⒁酝慕?jīng)驗沉淀下來,節(jié)約時間和精力。
創(chuàng)建一個父工程——maven_parent:
<groupId>com.alibaba.maven</groupId> <artifactId>maven_parent</artifactId> <version>1.0-SNAPSHOT</version> <!-- 當(dāng)前工程作為父工程,它要去管理子工程,所以打包方式必須是 pom --> <packaging>pom</packaging>
創(chuàng)建一個子工程——maven_son:


<!-- 使用parent標(biāo)簽指定當(dāng)前工程的父工程 -->
<parent>
<!--父工程的坐標(biāo)-->
<artifactId>maven_parent</artifactId>
<groupId>com.alibaba.maven</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<!-- 子工程的坐標(biāo) -->
<!-- 如果子工程坐標(biāo)中的groupId和version與父工程一致,那么可以省略 -->
<artifactId>maven_son</artifactId>創(chuàng)建好子工程maven_son后,父工程maven_parent的pom.xml文件也會發(fā)生改變:
<groupId>com.alibaba.maven</groupId>
<artifactId>maven_parent</artifactId>
<version>1.0-SNAPSHOT</version>
<!-- 創(chuàng)建子工程時,選擇父工程后,會在父工程中自動生成: -->
<modules>
<module>maven_son</module>
</modules>
<!-- 當(dāng)前工程作為父工程,它要去管理子工程,所以打包方式必須是 pom -->
<packaging>pom</packaging>在父工程maven_parent添加依賴:
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.7</version>
</dependency>
</dependencies>子工程會無條件繼承父工程中的依賴:

甚至包括scope是test和provide的:

3.父工程依賴統(tǒng)一管理
在使用父工程的時候,一般很少直接使用dependencies,讓所有依賴直接被子工程繼承,而是使用dependencyManagement來統(tǒng)一管理依賴:
<!-- 使用dependencyManagement標(biāo)簽配置對依賴的管理 -->
<!-- 被管理的依賴并沒有真正被引入到工程 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.7</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.9.2</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
</dependencyManagement>此時子工程中就需要手動選擇需要繼承父工程中的依賴:

<!-- 子工程引用父工程中的依賴信息時,可以把版本號去掉。 -->
<!-- 把版本號去掉就表示子工程中這個依賴的版本由父工程決定。 -->
<!-- 具體來說是由父工程的dependencyManagement來決定。 -->
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
</dependencies>

(二)Maven工程聚合關(guān)系
1.聚合概念
Maven 聚合是指將多個項目組織到一個父級項目中,以便一起構(gòu)建和管理的機制。聚合可以幫助我們更好地管理一組相關(guān)的子項目,同時簡化它們的構(gòu)建和部署過程。
2.聚合作用
- 管理多個子項目:通過聚合,可以將多個子項目組織在一起,方便管理和維護。
- 構(gòu)建和發(fā)布一組相關(guān)的項目:通過聚合,可以在一個命令中構(gòu)建和發(fā)布多個相關(guān)的項目,簡化了部署和維護工作。
- 優(yōu)化構(gòu)建順序:通過聚合,可以對多個項目進行順序控制,避免出現(xiàn)構(gòu)建依賴混亂導(dǎo)致構(gòu)建失敗的情況。
- 統(tǒng)一管理依賴項:通過聚合,可以在父項目中管理公共依賴項和插件,避免重復(fù)定義。
3.聚合語法與使用方法
父項目中包含的子項目列表。
當(dāng)我們對父工程進行了操作,比如package打包,里面的子工程也會進行相同的操作,提高開發(fā)效率。
<groupId>com.alibaba.maven</groupId>
<artifactId>maven_parent</artifactId>
<version>1.0-SNAPSHOT</version>
<modules>
<module>maven_son</module>
<module>../maven_A</module>
<module>../maven_web</module>
</modules>
<!-- 當(dāng)前工程作為父工程,它要去管理子工程,所以打包方式必須是 pom -->
<packaging>pom</packaging>父工程執(zhí)行package命令:

對應(yīng)的子工程會一鍵打包:

總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
解決springboot自定義注解AOP在controller上導(dǎo)致controller注入失敗問題
這篇文章主要介紹了解決springboot自定義注解AOP在controller上導(dǎo)致controller注入失敗問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-10-10
淺談SpringMVC之視圖解析器(ViewResolver)
本篇文章主要介紹了淺談SpringMVC之視圖解析器(ViewResolver),具有一定的參考價值,有興趣的可以了解一下2017-08-08
Java 處理超大數(shù)類型之BigInteger案例詳解
這篇文章主要介紹了Java 處理超大數(shù)類型之BigInteger案例詳解,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下2021-09-09

