有人認爲Maven是一個依賴管理工具,當然這種想法是錯誤的(確切的說Maven是一個項目管理工具,貫穿了整個項目生命週期,編譯,測試,打 包,發佈...),但Maven給人造成這種錯誤的印象也是有原因的,因爲Maven的依賴管理十分強大,用好了Maven,你不再需要面對一大堆jar 感到頭大,依賴衝突,無用依賴等問題也能夠得到有效的防止和解決。
最簡單的依賴
依賴是使用Maven座標來定位的,而Maven座標主要由GAV(groupId, artifactId, version)構成。因此,使用任何一個依賴之間,你都需要知道它的Maven座標。
最簡單的依賴如:
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.4</version>
</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有它的解決方案:
<dependencies>
<dependency>
<groupId>org.spring.framework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.spring.framework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.spring.framework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.spring.framework</groupId>
<artifactId>spring-mock</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies>
<properties>
<spring.version>2.5</spring.version>
</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文件打入最終的發佈包也不是好事,這無謂的增加了發佈包的大小。
其實我們應該這樣做:
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.4</version>
<scope>test</test>
</dependency>
於是,junit對於主源碼classpath不可用,對於測試源碼classpath可用,不會被打包。
再舉個例子,在開發javaee應用的時候我們一定會用到servlet-api,它對於主源碼和測試源碼都是必要的,因爲我們的代碼中會引入 servlet-api的包。但是,在打包的時候,將其放入WAR包就會有問題,因爲web容器會提供servlet-api,如果我們再將其打包就會造 成依賴衝突,解決方案如下:
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.4</version>
<scope>provided</scope>
</dependency>
將依賴範圍設置成provided,就意味着該依賴對於主源碼classpath,以及測試classpath可用,但不會被打包。這正是servlet-api所需要的。
這裏歸納一下主要的依賴範圍以及作用:
依賴範圍(scope) | 主源碼classpath可用 | 測試源碼classpath可用 | 會被打包 |
compile 缺省值 | TRUE | TRUE | TRUE |
test | FALSE | TRUE | FALSE |
runtime | FALSE | TRUE | TRUE |
provided | TRUE | TRUE | FALSE |
需要注意的是,當我們沒有聲明依賴範圍的時候,其默認的依賴範圍是compile。