Maven依賴傳遞,排除依賴和可選依賴


Maven 依賴傳遞是 Maven 的核心機制之一,它能夠一定程度上簡化 Maven 的依賴配置。本節我們將詳細介紹依賴傳遞及其相關概念。

依賴傳遞

如下圖所示,項目 A 依賴於項目 B,B 又依賴於項目 C,此時 B 是 A 的直接依賴,C 是 A 的間接依賴。

Maven 的依賴傳遞機制是指:不管 Maven 項目存在多少間接依賴, POM 中都只需要定義其直接依賴,不必定義任何間接依賴,Maven 會動讀取當前項目各個直接依賴的 POM,將那些必要的間接依賴以傳遞性依賴的形式引入到當前項目中。Maven 的依賴傳遞機制能夠幫助用戶一定程度上簡化 POM 的配置。


基於 A、B、C  三者的依賴關系,根據 Maven 的依賴傳遞機制,我們只需要在項目 A 的 POM 中定義其直接依賴 B,在項目 B 的 POM 中定義其直接依賴 C,Maven 會解析 A 的直接依賴 B的 POM ,將間接依賴 C 以傳遞性依賴的形式引入到項目 A 中。

通過這種依賴傳遞關系,可以使依賴關系樹迅速增長到一個很大的量級,很有可能會出現依賴重復,依賴沖突等情況,Maven 針對這些情況提供了如下功能進行處理。

  • 依賴范圍(Dependency scope)
  • 依賴調解(Dependency mediation)
  • 可選依賴(Optional dependencies)
  • 排除依賴(Excluded dependencies)
  • 依賴管理(Dependency management)

依賴范圍

首先,我們要知道 Maven 在對項目進行編譯、測試和運行時,會分別使用三套不同的 classpath。Maven 項目構建時,在不同階段引入到 classpath 中的依賴時不同的。例如編譯時,Maven 會將與編譯相關的依賴引入到編譯 classpath 中;測試時,Maven 會將與測試相關的的依賴引入到測試 classpath 中;運行時,Maven 會將與運行相關的依賴引入到運行 classpath 中。

我們可以在 POM 的依賴聲明使用 scope 元素來控制依賴與三種 classpath(編譯 classpath、測試 classpath、運行 classpath )之間的關系,這就是依賴范圍

Maven 具有以下 6 中常見的依賴范圍,如下表所示。

依賴范圍 描述
compile 編譯依賴范圍,scope 元素的缺省值。使用此依賴范圍的 Maven 依賴,對於三種 classpath 均有效,即該 Maven 依賴在上述三種 classpath 均會被引入。例如,log4j 在編譯、測試、運行過程都是必須的。
test 測試依賴范圍。使用此依賴范圍的 Maven 依賴,只對測試 classpath 有效。例如,Junit 依賴只有在測試階段才需要。
provided  已提供依賴范圍。使用此依賴范圍的 Maven 依賴,只對編譯 classpath 和測試 classpath 有效。例如,servlet-api 依賴對於編譯、測試階段而言是需要的,但是運行階段,由於外部容器已經提供,故不需要 Maven 重復引入該依賴
runtime  運行時依賴范圍。使用此依賴范圍的 Maven 依賴,只對測試 classpath、運行 classpath 有效。例如,JDBC 驅動實現依賴,其在編譯時只需 JDK 提供的 JDBC 接口即可,只有測試、運行階段才需要實現了 JDBC 接口的驅動。
system 系統依賴范圍,其效果與 provided 的依賴范圍一致。其用於添加非 Maven 倉庫的本地依賴,通過依賴元素 dependency 中的 systemPath 元素指定本地依賴的路徑。鑒於使用其會導致項目的可移植性降低,一般不推薦使用。
import 導入依賴范圍,該依賴范圍只能與 dependencyManagement 元素配合使用,其功能是將目標 pom.xml 文件中 dependencyManagement 的配置導入合並到當前 pom.xml 的 dependencyManagement 中。


依賴范圍與三種 classpath 的關系一覽表,如下所示。

