1. 程式人生 > >建議:Maven依賴常用技巧。

建議: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只會分析編譯主程式碼和測試程式碼需要用到的依賴,一些執行測試和執行時需要的依賴他就發現不了。當然,有時候確實能通過該資訊找到一些沒用的依賴,但一定要小心測試。