天天看點

建議:Maven依賴常用技巧。排除依賴歸類依賴優化依賴

排除依賴

傳遞性依賴會給項目隐式的引入很多依賴,這極大的簡化了項目依賴的管理,但是有些時候這種特性也會帶來問題。例如,目前項目有一個第三方依賴,而這個第三方依賴由于某些原因依賴了另外一個類庫的SNAPSHOT的不穩定性會直接影響到目前的項目。這時就需要排除掉該SNAPSHOT,并且在目前項目中聲明該類庫的某個正式釋出的版本。還有一些情況,你可能也想要替換某個傳遞性依賴,比如Sun JTA API,Hibernate依賴于這個JAR,但是由于版權的因素,該類庫不在中央倉庫中,而Apache Gernoimo項目有一個對應的實作。這時你就可以排除Sun JAT API,再聲明Geronimo的JTA API實作,見下面所示。

<project>
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.test</groupId>
    <artifactId>project-a</artifactId>
    <version>1.0.0</version>
    <dependencies>
        <dependency>
            <groupId>com.test</groupId>
            <artifactId>project-b</artifactId>
            <version>1.0.0</version>
            <exclusions>
                <exclusion>
                    <groupId>com.test</groupId>
                    <artifactId>project-c</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>com.test</groupId>
            <artifactId>project-c</artifactId>
            <version>1.1.0</version>
        </dependency>
    </dependencies>
</project>
           

上述代碼中,項目A依賴于項目B,但是由于一些原因,不想引入傳遞性依賴C,而是自己顯式的聲明對于項目C 1.1.0版本的依賴。代碼中使用exclusions元素聲明排除依賴,exclusions可以包含一個或者多個exclusion子元素,是以可以排除一個或者多個傳遞性依賴。需要注意的是,聲明exclusion的時候隻需要groupId和artifactId,而不需要version元素,這時因為隻需要groupId和artifactId就能唯一定位依賴圖中的某個依賴。換句話說,Maven解析後的依賴中,不可能出現groupId和artifactId相同,但是version不同的兩個依賴。

歸類依賴

在實際項目中,有很多關于Spring Framework的依賴,他們分别是org.springframework:spring-core:2.5.6、org.springframework:spring-beans:2.5.6、org.springframework:spring-context:2.5.6和org.springframework:spring-context-support:2.5.6,他們是來自同一項目的不同子產品。是以,所有這些依賴的版本都是相同的,而且可以預見,如果将來需要更新Spring Framwork,這些依賴的版本會一起更新。這一情況在Java中似曾相識,考慮如下簡單代碼。

public double c(double r)
{
    return 2*3.14*r;
}
public double s(double r)
{
    return 3.14*r*r;
}
           

這兩個簡單的方程式計算圓的周長和面積,稍微有經驗的程式員一眼就會看出一個問題,使用字面量(3.14)顯然不合适,應該使用定義一個常量并在方法中使用,見下面代碼。

public final double PI = 3.14;

public double c(double r)
{
    return 2*PI*r;
}
public double s(double r)
{
    return PI*r*r;
}
           

使用常量不僅讓代碼變得更加簡潔,更重要的是可以避免重複,在需要更改PI的值的時候,隻需要修改一處,降低了錯誤發生的機率。

同理,對于Maven中的Spring Framework來說,也應該在一個唯一的地方定義版本,并且在dependency聲明中引用這一版本。這樣,在更新Spring Framework的時候就隻需要修改一處,實作方式見下面所示。

<project>
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.test</groupId>
    <artifactId>project-a</artifactId>
    <version>1.0.0-SNAPSHOT</version>

    <name>project-a</name>

    <properties>    
    <springframework.version>2.5.6</springframework.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</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>
    </dependencies>
</project>
           

這裡簡單用到了Maven屬性,首先使用properties元素定義Maven屬性,該例中定義了一個springframework.version子元素,其值為2.5.6。有了這個屬性定義之後,Maven運作的時候會将POM中的所有的${springframework.version}替換成實際值2.5.6。也就是說,可以使用美元符号和大括弧環繞的方式引用Maven屬性。然後,将所有Spring Framework依賴的版厄本那值用這一屬性引用表示。這和在Java中用常量PI替換3.14是同樣的道理,不同的隻是文法。

優化依賴

在軟體開發過程中,程式員會通過重構等方式不斷地優化自己的代碼,使其變得更簡潔、更靈活。同理,程式員也應該能夠對Maven項目的依賴了然于胸,并對其進行優化,如取出多餘的依賴,顯示的聲明某些必要的依賴。

Maven會自動解析所有項目的直接依賴和傳遞性依賴,并且根據規則正确判斷每個依賴的範圍,對于一些依賴沖突,也能進行調節,以確定任何一個構件隻有唯一的版本在依賴中存在。在這些工作之後,最後得到的哪些依賴被稱為已解析依賴(Resolved Dependency)。可以在運作如下的指令檢視目前項目的已解析依賴:

mvn dependency:list

在此基礎上,還能進一步了解已解析依賴的資訊。将直接在目前項目POM聲明的依賴定義為頂層依賴,而這些頂層依賴的依賴則定義為第二層依賴,以此類推,有第三、第四層依賴。當這些依賴經Maven解析後,就會構成一個依賴樹,通過這棵依賴樹就能很清楚的看到某個依賴是通過哪條傳遞路徑引入的。可以運作如下指令檢視目前項目的依賴樹:

mvn dependency:tree

使用dependency:list和dependency:tree可以幫助我們詳細了解項目中所有依賴的具體資訊,在此基礎上,還有dependency:analyze工具可以幫助分析目前項目的依賴。

mvn dependency:analyze

在執行上面的指令後展示的結果中重要的是以下兩個部分。首先是Used undeclared dependencies,意指項目中使用到的,但是沒有顯式聲明的依賴,這裡是spring-context。這種依賴意味着潛在的風險,目前項目直接在使用他們,例如有很多相關的Java import聲明,而這種依賴是通過直接依賴傳遞進來的,當更新直接依賴的時候,相關傳遞性依賴的版本也可能發生變化,這種變化不易察覺,但是有可能導緻目前項目出錯。例如由于接口的改變,目前項目中的相關代碼無法編譯。這種隐藏的,前在的威脅一旦出現,就往往需要耗費大量的時間來查明真相。是以,顯示聲明任何項目中直接用到的依賴。

結果中還有一個重要的部分是Unused declared dependencies,意指項目中未使用的,但顯示聲明的依賴,需要注意的是,對于這樣一類依賴,我們不應該簡單地直接删除其聲明,而是應該仔細分析。由于dependency:analyze隻會分析編譯主代碼和測試代碼需要用到的依賴,一些執行測試和運作時需要的依賴他就發現不了。當然,有時候确實能通過該資訊找到一些沒用的依賴,但一定要小心測試。