依賴范圍 編譯 classpath 測試 classpath 運行 classpath 例子
compile log4j
test - - junit
provided - servlet-api
runtime - - JDBC-driver
system - 非 Maven 倉庫的本地依賴

依賴范圍對傳遞依賴的影響

項目 A 依賴於項目 B,B 又依賴於項目 C,此時我們可以將 A 對於 B 的依賴稱之為第一直接依賴,B 對於 C 的依賴稱之為第二直接依賴。

B 是 A 的直接依賴,C 是 A 的間接依賴,根據 Maven 的依賴傳遞機制,間接依賴 C 會以傳遞性依賴的形式引入到 A 中,但這種引入並不是無條件的,它會受到依賴范圍的影響。

傳遞性依賴的依賴范圍受第一直接依賴和第二直接依賴的范圍影響,如下表所示。

  compile test provided runtime
compile compile - - runtime
test test - - test
provided provided - provided provided
runtime runtime - - runtime


注:上表中,左邊第一列表示第一直接依賴的依賴范圍,上邊第一行表示第二直接依賴的依賴范圍。交叉部分的單元格的取值為傳遞性依賴的依賴范圍,若交叉單元格取值為“-”,則表示該傳遞性依賴不能被傳遞

通過上表,可以總結出以下規律(*):

  • 當第二直接依賴的范圍是 compile 時,傳遞性依賴的范圍與第一直接依賴的范圍一致;
  • 當第二直接依賴的范圍是 test 時,傳遞性依賴不會被傳遞;
  • 當第二直接依賴的范圍是 provided 時,只傳遞第一直接依賴的范圍也為 provided 的依賴,且傳遞性依賴的范圍也為 provided;
  • 當第二直接依賴的范圍是 runtime 時,傳遞性依賴的范圍與第一直接依賴的范圍一致,但 compile 例外,此時傳遞性依賴的范圍為 runtime。

依賴調節

Maven 的依賴傳遞機制可以簡化依賴的聲明,用戶只需要關心項目的直接依賴,而不必關心這些直接依賴會引入哪些間接依賴。但當一個間接依賴存在多條引入路徑時,為了避免出現依賴重復的問題,Maven 通過依賴調節來確定間接依賴的引入路徑。

依賴調節遵循以下兩條原則:

  1. 引入路徑短者優先
  2. 先聲明者優先


以上兩條原則,優先使用第一條原則解決,第一條原則無法解決,再使用第二條原則解決。

引入路徑短者優先

引入路徑短者優先,顧名思義,當一個間接依賴存在多條引入路徑時,引入路徑短的會被解析使用。

例如,A 存在這樣的依賴關系:
A->B->C->D(1.0)
A->X->D(2.0)

D 是 A 的間接依賴,但兩條引入路徑上有兩個不同的版本,很顯然不能同時引入,否則造成重復依賴的問題。根據 Maven 依賴調節的第一個原則:引入路徑短者優先,D(1.0)的路徑長度為 3,D(2.0)的路徑長度為 2,因此間接依賴 D(2.0)將從 A->X->D(2.0) 路徑引入到 A 中。

先聲明者優先

先聲明者優先,顧名思義,在引入路徑長度相同的前提下,POM 文件中依賴聲明的順序決定了間接依賴會不會被解析使用,順序靠前的優先使用。

例如,A 存在以下依賴關系:
A->B->D(1.0)
A->X->D(2.0)

D 是 A 的間接依賴,其兩條引入路徑的長度都是 2,此時 Maven 依賴調節的第一原則已經無法解決,需要使用第二原則:先聲明者優先。

A 的 POM 文件中配置如下。

<dependencies>
    ...      
    <dependency>
        ...
        <artifactId>B</artifactId>       
        ...
    </dependency>
    ...
    <dependency>
        ...
        <artifactId>X</artifactId>
        ...
    </dependency>
    ...
</dependencies>

有以上配置可以看出,由於 B 的依賴聲明比 X 靠前,所以間接依賴 D(1.0)將從 A->B->D(1.0) 路徑引入到 A 中。

