maven項目對象模型(二)

1.4.4.傳遞性依賴

一個傳遞性依賴就是一個依賴的依賴。如果project-a依賴於project-b,而後者接着依賴於project-c,那麼project-c就是被認爲是project-a的傳遞性依賴。如果project-c依賴於project-d,那麼project-d也被認爲是project-a的傳遞性依賴。Maven的優勢之一就是它能夠管理傳遞性依賴,並且能夠幫助開發者屏蔽掉跟蹤所有編譯期和運行期依賴細節。有了傳遞性依賴機制。在使用Spring Framework 的時候就不用去考慮它依賴了那些包,也不用擔心一如多餘的依賴。Maven會解析各個直接依賴的POM,將那些必須的間接依賴,以傳遞性依賴的形式引入到當前的項目中。

Maven建立一個依賴圖,並且處理一些可能發生的衝突和重疊。

§傳遞性依賴的範圍


依賴範圍不僅可以控制依賴與三種classpath的關係,還對傳遞性依賴產生影響如下表


compile

test

provided

runtime

Compile

compile

——

      ——

Runtime

Test

test

——

——

test

Provided

provided

——

provided

Provided

Runtime

runtime

——

——

runtime

如果project-a包含一個project-b的測試範圍依賴,後者包含一個對於project-c的編譯範圍依賴。project-c將會是project-a的測試範圍傳統依賴。

從表中發現這樣的規律:當第二個直接依賴範圍是compile的時候,傳遞性依賴的範圍與第一直接依賴的範圍一致;當第二個直接依賴範圍是test的時候,傳遞性依賴不會得以傳遞;當第二個直接依賴範圍是provided的時候,只傳遞第一直接依賴範圍爲provided的依賴。且傳遞性依賴的範圍同樣爲provided;當第二直接依賴範圍是runtime的時候,依賴性傳遞的範圍與第一直接依賴的範圍一致,但compile列外,此時傳遞性依賴的範圍爲runtime。

1.4.5.依賴版本界限

不是必須爲依賴聲明摸個特定的版本,可以指定一個滿足需求的依賴版本界限。例如:項目依賴JUnit3.8或以上的版本,或者說依賴於JUnit1.2.10和1.2.14之間的某個版本。可以使用如下的字符來圍繞一個或者多個版本號,開實現版本界限(,)不包含量詞,[, ]包含量詞。例如想訪問JUnit任意大於等於3.8但小於4.0版本的

<dependency>

<groupId>junit</groupId>

<artifactId>junit</artifactId>

<version>[3.8,4.0)</version>

<scope>test</scope>

</dependency>

如果想要依賴任意不大於3.8.1的版本,可以只指定一個上包含邊界。

<dependency>

<groupId>junit</groupId>

<artifactId>junit</artifactId>

<version>[,3.8.1]</version>ex-de

<scope>test</scope>

</dependency>

在逗號前面或者後面的版本不是必須的,這種空缺意味着正無窮或者負無窮。

注意:當生命一個“正常的”版本如JUnit3.8.2,最好表述成“允許任何版本,最好是3.8.2”。當檢測到版本衝突的時候,Maven會使用衝突算法來選擇最好的版本。如果指定[3.8.2],它意味只有3.8.2會被使用,沒有其它地方依賴地使用了一個版本[3.8.1],就會得到一個構建失敗的報告,說明有版本衝突。要保守黨的使用它,只有在確實需要的時候才使用。正好的做法是通過dependencyManagement來解決衝突。

1.4.6.依賴調解

Maven引入的傳遞性依賴機制,一方面大大簡化和方便了依賴聲明,另一方面,大部分情況下只需要關心項目的直接依賴,而不用考慮這些直接依賴會引入那些傳遞性依賴。但是有時候,當傳遞性依賴造成問題的時候,需要清楚地知道該傳遞性依賴是從哪條依賴路徑引入的。例如:項目A有這樣的依賴關係:A -> B -> C -> X(1.0), A-> D -> X(2.0),X是A的傳遞性依賴,但是兩條依賴路徑上有兩個版本的X,那麼哪個X會被Maven解析使用呢?兩個版本都被解析顯然是不對的,因爲會造成依賴重複,因此必須選擇一個。Maven依賴調解的第一個原則是:路徑最近者優先。該例中X(1.0)的路徑長度爲3,而X(2.0)路徑長度爲2,因此X(2.0)會被解析使用。

