依賴傳遞
依賴相關命令
mvn dependency:list:檢視當前專案所有依賴。
mvn dependency:tree:以樹的形式顯示當前專案的所有依賴,相比mvn dependency:list 列表顯示,能很清楚的看到某個依賴是通過哪條依賴路徑引入的。
mvn dependency:analyze:分析專案的依賴關係,並確定哪些依賴是:使用和宣告、使用和未宣告、未使用和宣告。
依賴的傳遞性
如有依賴關係為A->B->C,A依賴B,稱為直接依賴。A本身不依賴C,但C通過B傳遞給A,稱C為A的傳遞性依賴。
通過mvn dependency:list檢視A專案的依賴列表,可以看到依賴B和C:
[INFO] --- maven-dependency-plugin:2.8:list (default-cli) @ A ---
[INFO]
[INFO] The following files have been resolved:
[INFO] com.nocoffee:B:jar:0.0.1-SNAPSHOT:compile
[INFO] junit:junit:jar:3.8.1:test
[INFO] com.nocoffee:C:jar:0.0.1-SNAPSHOT:compile
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
依賴調解
場景1:
路徑1:A->B->C(version:1.0)
路徑2:A->D->E->C(version:2.0)
通過兩條依賴路徑可以看出,A的傳遞性依賴的C有兩個不同版本,為了避免依賴重複,最終只能選擇一個。這種情況Maven採用路徑最近者優先的原則來處理,路徑1中C到A的距離比路徑2中C到A的距離要短,於是路徑1中C(version:1.0)最終被A依賴。
mvn dependency:tree 檢視依賴路徑:
[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ A ---
[INFO] com.nocoffee:A:jar:0.0.1-SNAPSHOT
[INFO] +- com.nocoffee:B:jar:0.0.1-SNAPSHOT:compile
[INFO] | \- com.nocoffee:C:jar:0.0.1-SNAPSHOT:compile
[INFO] | \- com.nocoffee:D:jar:0.0.1-SNAPSHOT:compile
[INFO] | \- com.nocoffee:E:jar:0.0.1-SNAPSHOT:compile
[INFO] \- junit:junit:jar:3.8.1:test
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
場景2:
路徑1:A->B->C(version:1.0)
路徑2:A->D->C(version:2.0)
路徑1和路徑2中C到A的距離是相同的,通過路徑最近者優先原則無法判斷該使用哪個依賴,此時Maven會使用第一宣告者優先原則進行選擇,第一宣告者優先原則是指在POM依賴中宣告順序最靠前的那個依賴會被選擇。在A的POM檔案中B的宣告靠前,於是C(version:1.0)會被選擇。
<!-- A的pom.xml中依賴部分-->
<dependencies>
<dependency>
<groupId>com.nocoffee</groupId>
<artifactId>B</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.nocoffee</groupId>
<artifactId>D</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
mvn dependency:tree 檢視依賴路徑:
[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ A ---
[INFO] com.nocoffee:A:jar:0.0.1-SNAPSHOT
[INFO] +- com.nocoffee:B:jar:0.0.1-SNAPSHOT:compile
[INFO] | \- com.nocoffee:C:jar:0.0.1-SNAPSHOT:compile
[INFO] +- com.nocoffee:D:jar:0.0.1-SNAPSHOT:compile
[INFO] \- junit:junit:jar:3.8.1:test
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
排除依賴
在場景2中,如果要使A依賴C(version:2.0) ,則可以配置排除依賴:
<dependencies>
<dependency>
<groupId>com.nocoffee</groupId>
<artifactId>B</artifactId>
<version>0.0.1-SNAPSHOT</version>
<exclusions>
<!-- 排除依賴 C -->
<exclusion>
<groupId>com.nocoffee</groupId>
<artifactId>C</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.nocoffee</groupId>
<artifactId>D</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
mvn dependency:tree 檢視依賴路徑,A不再通過B依賴C,而是通過D依賴C:
[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ A ---
[INFO] com.nocoffee:A:jar:0.0.1-SNAPSHOT
[INFO] +- com.nocoffee:B:jar:0.0.1-SNAPSHOT:compile
[INFO] +- com.nocoffee:D:jar:0.0.1-SNAPSHOT:compile
[INFO] | \- com.nocoffee:C:jar:0.0.1-SNAPSHOT:compile
[INFO] \- junit:junit:jar:3.8.1:test
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
可選依賴
可以將某個依賴配置為可選依賴,則該依賴不會參與依賴傳遞。
以場景2為例,可以在B的pom.xml裡將C配置為可選依賴,使A依賴D的C(version:2.0)。
<!-- B的pom.xml -->
<dependency>
<groupId>com.nocoffee</groupId>
<artifactId>C</artifactId>
<version>0.0.1-SNAPSHOT</version>
<!-- 設定可選依賴 -->
<optional>true</optional>
</dependency>
mvn dependency:tree 檢視依賴路徑,A不再通過B依賴C,而是通過D依賴C:
[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ A ---
[INFO] com.nocoffee:A:jar:0.0.1-SNAPSHOT
[INFO] +- com.nocoffee:B:jar:0.0.1-SNAPSHOT:compile
[INFO] +- com.nocoffee:D:jar:0.0.1-SNAPSHOT:compile
[INFO] | \- com.nocoffee:C:jar:0.0.1-SNAPSHOT:compile
[INFO] \- junit:junit:jar:3.8.1:test
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
依賴範圍
Maven在編譯、測試、執行時都會使用不同的classpath,依賴範圍是用來控制依賴和三種classpath的關係。
依賴範圍介紹
- compile:編譯依賴範圍,預設使用該依賴範圍,在所有classpath中都可用,並且依賴項將傳播到依賴專案。
- provided:已提供依賴範圍,只對於編譯和測試classpath有效,執行時無效,如Servlet API,此範圍不具有傳遞性。
- runtime:執行時依賴範圍,只對於測試和執行classpath有效,但在編譯主程式碼時無效。
- test:測試依賴範圍,只對於測試的classpath有效,僅適用於測試編譯和執行階段,如junit。此範圍不具有傳遞性。
- system:系統依賴範圍,該依賴於三種classpath的關係和provided依賴範圍完全一致。區別在於system依賴範圍必須通過systemPath元素顯示的指定依賴檔案的路徑。
- import:匯入依賴範圍,該依賴範圍不會對三種classpath產生影響,只有在
部分中的pom型別依賴項才支援此範圍,它指示要替換為指定POM的 部分中的有效依賴項列表的依賴項。由於它們被替換,具有匯入範圍的依賴項實際上不參與限制依賴項的傳遞性。
依賴範圍對依賴傳遞的影響
每個範圍(import匯入依賴範圍除外)以不同方式影響傳遞依賴性,如下表所示。以A->B->C依賴路徑為例,左邊第一列為第一直接依賴(B在A中的依賴範圍),最上面一行為第二直接依賴(C在B中的依賴範圍),交叉單元格為傳遞性依賴範圍(C在A中的依賴範圍)。
compile | provided | runtime | test | |
compile | compile(*) | - | runtime | - |
provided | provided | - | provided | - |
runtime | runtime | - | runtime | - |
test | test | - | test | - |