Maven排除依賴和可選依賴

我們知道 Maven 依賴具有傳遞性,例如 A 依賴於 B,B 依賴於 C,在不考慮依賴范圍等因素的情況下,Maven 會根據依賴傳遞機制,將間接依賴 C 引入到 A 中。但如果 A 出於某種原因,希望將間接依賴 C 排除,那該怎么辦呢?Maven 為用戶提供了兩種解決方式:排除依賴(Dependency Exclusions)和可選依賴(Optional Dependencies)

排除依賴

假設存在這樣的依賴關系,A 依賴於 B,B 依賴於 X,B 又依賴於 Y。B 實現了兩個特性,其中一個特性依賴於 X,另一個特性依賴於 Y,且兩個特性是互斥的關系,用戶無法同時使用兩個特性,所以 A 需要排除 X,此時就可以在 A 中將間接依賴 X 排除。

排除依賴是通過在 A 中使用 exclusions 元素實現的,該元素下可以包含若干個 exclusion 子元素,用於排除若干個間接依賴,示例代碼如下。

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>net.biancheng.www</groupId>
    <artifactId>A</artifactId>
    <version>1.0-SNAPSHOT</version>
    <dependencies>
        <dependency>
            <groupId>net.biancheng.www</groupId>
            <artifactId>B</artifactId>
            <version>1.0-SNAPSHOT</version>
            <exclusions>
                <!-- 設置排除 -->
                <!-- 排除依賴必須基於直接依賴中的間接依賴設置為可以依賴為 false -->
                <!-- 設置當前依賴中是否使用間接依賴 -->
                <exclusion>
                    <!--設置具體排除-->
                    <groupId>net.biancheng.www</groupId>
                    <artifactId>X</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>
</project>

關於 exclusions 元素及排除依賴說明如下:

  • 排除依賴是控制當前項目是否使用其直接依賴傳遞下來的間接依賴;
  • exclusions 元素下可以包含若干個 exclusion 子元素,用於排除若干個間接依賴;
  • exclusion 元素用來設置具體排除的間接依賴,該元素包含兩個子元素:groupId 和 artifactId,用來確定需要排除的間接依賴的坐標信息;
  • exclusion 元素中只需要設置 groupId 和 artifactId 就可以確定需要排除的依賴,無需指定版本 version

可選依賴

與上文的應用場景相同,也是 A 希望排除間接依賴 X,除了在 B 中設置可選依賴外,我們還可以在 B 中將 X 設置為可選依賴。

設置可選依賴

在 B 的 POM 關於 X 的依賴聲明中使用 optional 元素,將其設置成可選依賴,示例配置如下。

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>net.biancheng.www</groupId>
    <artifactId>B</artifactId>
    <packaging>jar</packaging>
    <version>1.0-SNAPSHOT</version>
    <dependencies>
        <dependency>
            <groupId>net.biancheng.www</groupId>
            <artifactId>X</artifactId>
            <version>1.0-SNAPSHOT</version>
            <!--設置可選依賴  -->
            <optional>true</optional>
        </dependency>
    </dependencies>
</project>

關於 optional 元素及可選依賴說明如下:

  • 可選依賴用來控制當前依賴是否向下傳遞成為間接依賴;
  • optional 默認值為 false,表示可以向下傳遞稱為間接依賴;
  • 若 optional 元素取值為 true,則表示當前依賴不能向下傳遞成為間接依賴。

排除依賴 VS 可選依賴 

排除依賴和可選依賴都能在項目中將間接依賴排除在外,但兩者實現機制卻完全不一樣。

    • 排除依賴是控制當前項目是否使用其直接依賴傳遞下來的接間依賴;
    • 可選依賴是控制當前項目的依賴是否向下傳遞;
    • 可選依賴的優先級高於排除依賴
    • 若對於同一個間接依賴同時使用排除依賴和可選依賴進行設置,那么可選依賴的取值必須為 false,否則排除依賴無法生效。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM