天天看點

Maven最佳實踐:管理依賴

From:http://juvenshun.iteye.com/blog/337405

Maven最佳實踐:管理依賴

"If I have seen further it is by standing on the shoulders of Giants" —— Isaac Newton (1642-1727)

有人認為Maven是一個依賴管理工具,當然這種想法是錯誤的(确切的說Maven是一個項目管理工具,貫穿了整個項目生命周期,編譯,測試,打包,釋出...),但Maven給人造成這種錯誤的印象也是有原因的,因為Maven的依賴管理十分強大,用好了Maven,你不再需要面對一大堆jar感到頭大,依賴沖突,無用依賴等問題也能夠得到有效的防止和解決。本節介紹如何用好Maven的依賴管理。

最簡單的依賴

依賴是使用Maven坐标來定位的,而Maven坐标主要由GAV(groupId, artifactId, version)構成。是以,使用任何一個依賴之間,你都需要知道它的Maven坐标,關于如何尋找Maven坐标,《搜尋Maven倉庫》 一文可以幫助你。

最簡單的依賴如:

Xml代碼  

1 <dependency>  
2   <groupId>junit</groupId>  
3   <artifactId>junit</artifactId>  
4   <version>4.4</version>  
5 </dependency>        

上例中我們聲明了一個對junit的依賴,它的groupId是junit, artifactId是junit, version是4.4。這一組GAV構成了一個Maven坐标,基于此,Maven就能在本地或者遠端倉庫中找到對應的junit-4.4.jar檔案。

依賴歸類

随着項目的增大,你的依賴越來越多,比如說你依賴了一堆spring的jar,有org.spring.framework:spring-core, org.spring.framework:beans, org.spring.framework:spring-web, org.spring.framework:spring-mock。它們的groupId是相同的,artifactId不同。為了管理其版本,你對它們進行過統一的更新,逐個的将version改成了最新版。但是,顯然,當POM很大的時候你說不定會犯錯誤,而當版本不一緻的時候,一些詭異的相容性問題就可能出現。

對此,Maven有它的解決方案:

1 <dependencies>  
 2   <dependency>  
 3     <groupId>org.spring.framework</groupId>  
 4     <artifactId>spring-core</artifactId>  
 5     <version>${spring.version}</version>  
 6   </dependency>  
 7   <dependency>  
 8     <groupId>org.spring.framework</groupId>  
 9     <artifactId>spring-beans</artifactId>  
10     <version>${spring.version}</version>  
11   </dependency>  
12   <dependency>  
13     <groupId>org.spring.framework</groupId>  
14     <artifactId>spring-web</artifactId>  
15     <version>${spring.version}</version>  
16   </dependency>  
17   <dependency>  
18     <groupId>org.spring.framework</groupId>  
19     <artifactId>spring-mock</artifactId>  
20     <version>${spring.version}</version>  
21   </dependency>  
22 </dependencies>  
23   
24 <properties>  
25   <spring.version>2.5</spring.version>  
26 </properties>        

這裡我們定義了一個Maven屬性,其名稱為spring.version,值是2.5。在這個POM中,我們就能用${spring.version}的方式來引用該屬性。我們看到,所有spring相關的依賴的version元素現在都成了${spring.version},當Maven運作的時候,它會自動用值2.5來替換這個引用。

當我們需要更新spring的時候,隻要更改一個地友善可,而且,你現在能很高的保證所有的spring依賴包都是同一個版本。

依賴範圍(scope)

本文的第一個例子其實是有漏洞的,對于Junit,一般來說你隻有在運作測試的時候需要它,也就是說,它對于src/main/java的classpath沒什麼意義,并且,将Junit的jar檔案打入最終的釋出包也不是好事,這無謂的增加了釋出包的大小。

其實我們應該這樣做:

Xml代碼  

1 <dependency>  
2   <groupId>junit</groupId>  
3   <artifactId>junit</artifactId>  
4   <version>4.4</version>  
5   <scope>test</test>  
6 </dependency>      

于是,junit對于主源碼classpath不可用,對于測試源碼classpath可用,不會被打包。

再舉個例子,在開發javaee應用的時候我們一定會用到servlet-api,它對于主源碼和測試源碼都是必要的,因為我們的代碼中會引入servlet-api的包。但是,在打包的時候,将其放入WAR包就會有問題,因為web容器會提供servlet-api,如果我們再将其打包就會造成依賴沖突,解決方案如下:

1 <dependency>  
2   <groupId>javax.servlet</groupId>  
3   <artifactId>servlet-api</artifactId>  
4   <version>2.4</version>  
5   <scope>provided</scope>  
6 </dependency>        

