Maven依賴總結


一:依賴范圍

Maven在編譯項目主代碼的時候需要使用一套classpath。其次,在編譯和執行測試的時候會使用另外一套classpath。最后,實際運行Maven項目的時候,又會使用一套classpath。

所謂的依賴范圍就是用來控制依賴與這三種classpath(編譯、測試、運行)的關系,Maven有以下幾種依賴范圍:

  • compile:編譯依賴范圍。如果沒有指定,默認使用該依賴范圍。使用此依賴范圍時,對於編譯、測試、運行都有效。例如:spring-core,編譯、測試、運行時都需要使用該依賴。
  • test:測試依賴范圍。只對測試classpath有效。例如:JUnit,它只在編譯測試代碼以及運行測試的時候才需要,編譯和運行classpath時無法使用此依賴。
  • provided:已提供依賴范圍。對於編譯和測試時有效,但在運行時無效。例如:servlet-api,編譯和測試項目的時候需要該依賴,但運行時,由於容器已經提供,就不需要Maven重復的引入。
  • runtime:運行時依賴。編譯時無效,對於測試和運行有效。例如:JDBC驅動實現,編譯時只需要JDK提供的JDBC接口,只有在執行測試和運行時才需要實現上述接口的具體JDBC驅動。
  • system:系統依賴范圍。同provided。使用該依賴時必須通過systemPath元素顯式地指定依賴文件路徑。主要用於依賴本地的、且Maven倉庫之外的類庫文件。例如:
<dependency>
  <groupId>javax.sql</groupId>
  <artifactId>jdbc-stdext</artifactId>
  <version>2.5</version>
  <scope>system</scope>
  <systemPath>${spath}/lib/test.jar<systemPath>
</dependency>

二:傳遞依賴和依賴范圍

當我們依賴一個a.jar時,如果a.jar依賴b.jar,那么只需要早pom中聲明對a.jar的依賴即可,b.jar會被Maven自動加載進來。

例如:有一個org.springframework:spring-core:2.5.6的依賴,而實際上spring-core也有它自己的依賴,它依賴commons-logging。有了傳遞依賴機制,在使用spring-core時不需要考慮它依賴了什么。Maven會自動解析。

依賴范圍在傳遞依賴時會略有變化

當第二直接依賴的范圍是compile的時候,傳遞性依賴的范圍與第一直接依賴的范圍一致;

當第二直接依賴的范圍是test的時候,依賴不會得以傳遞;

當第二直接依賴的范圍是provided的時候,只傳遞第一直接依賴范圍也為provi的依賴,且范圍為provided;

當第二直接依賴的范圍是runtime的時候,傳遞性的依賴范圍與第一直接依賴的范圍一致,但compile例外,此時傳遞依賴范圍為runtime;

三:依賴調解

  • 原則一:路徑最近者優先。例如:A ->B ->C ->X(1.0) 同時 A ->D ->X(2.0),很顯然X(2.0)路徑更短,會被解析使用。
  • 原則二:第一聲明者優先。在依賴長度相等情況下,解析在pom中依賴聲明中順序考前的。例如:A ->B ->X(1.0) 同時 A ->D ->X(2.0)。如果B在D之前聲明,那么X(1.0)會被解析。

除以上兩種原則外,還可以手動排除,例如:A ->B ->X(1.0)同時A ->X(2.0)。如果項目A希望加載X(2.0)可做如下聲明,通過<exclusion>元素來顯式排除。

<dependency>
  <groupId>com.xxx.xx</groupId>
  <artifactId>xx-B</artifactId>
  <version>2.5</version>
  <exclusions>
    <exclusion>
      <groupId>com.xx</groupId>
      <artifactId>project-X</artifactId>
    </exclusion>
  </exclusions>
</dependency>
<dependency> 
  <groupId>com.xxx.xx</groupId>
  <artifactId>project-x</artifactId>
  <version>2.0</version>
</dependency>

四:可選依賴

例如:b.jar是一個持久層工具包,它同時支持Mysql和PostgreSql,A項目依賴b.jar,那么在構建A時需要這兩種數據庫的驅動程序,但在使用的時候知會依賴一種數據庫。A項目的依賴聲明如下:

<dependency>
  <groupId>com.xxx.xx</groupId>
  <artifactId>xx.db</artifactId>
  <version>2.5</version>
  <dependencies>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.10</version>
      <optional>true</optional>
    </dependency>
    <dependency>
      <groupId>postgresql</groupId>
      <artifactId>postgresql</artifactId>
      <version>8.4-701.jdbc3</version>
      <optional>true</optional>
    </dependency>
  </dependencies>
</dependency>

使用<optional>元素表示這兩個為可選依賴,這是依賴不會傳遞到A項目,當A項目需要使用基於MySQL數據庫時,需要顯式聲明對mysql的依賴。

另外:在理想的情況下是不會出現這種情況的,因為在面向對象設計中,有一個單一職責原則,即一個jar的職責應該只有一個。所以對於b.jar,最好是創建2個Maven項目,分別實現mysql和postgresql。

五:依賴優化

代碼需要不斷重構才能達到最優,依賴管理也是一樣,需要不斷的進行去除多余依賴,以及顯式的聲明某些必要的依賴。

Maven會自動解析所有項目的直接依賴和傳遞依賴,並根據規則判斷每個依賴的范圍,對於一些依賴沖突也能進行調節,這些工作之后得到這個項目的完整的已解析依賴。

可通過運行以下命令查看當前項目的已解析依賴:

mvn dependency:list

已解析依賴查看

上圖展示了當前項目中所有已解析的依賴,同時每個依賴的范圍也得以明確標示。

如果將直接在pom中聲明的依賴定義為第一層依賴,這些頂層依賴的依賴定義為第二層依賴,則以此類推可以形成一個完整的依賴樹。

可運行以下明細查看當前項目的依賴樹

mvn dependency:tree

依賴樹

從上圖中可以清晰看出,雖然沒有聲明slf4j-api,但它通過傳遞依賴被加載進來,其范圍為compile。

可運行以下命令對當前項目依賴進行簡單分析

依賴分析

上圖中Used undeclared dependencies,表示項目中使用到的,但是沒有顯式聲明的依賴。可以看到第一個依賴是SNAPSHOT版本,它是通過傳遞依賴被加載進來的,這種依賴就是項目中的隱藏的、潛在的炸彈,因為引用的是SNAPSHOT非穩定版本,且不在pom中顯式聲明,很容易被忽略。該炸彈一旦爆炸,往往需要耗費大量時間來查明。

還有一個Unused declared dependencies,表示項目中未使用的,但是顯式聲明的依賴。如果真的不需要,建議去除聲明。但需要注意的是,dependency:analyze知會分析編譯和測試時需要用到的依賴,一些運行時依賴就無法被發現。所以在優化依賴時一定要小心測試。


免責聲明!

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



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