依賴調解第一原則不能解決所有問題,例如依賴關係A –> B -> Y(1.0),A -> B ->Y(2.0),Y(1.0)和Y(2.0)的依賴路徑長度都是2。在Maven2.0.8及之前的版本中,這是不確定的,但是從Maven2.0.9開始,爲了儘可能避免構建的不確定,Maven定義了依賴調解的第二原則:第一原則優先,在依賴路徑長度相等的前提下,在POM中依賴聲明的順序決定了誰會解析使用,順序最靠前的那個依賴被使用。如果B的依賴聲明在C之前,那麼Y(1.0)就會被解析使用。

1.4.7.可選依賴

假設有這樣一個依賴關係,項目A依賴於項目B,項目B依賴於項目X和Y,B對於X和Y的依賴都是可選依賴:A -> B, B ->X(可選),B -> y(可選)。根據傳遞性依賴的定義,如果所有這三個依賴的範圍都是compile,那麼X,Y就是A的compile範圍傳遞性依賴。然而,由於這裏X,Y是可選依賴,依賴將不會的依傳遞。X,Y將不會對A有任何影響。項目B實現了兩個特徵,其中特徵一依賴於X,特徵二依賴於Y,而且這兩個特徵是互斥的,用戶不可能同時使用兩個。在編譯B項目時需要兩個依賴,如下:

<project>

<modelVersion>4.0.0</modelVersion>

<groupId>org.sonatype.mavenbook</groupId>

<artifactId>my-project-b</artifactId>

<version>1.0.0</version>

<dependencies>

<dependency>

<groupId>net.sf.ehcache</groupId>

<artifactId>ehcache</artifactId>

<version>1.4.1</version>

<optional>true</optional>

</dependency>

<dependency>

<groupId>swarmcache</groupId>

<artifactId>swarmcache</artifactId>

<version>1.0RC2</version>

<optional>true</optional>

</dependency>

<dependency>

<groupId>log4j</groupId>

<artifactId>log4j</artifactId>

<version>1.2.13</version>

</dependency>

</dependencies>

</project>

上述XML代碼片段中,使用<optional>元素表示net.sf.ehcache和swarmcache這兩個依賴爲可選依賴,它們只會對當前項目B產生影響,當其它項目依賴於B的時候,這兩個依賴不會被傳遞。因此,當A依賴於項目B的時候,那麼在項目A中就要顯示的聲明使用哪種依賴項。

<project>

<modelVersion>4.0.0</modelVersion>

<groupId>org.sonatype.mavenbook</groupId>

<artifactId>my-application</artifactId>

<version>1.0.0</version>

<dependencies>

<dependency>

<groupId>org.sonatype.mavenbook</groupId>

<artifactId>my-project</artifactId>

<version>1.0.0</version>

</dependency>

<dependency>

<groupId>net.sf.ehcache</groupId>

<artifactId>swarmcache</artifactId>

<version>1.4.1</version>

</dependency>

</dependencies>

</project>

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

1.4.8.排除依賴

有很多時候需要排除一個傳遞性依賴,就要適用exclusions元素聲明排除依賴。

<dependency>

<groupId>org.sonatype.mavenbook</groupId>

<artifactId>project-a</artifactId>

<version>1.0</version>

<exclusions>

<exclusion>

<groupId>org.sonatype.mavenbook</groupId>

<artifactId>project-b</artifactId>

</exclusion>

</exclusions>

</dependency>

exclusions可以包含一個或者多個exclusion子元素。

排除並且替換一個傳遞性依賴。

<dependencies>

<dependency>

<groupId>org.hibernate</groupId>

<artifactId>hibernate</artifactId>

<version>3.2.5.ga</version>

<exclusions>

<exclusion>

<groupId>javax.transaction</groupId>

<artifactId>jta</artifactId>

</exclusion>

</exclusions>

</dependency>

<dependency>

<groupId>org.apache.geronimo.specs</groupId>

<artifactId>geronimo-jta_1.1_spec</artifactId>

<version>1.1</version>

</dependency>

</dependencies>

沒有標記說依賴geronimo-jta_1.1_spec是一個替換,它只是正好提供了原來JTA依賴一樣的API。有以下幾種情況可能想要排除或者替換傳遞性依賴的情況:

§構建的groupId和artifactId已經改變,而當前項目需要一個傳遞性依賴不同名稱的版本—結果是classpath中會出現同樣項目的兩份內容。一般來說Maven會捕獲到這種衝突並且使用該項目的一個單獨的版本,但是當artifactId和gruopId不一樣的時候,Maven就會認爲他們是兩種不同的類庫。

