Maven實戰07_依賴

1:依賴聲明

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

解釋說明:

  • 根元素prject下的dependencies可以包含一個或所個dependency元素,以聲明一個或多個項目依賴
  • groupId、artifactId、version:依賴的基本座標,每一個依賴必須具備的屬性,Maven只有根據這些座標才能找到並下載依賴
  • type:依賴的類型,對應於項目座標定義的packaging。大部分情況下,不必聲明,默認爲jar
  • scope:依賴的範圍,見下面2:依賴範圍
  • optional:標記依賴是否可選,見下面4:依賴可選
  • exclusion:用來排除傳遞性依賴,見下面3:傳遞性依賴

2:依賴範圍(用於確認是否可導入依賴包)

Maven在編譯項目主代碼的時候需要使用一套classpath。例如,郵件模塊中的javax.mai依賴會出現

Maven在編譯和執行測試的時候會使用另一套classpath。例如,Juni依賴t只會出現在測試classpath下,javax.mail也會出現

Maven在運行實際項目的時候,又會使用一套classspath。例如,javax.mail需要出現,junit則不需要出現。

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

  • compile:編譯依賴範圍。如果沒有指定,默認的依賴範圍。此依賴範圍的Maven依賴,對於編譯、測試、運行三種classpath都有效。
  • test:測試依賴範圍,此依賴範圍的Maven依賴,只對於測試的classpath有效,在項目編譯主代碼或者運行項目的使用時將無法使用此類依賴。
  • provided:已提供依賴範圍,此依賴範圍的Maven依賴,對於編譯和測試classpath有效,但在運行時無效,例如SpringBoot生成war包,其中的tomcat依賴就要是provided依賴範圍,在開發中,就要改成compile依賴
  • runtime:運行時依賴範圍,此依賴範圍的Maven依賴,對於測試和運行有效,但在編譯主代碼時無效,典型的例子就是JDBC驅動實現,項目主代碼的編譯只需要JDK提供的JDBC接口,只有在執行測試或者運行項目的時候才需要實現上述接口。
  • system:系統依賴範圍,該依賴與三種classpath的關係,和provided依賴範圍完全一致。但是使用system範圍的依賴必須通過systemPath元素顯式地指定依賴文件的路徑。由於此類依賴不是通過Maven倉庫解析的,而且往往與本機系統綁定,可能造成構建的不可移植,因此要謹慎使用。systemPath可以引用環境變量,如:
<dependency>
    <groupId>javax.sql</groupId>
    <artifactId>jdbc-stdext</artifactId>                
    <version>2.0</version>
    <scope>system</scope>
    <systemPath>${java.home}/lib/rt.jar</systemPath>
</dependency>
  • import(Maven2.0.9及以上):導入依賴範圍,該依賴範圍不會對三種classpath產生實際的影響。

上述出import以外的各種依賴範圍與三種classpath的關係表:

3:傳遞性依賴

一個基於SpringFramework的項目,如果不使用Maven,就要手動的去添加依賴包,通常是去官網下載,但是裏面的依賴很多,會有不必要的依賴存在。另一種做法就是下載必要依賴包,但是這個包中不包含其他相關依賴,到實際使用的時候,根據報錯引入相關依賴,這種做法是很麻煩的。

Maven的傳遞性依賴機制可以解決這個問題,例如:有一個spring-core的依賴,而在spring-core中也存在自己的依賴,包含了一個commons-logging依賴,見代碼清單:

<dependency>       
    <groupId>commons-logging</groupId>
    <artifactId>commons-logging</artifactId>
    <version>1.1.1</version>
    <scope>compile</scope>            
</dependency>

該依賴的依賴範圍是compile。而spring-core的依賴範圍也是cmpile,因此就會出現傳遞性依賴,使得項目也同時依賴commons-logging,如圖所示

4:傳遞性依賴和依賴範圍

針對上圖的傳遞性依賴做一些解釋

Maven project 依賴於 spring-core : 第一直接依賴

spring-core 依賴於 commons-logging :第二直接依賴

Maven project 對於 commons-logging:傳遞依賴

5:依賴調解

並不是所有的傳遞性依賴都能正常工作,當傳遞性依賴造成問題的時候,我們就需要清楚的知道該傳遞性依賴是從哪條依賴路徑引入的。

