Maven快速入門(五)Maven的依賴管理

章為忠發表於2021-11-10

前面我們講了maven專案中的最重要的檔案:pom.xml 配置檔案相關內容。介紹了pom 是如何定義專案,如何新增依賴的jar 包的等。

 

我們知道,在Maven的生命週期中,存在編譯、測試、執行等過程,那麼有些依賴只用於測試,比如junit;有些依賴編譯用不到,只有執行的時候才能用到,比如mysql的驅動包在編譯期就用不到(編譯期用的是JDBC介面),而是在執行時用到的;還有些依賴,編譯期要用到,而執行期不需要提供,因為有些容器已經提供了,比如servlet-api在tomcat中已經提供了,我們只需要的是編譯期提供而已。

 

那麼Maven是如何管理各個jar包的依賴關係,jar包之間的依賴傳遞和依賴衝突呢?所以接下來就講一講maven的依賴管理。

 

一、宣告依賴

Maven 專案使用pom.xml 檔案,可以方便的管理專案中所有依賴的jar包,之前也介紹過pom.xml 檔案是如何定義依賴的。下面就以一段 junit依賴宣告,演示pom.xml 是如何定義相關依賴的:

<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>3.8.1</version>
        <type>jar</type>
        <scope>test</scope>
        <optional>false</optional>
        <exclusions>
            <exclusion></exclusion>
        </exclusions>
    </dependency>
</dependencies>

上面的示例我們定義了junit的依賴,而且junit只在測試階段生效。具體配置引數如下:

  • type:依賴型別,對應構件中定義的 packaging,可不宣告,預設為 jar;
  • scope:依賴範圍,大致有compile、provided、runtime、test、system等幾個;
  • optional:依賴是否可選;
  • exclusions:排除傳遞依賴。

總結說來,在pom.xml 配置檔案中,配置節點引入了節點,它主要管理依賴的範圍。大致有compile、provided、runtime、test、system等幾個。引入 節點排除某些依賴。解決了控制各個jar包的依賴範圍,jar包之間的依賴傳遞和依賴衝突的問題。

 

二、依賴範圍

Maven在執行不同的命令時(mvn package,mvn test,mvn install ……),會使用不同的 classpath,Maven 對應的有三套 classpath,即:編譯classpath、測試classpath,執行classpath。我們可在dependency中的scope元素中進行配置。從而決定了該依賴構件會被引入到哪一個 classpath 中。預設為:compile。Maven提供的依賴範圍如下:

  • compile:編譯依賴範圍,預設值。此選項對編譯、測試、執行三種 classpath 都有效,如 hibernate-core-3.6.5.Final.jar,表明在編譯、測試、執行的時候都需要該依賴;
  • test:測試依賴範圍。只對測試有效,表明只在測試的時候需要,在編譯和執行時將無法使用該類依賴,如 junit;
  • provided:已提供依賴範圍。編譯和測試有效,執行無效。如 servlet-api ,在專案執行時,tomcat 等容器已經提供,無需 Maven 重複引入;
  • runtime:執行時依賴範圍。測試和執行有效,編譯無效。如 jdbc 驅動實現,編譯時只需介面,測試或執行時才需要具體的 jdbc 驅動實現;
  • system:系統依賴範圍。和 provided 依賴範圍一致,也是編譯和測試有效,需要通過  顯示指定,且可以引用環境變數;
  • import:匯入依賴範圍。使用該選項,通常需要 pom,將目標 pom 的 dependencyManagement 配置匯入合併到當前 pom 的 dependencyManagement 元素。

需要注意的是,在打包階段,使用的是執行classpath。即引入到執行classpath中的Maven依賴會被一起打包。

 

三、依賴傳遞

所謂依賴傳遞,就是A依賴B,B依賴C,A就間接依賴C,那麼A與C直接的依賴關係就叫傳遞性依賴。

直接依賴又叫第一直接依賴,傳遞性依賴又叫第二直接依賴,其中第一直接依賴和第二直接依賴的依賴範圍,決定了傳遞性依賴的依賴範圍。

下面使用hibernate-core的依賴關係詳細介紹所謂的依賴傳遞:

