詳解maven依賴問題

約定配置

Maven 提倡使用一個共同的標準目錄結構,Maven 使用約定優於配置的原則,大家儘可能的遵守這樣的目錄結構,如下所示:
在這裏插入圖片描述

basedir 可以通過:System.getProperty('basedir') 獲取到變量的值

maven他自己就知道你項目的源碼、資源、測試代碼、打包輸出的位置,這些都是maven規定好的.

pom文件

POM( Project Object Model,項目對象模型 ) 是 Maven 工程的基本工作單元,是一個XML文件,包含了項目的基本信息,用於描述項目如何構件,聲明項目依賴,等等。

執行任務或目標時,Maven 會在當前目錄中查找 POM。它讀取 POM,獲取所需的配置信息,然後執行目標。

POM 中可以指定以下配置

  • 項目依賴
  • 插件
  • 執行目標
  • 項目構件 profile
  • 項目版本
  • 項目開發者列表
  • 相關郵件列表信息

maven座標

  • goupId:定義當前構件所屬的組,通常與域名反向一一對應。
  • artifactId:項目組中構件的編號。
  • version:當前構件的版本號,每個構件可能會發布多個版本,通過版本號來區分不同版本的構件。
  • package:定義該構件的打包方式,比如我們需要把項目打成jar包,採用java -jar去運行這個jar包,那這個值爲jar;若當前是一個web項目,需要打成war包部署到tomcat中,那這個值就是war,可選(jar、war、ear、pom、maven-plugin),比較常用的是jar、war、pom,packeage可以省略,默認爲jar

maven導入依賴的構件

  • dependencies元素中可以包含多個dependency,每個dependency就表示當前項目需要依賴的一個構件的信息
  • dependency中groupId、artifactId、version是定位一個構件必須要提供的信息,所以這幾個是必須的
  • type:依賴的類型,表示所要依賴的構件的類型,對應於被依賴的構件的packaging。大部分情況下,該元素不被聲明,默認值爲jar,表示被依賴的構件是一個jar包。
  • scope:依賴的範圍,後面詳解
  • option:標記依賴是否可選,後面詳解
  • exclusions:用來排除傳遞性的依賴

maven依賴範圍(scope)

scope是用來控制被依賴的構件與classpath的關係(編譯、打包、運行所用到的classpath),scope有以下幾種值:

  • compile
    編譯依賴範圍,如果沒有指定,默認使用該依賴範圍,對於編譯源碼、編譯測試代碼、測試、運行4種classpath都有效,比如上面的spring-web。

  • test
    測試依賴範圍,使用此依賴範圍的maven依賴,只對編譯測試、運行測試的classpath有效,在編譯主代碼、運行項目時無法使用此類依賴。比如junit,它只有在編譯測試代碼及運行測試的時候才需要。

  • provide
    已提供依賴範圍。表示項目的運行環境中已經提供了所需要的構件,對於此依賴範圍的maven依賴,對於編譯源碼、編譯測試、運行測試中classpath有效,但在運行時無效。比如上面說到的servlet-api,這個在編譯和測試的時候需要用到,但是在運行的時候,web容器已經提供了,就不需要maven幫忙引入了。

  • runtime
    運行時依賴範圍,使用此依賴範圍的maven依賴,對於編譯測試、運行測試和運行項目的classpath有效,但在編譯主代碼時無效,比如jdbc驅動實現,運行的時候才需要具體的jdbc驅動實現。

  • system
    系統依賴範圍,該依賴與3中classpath的關係,和provided依賴範圍完全一致。但是,使用system範圍的依賴時必須通過systemPath元素顯示第指定依賴文件的路徑。這種依賴直接依賴於本地路徑中的構件,可能每個開發者機器中構件的路徑不一致,所以如果使用這種寫法,你的機器中可能沒有問題,別人的機器中就會有問題,所以建議謹慎使用

依賴範圍與classpath的關係如下:
在這裏插入圖片描述

scope如果對於運行範圍有效,意思是指依賴的jar包會被打包到項目的運行包中,最後運行的時候會被添加到classpath中運行。如果scope對於運行項目無效,那麼項目打包的時候,這些依賴不會被打包到運行包中。

依賴的傳遞

只引入了spring-web依賴,而spring-web又依賴了spring-beans、spring-core,依賴也被自動加進來了,這種叫做依賴的傳遞。

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.1.0.RELEASE</version>
</dependency>