§某個構件沒有在項目中被使用,而且該傳遞性依賴沒有被標示爲可選依賴的這種情況下,可能要排除這樣依賴,因爲它不是系統需要的東西,儘量減少應用程序發佈時的類庫數目。

§一個構件已經在運行時的容器中提供了,因此不應該被包含在構件中。

§爲了排除一個可能是多個實現的API依賴。

1.4.9.歸類依賴

有很多關於Spring Framework的依賴,它們分別是spring-aop,spring-beans,spring-context,spring-context-support,spring-core等,它們是來自同一項目的不同模塊,因此,是由於這些依賴的版本都是相同的。如果將要升級Spring Framework,這些依賴的版本會一起升級。所以應該在一個唯一的地方定義一個版本,並且在dependency聲明中引用這一版本,這樣在升級Spring Framework的時候就只需要修改一處。

<project>

<modelVersion>4.0.0</modelVersion>

<groupId>org.sonatype.mavenbook</groupId>

<artifactId>my-project</artifactId>

<version>1.0.0-SNAPSHOT</version>

<properties>

<springframework.version>3.0.0.RELEASE</springframework.version>

</properties>

<dependencies>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-aop</artifactId>

<version>${springframework.version}</version>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-beans</artifactId>

<version>${springframework.version}</version>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-context</artifactId>

<version>${springframework.version}</version>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-context-support</artifactId>

<version>${springframework.version}</version>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-core</artifactId>

<version>${springframework.version}</version>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-jdbc</artifactId>

<version>${springframework.version}</version>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-orm</artifactId>

<version>${springframework.version}</version>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-test</artifactId>

<version>${springframework.version}</version>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-tx</artifactId>

<version>${springframework.version}</version>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-jms</artifactId>

<version>${springframework.version}</version>

</dependency>

</dependencies>

</project>

1.4.10.依賴管理

Maven提供dependencyManagement元素既能讓子模塊繼承到父模塊的依賴配置,又能保證自模塊依賴的使用靈活性。

實際的項目中,你會有許多的Maven模塊,而且你往往發現這些模塊有很多依賴完全相同的構建,A模塊有個對spring的依賴,B模塊也有,它們的依賴配置一模一樣,同樣的groupId, artifactId, version,或者還有exclusions,classifer。這是一種重複,重複就意味着潛在的問題,Maven提供的dependencyManagement就是用來消除這種重複的。

正確的做法是:

1. 在父模塊中使用dependencyManagement配置依賴

2. 在子模塊中使用dependencies添加依賴

dependencyManagement實際上不會真正引入任何依賴,dependencies纔會。但是,當父模塊中配置了某個依賴之後,子模塊只需使用簡單groupId和artifactId就能自動繼承相應的父模塊依賴配置。

父模塊中如此聲明:

<project>

<modelVersion>4.0.0</modelVersion>

<groupId>org.sonatype.mavenbook</groupId>

<artifactId>a-parent</artifactId>

<version>1.0.0</version>

...

<dependencyManagement>

<dependencies>

<dependency>

<groupId>mysql</groupId>

<artifactId>mysql-connector-java</artifactId>

<version>5.1.2</version>

</dependency>

    ...

<dependencies>

</dependencyManagement>

</project>

子模塊中如此聲明:

<project>

<modelVersion>4.0.0</modelVersion>

<parent>

<groupId>org.sonatype.mavenbook</groupId>

<artifactId>a-parent</artifactId>

<version>1.0.0</version>

</parent>

<artifactId>project-a</artifactId>

...

<dependencies>

<dependency>

<groupId>mysql</groupId>

<artifactId>mysql-connector-java</artifactId>

</dependency>

</dependencies>

</project>

依賴配置越複雜,依賴管理所起到的作用就越大,它不僅能夠幫助簡化配置,它還能夠鞏固依賴配置,也就是說,在整個項目中,對於某個構件(如mysql)的依賴配置只有一種,這樣就能避免引入不同版本的依賴,避免依賴衝突。

1.4.11.優化依賴

§mvn dependency:list 此命令查看當前項目的已解析的依賴。

§mvndependency:tree 此命令查看當前項目的依賴樹。

§mvndependency:analyze 此命令可以幫助分析當前項目的依賴。使用但未聲明的依賴與聲明但未使用的依賴。

使用以上命令可以幫助我們瞭解項目所有依賴的具體情況。




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