1. 程式人生 > >Maven實戰-由一次jar包衝突來詳述 Maven 依賴範圍及依賴調節原則

Maven實戰-由一次jar包衝突來詳述 Maven 依賴範圍及依賴調節原則

這篇文章的緣由是剛來搭建的專案有jar包衝突,報下面這個錯基本就是servlet-api包衝突無疑了

Caused by: java.lang.ClassCastException: org.springframework.web.SpringServletContainerInitializer cannot be cast to javax.servlet.ServletContainerInitializer

那怎麼解決呢,一開始我想著找到 依賴關係,但是有可能A依賴B,B又依賴C,然後刪掉多餘的JAR包依賴。但是這些關聯的包都是固定死的,pom檔案都是從遠端倉庫拉的,不能改變,只能另找辦法。

然後就是今天的主題,Maven 依賴範圍及依賴調節原則

我們可以在根專案加一個Servlet-api的包,根據 路徑最近者優先 ,maven只會載入我的Servlet-api包,其餘的更遠的就不載入了。其次,我在scope填provided,這樣在專案真正執行時,maven不會自己載入Servlet-api包,而是相信容器會提供這個包,這樣就不會跟容器(比如tomcat)造成jar包衝突。

下面是我看的一篇文章來的靈感,所以這本質還是一篇轉載文章啦~

——————————————-轉載分割線—————————————————————————————–

當在我們 POM 檔案中配置 Maven 依賴的時候,常見的格式如下:

<project>
    ...
    <dependencies>
        <dependency>
            <groupId>...</groupId>
            <artifactId>...</artifactId>
            <version>...</version>
            <type>...</type>
            <scope>...</scope>
            <optional>...
</optional> <exclusions> <exclusion> ... </exclusion> ... </exclusions> </dependency> ... </dependencies> ... </project>

其中,groupId、artifactId 和 version 是 Maven 依賴的基本座標,也是最重要的。其他的元素如 type 表示依賴的型別,預設為 jar ;scope 為依賴的範圍,預設為 compile ;optional 用來標記依賴是否可選;exclusions 用來排除傳遞性依賴。

現在我們所要講的依賴範圍,正是其中 scope 元素所能選擇的內容。在正式介紹依賴範圍之前,我們需要知道一件事,那就是: Maven 專案的環境變數(classpath)有三種,分別為編譯時用的環境變數、測試時用的環境變數和執行時用的環境變數 .而依賴範圍就是用來控制依賴與這三種環境的關係的:

  • compile :編譯依賴範圍,此為預設值,對編譯、測試和執行三種環境變數都有效。
  • test :測試依賴範圍,僅對測試環境變數有效。
  • provided :已提供的依賴範圍,對編譯和測試環境變數都有效。
  • runtime :執行依賴範圍,對測試和執行環境變數都有效。
  • system :系統依賴範圍,對編譯和測試環境變數都有效,但由於此依賴不是通過 Maven 倉庫解析的,而且往往與本機系統繫結,可能造成構建的不可移植,因此應該謹慎使用。
  • import :匯入依賴範圍,並不會對上面的三種環境變數產生實際的影響。
依賴範圍(scope) 編譯環境變數有效 測試環境變數有效 執行環境變數有效
compile Y Y Y
test N Y N
provided Y Y N
runtime N Y Y
system Y Y N

如上圖所示,此為 Maven 的直接依賴範圍。除此之外,還有傳遞性依賴,那麼何為傳遞性依賴呢?

舉一個簡單的例子,在預設依賴範圍的情況下,如果專案 A 依賴 B,B 依賴 C,那麼 A 就依賴 C,這時 A 與 C 就是傳遞性依賴的關係。而且我們稱 A 對 B 的依賴為第一直接依賴,B 對 C 的依賴為第二直接依賴,以此類推。

至於為什麼要強調 在預設依賴範圍的情況下 ,則是傳遞性依賴也是有條件的,具體如下圖所示:

第一直接依賴\第二直接依賴 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 ,其他三種皆與第一直接依賴範圍相同。

此外,有一些特殊的依賴情況,可能會造成一些困擾和問題,例如:

  • 第一種依賴路徑:A -> B -> C -> D(1.0)
  • 第二種依賴路徑:A -> B -> D(1.1)

    其中 -> 表示依賴。如上面所示,專案 A 通過不同的路徑都依賴到了專案 D,但 D 有兩個不同的版本,這時怎麼辦?

對於這種情況,Maven 給出了一種解決方案,即: 路徑最近者優先 。以上面的案例為例,第一種依賴路徑的距離為 3 ,第二種依賴路徑的距離為 2 ,因此這時就以第二種依賴路徑為準,即依賴 D(1.1) 而不是 D(1.0) 。

不過,大家有沒有想過:如果兩種依賴路徑的距離相同該怎麼辦呢?例如:

  • 第一種依賴路徑:A -> B -> D(1.0)
  • 第二種依賴路徑:A -> B -> D(1.1)

對於這種情況,在 Maven 2.0.9 之前是不能確定到底誰被依賴的,也就造成了依賴不確定性。但是在 Maven 2.0.9 之後,Maven 給出瞭解決方案,即: 第一宣告優先 。簡而言之,誰先宣告就依賴誰。

最後,給出一些比較有用的 Maven 命令,通過這些 Maven 命令,我們能夠快速瞭解 Maven 的依賴情況:

  • mvn dependency:list :檢視 Maven 依賴列表,包直接依賴與傳遞性依賴。
  • mvn dependency:tree 檢視 Maven 依賴樹,可以清晰的看出依賴情況。
  • mvn analyze :分析 Maven 依賴的使用情況。

對於 Maven 這個優秀的構建工具、依賴管理工具、專案管理工具,值得我們花些時間去了解和掌握它!祝好。

溫馨提示:此文的構思緣起于徐曉斌所著的《Maven實戰》。