将依賴範圍設定成provided,就意味着該依賴對于主源碼classpath,以及測試classpath可用,但不會被打包。這正是servlet-api所需要的。

這裡歸納一下主要的依賴範圍以及作用:

依賴範圍(scope) 主源碼classpath可用 測試源碼classpath可用 會被打包
compile 預設值 TRUE
test FALSE
runtime
provided

需要注意的是,當我們沒有聲明依賴範圍的時候,其預設的依賴範圍是compile。

分類器(classifer)

GAV是Maven坐标最基本最重要的組成部分,但GAV不是全部。還有一個元素叫做分類器(classifier),90%的情況你不會用到它,但有些時候,分類器非常不可或缺。

舉個簡單的例子,當我們需要依賴TestNG的時候,簡單的聲明GAV會出錯,因為TestNG強制需要你提供分類器,以差別jdk14和jdk15,我們需要這樣聲明對TestNG的依賴:

1 <dependency>  
2   <groupId>org.testng</groupId>  
3   <artifactId>testng</artifactId>  
4   <version>5.7</version>  
5   <classifier>jdk15</classifier>  
6 </dependency>      

你會注意到maven下載下傳了一個名為testng-5.7-jdk15.jar的檔案。其命名模式實際上是<artifactId>-<version>-<classifier>.<packaging>。了解了這個模式以後,你就會發現很多檔案其實都是預設構件的分類器擴充,如 myapp-1.0-test.jar, myapp-1.0-sources.jar。

分類器還有一個非常有用的用途是:我們可以用它來聲明對test構件的依賴,比如,我們在一個核心子產品的src/test/java中聲明了一些基礎類,然後我們發現這些測試基礎類對于很多其它子產品的測試類都有用。沒有分類器,我們是沒有辦法去依賴src/test/java中的内容的,因為這些内容不會被打包到主構件中,它們單獨的被打包成一個模式為<artifactId>-<version>-test.jar的檔案。

我們可以使用分類器來依賴這樣的test構件:

1 <dependency>  
2   <groupId>org.myorg.myapp</groupId>  
3   <artifactId>core</artifactId>  
4   <version>${project.version}</version>  
5   <classifier>test</classifier>  
6 </dependency>        

了解了分類器,那麼可供依賴的資源就變得更加豐富。

依賴管理(dependencyManagement)

當你隻有一個Maven子產品的時候,你完全不需要看這個部分。但你心裡應該清楚,隻有一個Maven子產品的項目基本上隻是個玩具。

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

正确的做法是:

1. 在父子產品中使用dependencyManagement配置依賴

2. 在子子產品中使用dependencies添加依賴

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

這裡是一個來自于《Maven權威指南》的例子:

父子產品中如此聲明:

1 <project>  
 2   <modelVersion>4.0.0</modelVersion>  
 3   <groupId>org.sonatype.mavenbook</groupId>  
 4   <artifactId>a-parent</artifactId>  
 5   <version>1.0.0</version>  
 6   ...  
 7   <dependencyManagement>  
 8     <dependencies>  
 9       <dependency>  
10         <groupId>mysql</groupId>  
11         <artifactId>mysql-connector-java</artifactId>  
12         <version>5.1.2</version>  
13       </dependency>  
14       ...  
15     <dependencies>  
16   </dependencyManagement>        

子子產品中如此聲明:

1 <project>  
 2   <modelVersion>4.0.0</modelVersion>  
 3   <parent>  
 4     <groupId>org.sonatype.mavenbook</groupId>  
 5     <artifactId>a-parent</artifactId>  
 6     <version>1.0.0</version>  
 7   </parent>  
 8   <artifactId>project-a</artifactId>  
 9   ...  
10   <dependencies>  
11     <dependency>  
12       <groupId>mysql</groupId>  
13       <artifactId>mysql-connector-java</artifactId>  
14     </dependency>  
15   </dependencies>  
16 </project>        

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

小結

本文講述了一些Maven依賴中重要的概念,并通過樣例提供了一些最佳實踐,如依賴歸類,依賴範圍,分類器,以及依賴管理。我的目的是通過淺顯的例子講述那些你實際工作中會需要了解的80%的内容,如果你需要更深入的了解,請參考《Maven權威指南》 。

作者:no-npe

出處:https://www.cnblogs.com/geekdc

本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此聲明,且在文章頁面明顯位置給出原文連結,否則保留追究法律責任的權利。

由于作者個人水準有限,如果文中有什麼錯誤,歡迎指出。以免更多的人被誤導。