image.png

如上圖所示,hibernate-core 依賴 hibernate-commons-annotations ,而 hibernate-commons-annotations 又依賴 slf4j-api ,hibernate-core 對 slf4j-api 的依賴就是傳遞依賴。pom.xml 檔案中我們只需要引入 hibernate-core 構件的依賴,Maven 會自動為我們引入依賴jar包及傳遞依賴的jar包,無需再手動引一遍相關依賴。所以不用擔心依賴衝突的問題。

那麼如何判定一個間接依賴是否有必要被引入呢?間接依賴被引入後其依賴範圍又是什麼呢?

其實很簡單,就是通過第一直接依賴的依賴範圍和第二直接依賴的依賴範圍之間的關係,來判定是否有必要引入間接依賴以及確定引入間接依賴後其依賴範圍,如下表所示:

image.png

所以,通過上表我們可以發現:

1)第二直接依賴的依賴範圍為compile : 傳遞依賴的依賴範圍同第一直接依賴的依賴範圍一樣。

2)第二直接依賴的依賴範圍為test : 傳遞依賴不會被引入。

3)第二直接依賴的依賴範圍為provided : 只有當第一直接依賴的依賴範圍亦為provided時,傳遞依賴才會被引入,且依賴範圍依然是provided。

4)第二直接依賴的依賴範圍為runtime : 除了第一直接依賴的依賴範圍為compile時傳遞依賴的依賴範圍為runtime外,其餘情況下,傳遞依賴的依賴範圍同第一直接依賴的依賴範圍一樣。

 

四、依賴衝突

通常我們不需要關心傳遞性依賴,但是當多個傳遞性依賴中有對同一構件不同版本的依賴時,如何解決呢?通常我們有一下兩個原則:

  • 短路徑優先:假如有以下依賴:A -> B -> C ->X(版本 1.0) 和 A -> D -> X(版本 2.0),則優先解析較短路徑的 X(版本 2.0);
  • 先宣告優先:即誰先宣告,誰被解析。

針對依賴衝突中的“短路徑優先”,那如果我們就想使用長路徑的依賴怎麼辦呢?這時可以使用依賴排除元素,顯示排除短路徑依賴。在非衝突的情況下,這種方法同樣有效。

 

五、解決依賴衝突

一般情況下我們不需要關心依賴的問題。而當依賴出問題時,我們需要知道該如何解決依賴衝突。解決依賴衝突的方式如下:

(1)首先,使用:mvn dependency:tree 命令檢視專案的依賴列表,

(2)然後,通過依賴列表,找出衝突的jar包。

(3)最後,可選依賴 option或是排除依賴 exclusions 處理相關衝突。

 

1.可選依賴 option

通過專案中的pom.xml中的dependency下的option元素中進行配置,只有顯式地配置專案中某依賴的option元素為true時,該依賴才是可選依賴;不設定該元素或值為false時,作用是:當某個間接依賴是可選依賴時,無論依賴範圍是什麼,其都不會因為傳遞性依賴機制而被引入。

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <!-- optional=true,依賴不會傳遞,該專案依賴devtools;之後依賴boot專案的專案如果想要使用devtools,需要重新引入 -->
            <optional>true</optional>
        </dependency>

上面的示例,當optional=true,devtools的依賴不會傳遞,該專案依賴devtools;之後依賴該專案的專案則不會依賴devtools元件,如果想要使用devtools,需要重新引入。

 

2.排除依賴 exclusions

我們知道,由於Maven的的傳遞性依賴機,有時候第三方元件B的C依賴由於版本(1.0)過低時。我們期望能夠將該間接依賴直接剔除出去,這樣不會影響到專案中其他依賴元件。這時可以通過exclusions元素實現。

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-core</artifactId>
  <exclusions>
    <exclusion>
      <artifactId>commons-logging</artifactId>
      <groupId>commons-logging</groupId>
    </exclusion>
  </exclusions>
</dependency>

上面,我們排除了spring-core元件忠的commons-logging。

 

最後

以上,我們就把Maven的依賴關係,Jar包之間的依賴傳遞和依賴衝突介紹完了。

 

 

 

相關文章