例如:

  • 第一原則:路徑最近者優先:

      在項目A中存在這樣的依賴。A –> B –> C –>X(1.0)、A –> D –> X(2.0),X是A的傳遞性依賴,但是這兩條依賴路徑上有兩個版本的X,那麼哪個X會被Maven解析使用呢?在Maven中採用路徑最近者優先策略,因此,X(2.0)會被解析使用

  • 第二原則:第一聲明者優先

      在針對第一原則下路勁相同,版本不一致的缺陷下提出了第二原則,在項目A中存在這樣的依賴關係。A –> B –> Y(1.0)、A –> C – > Y(1.0),Y的依賴路徑都是2,從Maven2.0.9開始,爲了避免構建對的不確定性,Maven使用了該原則,在依賴路徑相等的情況下,在POM中依賴聲明的順序決定了誰會被解析使用,順序最靠前的那個依賴優勝,如果B的依賴聲明在C之前,那麼Y(1.0)就會被解析使用。

6:可選依賴

    假設有這樣一種依賴關係,項目A依賴於項目B,項目B依賴於項目X和項目Y,B對於X和Y的依賴都是可選依賴(optional爲true):A -> B,B -> X(可選),B -> Y(可選)。根據傳遞性依賴的定義,如果所有這三個依賴的範圍都是compile,那麼X,Y就是A的compile範圍傳遞性依賴。然而,由於這裏X,Y是可選依賴,依賴就不會傳遞,也就是說,X,Y將不會對A有任何影響 。

    爲什麼要使用可選依賴這一特性呢?可能B項目實現了兩個特性,其中的一個特性一依賴於X,特性二依賴於Y,而且這兩個特性是相互排斥的,用戶不可能同時使用兩個特性,比如B是一個持久層隔離工具包,它支持多種數據庫,包括Mysql,PostgreSQL等,在構建這個工具包時,需要這兩種數據庫的驅動程序,但在使用這個工具包的時候,只會依賴一種數據庫。

    在理想情況下,是不應該使用可選依賴的,因爲使用了可選依賴的原因是某一個項目實現了多個特性,在面向對象設計中,有個單一職責性原則,意指一個類應該只有一個職責,而不是糅合太多的功能。這個原則在規劃Maven的時候同樣適用。

7:最佳實踐

  • 排除依賴

    之所以要排除依賴的原因有以下幾點:

  1. 依賴不穩定,需要正式版本,多爲引用第三方依賴中包含這個不穩定依賴導致
  2. 由於版權,依賴不再中央倉庫,需要排除改依賴

上述圖排除依賴的代碼清單

<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>xx.xxx.xx</groupId> 

<artifactId>A</artifactId> 

<version>1.0-SNAPSHOT</version> 

<dependency> 

<groupId>xx.xxx.xx</groupId> 

<artifactId>B</artifactId> 

<version>1.0</version> 

<!-- 排除C的不穩定依賴 --> 

<exclusions> 

<exclusion> 

<groupId>xx.xx.xx</groupId> 

<artifactId>C</artifactId> 

</exclusion> 

</exclusions> 

</dependency> 

<!-- 引入C的穩定依賴 --> 

<dependency> 

<groupId>xx.xx.xx</groupId> 

<artifactId>C</artifactId> 

<version>1.0</version> 

</dependency> 

</project>

代碼清單說明

     上述代碼中,項目A依賴於項目B,但是由於一些原因,不想引入項目B中的依賴C,而是自己顯示的聲明對於項目C穩定版本1.0的依賴。所以在項目B中使用exclusions對依賴進行排除,由英語語法的單複數可知,在exclusions中可包含多個exclusion,即可排除多個依賴。需要注意的是,聲明exclusion的時候只需要groupId和artifactId,而不需要version元素,這是因爲只需要groupId和artifactId就能唯一定位依賴圖中的某個依賴。也就是說,在Maven依賴中,不可能出現groupId和artifactId相同但是version不同的兩個依賴。

  • 歸類依賴

    之所以使用歸類依賴,是爲了保持版本的一致性,排除出錯,例如我們通常在整合Spring Framework的時候,有很多都是相同版本的,對此,Maven也提供了歸類依賴的方式簡單管理版本,這樣做的好處就是,將來在升級版本的時候,只需要改這個歸類,不需要改所有的依賴版本了。這個就和Java中的常量是一個意思,在Maven中通過EL表達式替換。

    歸類依賴的語法:

    <properties>
    <springframeword.version>4.1.5</springframeword.version>
    </properties>

    <!— 使用 -->

    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>${springframeword.version}</version>
    </dependency>

     

  • 優化依賴

    優化依賴是指:對項目的依賴進行優化,去除多餘的依賴,顯式的聲明某些必要的依賴

    使用mvn dependency:list查看當前項目的已解析依賴,依賴範圍

    使用mvn dependency:tree查看當前的依賴樹,可以清晰的看到依賴的引入路徑

    使用mvn dependency:anapyze分析依賴,只會分析編譯主代碼和測試代碼需要用到的依賴,一些執行測試和運行時需要的依賴就發現不了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章