在這裏插入圖片描述

scope元素的值會對這種傳遞依賴會有影響

假設A依賴於B,B依賴於C,我們說A對於B是第一直接依賴,B對於C是第二直接依賴,而A對於C是傳遞性依賴,而第一直接依賴的scope和第二直接依賴的scope決定了傳遞依賴的範圍,即決定了A對於C的scope的值。

下面我們用表格來列一下這種依賴的效果,表格最左邊一列表示第一直接依賴(即A->B的scope的值),而表格中的第一行表示第二直接依賴(即B->C的scope的值),行列交叉的值顯示的是A對於C最後產生的依賴效果。
在這裏插入圖片描述

  1. 比如A->B的scope是compile,而B->C的scope是test,那麼按照上面表格中,對應第2行第3列的值-,那麼A對於C是沒有依賴的,(即C 不會傳遞給A),A對C的依賴沒有從B->C傳遞過來,所以A中是無法使用C的
  2. 比如A->B的scope是compile,而B->C的scope是runtime,那麼按照上面表格中,對應第2行第5列的值爲runtime,那麼A對於C是的依賴範圍是runtime,表示A只有在運行的時候C纔會被添加到A的classpath中,即對A進行運行打包的時候,C會被打包到A的包中
  3. 大家仔細看一下,上面的表格是有規律的,當B->C依賴是compile的時候(表中第2列),那麼A->C的依賴範圍和A->B的sope是一樣的;當B->C的依賴是test時(表中第3列),那麼B->C的依賴無法傳遞給A;當B->C的依賴是provided(表第4列),只傳遞A->C的scope爲provided的情況,其他情況B->C的依賴無法傳遞給A;當B->C的依賴是runtime(表第5列),那麼C按照B->C的scope傳遞給A

maven依賴調解功能

  1. 現實中可能存在這樣的情況,
    A->B->C->Y(1.0),
    A->D->Y(2.0),
    此時Y出現了2個版本,1.0和2.0,此時maven會選擇Y的哪個版本?
  • 路徑最近原則
    上面
    A->B->C->Y(1.0),
    A->D->Y(2.0),
    Y的2.0版本距離A更近一些,所以maven會選擇2.0。
  1. 但是如果出現了路徑是一樣的,如:
    A->B->Y(1.0),
    A->D->Y(2.0),
    此時maven又如何選擇呢?
  • 最先聲明原則
    如果出現了路徑一樣的,此時會看A的pom.xml中所依賴的B、D在dependencies中的位置,誰的聲明在最前面,就以誰的爲主
    比如A->B在前面,那麼最後Y會選擇1.0版本。

可選依賴(optional元素)

A->B中scope:compile
B->C中scope:compile
按照上面介紹的依賴傳遞性,C會傳遞給A,被A依賴。

假如B不想讓C被A自動依賴,可以怎麼做呢?
dependency元素下面有個optional,是一個boolean值,表示是一個可選依賴,B->C時將這個值置爲true,那麼C不會被A自動引入。

排除依賴

A項目的pom.xml中

<dependency>
    <groupId>com.java</groupId>
    <artifactId>B</artifactId>
    <version>1.0</version>
</dependency>

B項目1.0版本的pom.xml中

<dependency>
    <groupId>com.java</groupId>
    <artifactId>C</artifactId>
    <version>1.0</version>
</dependency>

上面A->B的1.0版本,B->C的1.0版本,而scope都是默認的compile,根據前面講的依賴傳遞性,C會傳遞給A,會被A自動依賴,但是C此時有個更新的版本2.0,A想使用2.0的版本,此時A的pom.xml中可以這麼寫:

<dependency>
    <groupId>com.java</groupId>
    <artifactId>B</artifactId>
    <version>1.0</version>
    <exclusions>
        <exclusion>
            <groupId>com.java</groupId>
            <artifactId>C</artifactId>
        </exclusion>
    </exclusions>
</dependency>

上面使用使用exclusions元素排除了B->C依賴的傳遞,也就是B->C不會被傳遞到A中。

exclusions中可以有多個exclusion元素,可以排除一個或者多個依賴的傳遞,聲明exclusion時只需要寫上groupId、artifactId就可以了,version可以省略。

參考

詳解maven解決依賴問題

發佈了19 篇原創文章 · 獲贊 2 · 訪問量 1412
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章