Github地址:https://github.com/zwjlpeng/Maven_Detail
Maven最大的好處就是能夠很方便的管理項目對第三方Jar包的依賴,只需在Pom文件中添加幾行配置文件,就可以將第三方的Jar包納入自已項目的類路徑下,在Pom配置文件中我們也可以指定第三方Jar包的版本號,表明依賴第三方某一版本的Jar包,因此通過Maven管理項目的依賴,我們可以很容易的對項目依賴的第三方Jar包進行升級,升級過程中需要我們做的僅僅是更改配置文件->重新mvn package即可,是不是so easy~,想想通過傳統方式搭建的Web工程,當我們為了解決低版本Spring中的BUG【如版本3.2.3中,當我們設置了@RequestParam(value="username", required=false)注解后,傳入參數為空時,系統卻拋出異常~】時,不得不升級Spring的版本時,我們需要怎么做?需要到Spring官網下載高版本,然后將相應的Jar包添加到工程的Build Path下,重新編譯工程,當一個Jar包需要升級時,估且可以忍受,但是當多了,我想大概也只能呵呵了...
當很多人從一個軟件遷移到另一個軟件並不再回頭的時候,就值得我們注意了...
直接依賴/間接依賴
Maven中最容易理解的就是直接依賴,A項目的運行需要有B項目的存在,這就是一個直接依賴,當B項目的運行需要有C項目的存在時,這里就存在A項目對C項目的一個間接依賴,A項目要想成功運行,在其類路徑下必須要存在B項目和C項目,當然使用Maven我們不需要關注這種間接依賴,Maven會幫我們處理,如我們的項目中需要spring-core,在Pom文件中我們只需要添加如下配置,再看看我們的類路徑是不是存在了spring-core-4.1.4.RELEASE.jar/commons-logging-1.2.jar兩個Jar包,其中這個commons-logging-1.2.jar包就是通過間接依賴加入類路徑下的~
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>4.1.4.RELEASE</version> </dependency>
再看看spring-core-4.1.4.RELEASE.jar里面的Pom文件,在這個Pom文件中將對commons-logging-1.2.jar依賴設置為Compile並且非可選,下是是其Pom.xml文件
<dependency> <groupId>commons-codec</groupId> <artifactId>commons-codec</artifactId> <version>1.10</version> <scope>compile</scope> <optional>true</optional> </dependency> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.2</version> <scope>compile</scope> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> <scope>compile</scope> <optional>true</optional> </dependency> <dependency> <groupId>net.sf.jopt-simple</groupId> <artifactId>jopt-simple</artifactId> <version>4.8</version> <scope>compile</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.4</version> <scope>compile</scope> <optional>true</optional> </dependency>
在這個Pom.xml文件中,只有這個Jar包是必須的,其他依賴均是可選的(Optional)依賴,在Maven中可選依賴是不會進行傳遞的,為什么要有可選依賴呢?例如,一個持久層模塊不但可以持久化Oracle數據庫,也可以持久化Mysql數據庫,那么在這個持化層框架中應該對應了兩份代碼,一份是關於Oracle持久化的處理,一份是關於Mysql持久化的處理,如果將持久層模塊的Oracle與Mysql驅動均設置為非可選依賴,那么依賴這個持久層框架的項目類路徑中將同時出現Mysql以及Oracle的驅動Jar包,如果真的這樣,你會不會感覺到這種設計很奇葩~,因此Maven中Optional關鍵字就誕生了~~~
重復引入的處理?
在什么情況下,會出現依賴的重復引入呢?
場景一:項目A依賴於項目B與項目C,項目B與項目C均依賴於項目D,這時項目中就會出現兩份D項目(有可能B項目與C項目依賴的D項目的版本還不一樣~),這種情況下,Maven是如何處理的呢?
規則,第一聲明者優化
這種情況下,Maven處理方法,是完全依賴於項目B與項目C在Pom.xml文件中聲明的順序,哪個聲明在前就使用哪個項目的D依賴,如下是一個典型的例子~
<dependency> <groupId>org.apache.hbase</groupId> <artifactId>hbase-common</artifactId> <version>0.99.0</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>4.1.4.RELEASE</version> </depend
上面Pom配置文件中最終依賴的commons-logging為commons-logging-1.1.3.jar,但是當我們調整配置文件依賴的順序,變成如下配置時,commons-logging的版本又變為了commons-logging-1.2.jar,結論是完全符合第一聲明者優化的原則
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>4.1.4.RELEASE</version> </dependency> <dependency> <groupId>org.apache.hbase</groupId> <artifactId>hbase-common</artifactId> <version>0.99.0</version> </dependency>
場景二:一個Pom文件中聲明了對一個項目的高低版本的依賴
規則,使用最后聲明者,示例如下
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <!-- 版本一 --> <version>2.0.6</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <!-- 版本二 --> <version>4.1.4.RELEASE</version> </dependency>
最終spring-core-4.1.4.RELEASE.jar的版本號為4.1.4,但當將版本一與版本二的順序調換后,則項目類路徑下依賴的Jar包就會變成2.0.6
場景三:A項目依賴於B項目,B項目依賴於D項目,A項目依賴於C項目,C項目依賴於E項目,E項目又依賴於D項目,簡化一下,即A->B->D/A->C->E->D,這時項目中也會由於傳遞依賴,引入了對D項目的兩次依賴
規則:最短路徑優先
在場景三中,由於A項目從B項目得到對D項目的依賴路徑要比從C項目中獲取D項目依賴的路徑要短,說起來太轉口了~,因此會優先采用從B項目中得到的D依賴,如下是一個示例~
這個在開源項目中,找了半天也沒有找到很好的例子
Maven中的依賴范圍
什么是依賴范圍,依賴范圍指明了第三方Jar包在類路徑中的可見性,當我們向項目中添加了依賴,默認的依賴范圍是Compile
Maven中具體的幾種編譯范圍如下
compile:默認的編譯范圍,表示該Jar包在編譯、運行、測試類路徑中均可見
test:表示該Jar包僅在測試類路徑中可見,正式發布打包時,里面沒有該Jar包
provided:表示該Jar包在運行時由服務器提供,該Jar包對於編譯和測試classpath有效,但在運行時無效,即發布時,最終打包的項目中不會含有該Jar包。典型范例:servlet-api
runtime:運行時依賴范圍,對於測試和運行classpath有效,但在對編譯主代碼時無效。典型范例:JDBC
system:系統依賴范圍,使用system范圍的依賴必須通過systemPath元素顯示地指定依賴文件的路徑,不依賴Maven倉庫解析,但是這樣會對項目的移植性造成嚴重的影響,如下是使用系統依賴的一個典型例子
<dependency> <artifactId>com.netease</artifactId> <groupId>httpclient</groupId> <version>1.0</version> <scope>system</scope> <systemPath>E:\source\Crawer\lib\commons-httpclient-3.1_2.jar</systemPath> </dependency
在上面配置文件中,真正有作用的一句就是systemPath,其他的可以隨意配置,沒什么影響~,經過上面的配置后,可以發現項目的classpath中已經出現了commons-httpclient-3.1_2.jar包
作用system指定依賴范圍,該依賴范圍會出現在編譯、測試、運行時的類路徑下
import:這個范圍maven 2.0.9以上才支持,據說是為了支持繼承,沒用過~~~
傳遞性依賴的依賴范圍處理
傳遞性會導致依賴范圍的變更,如A項目對B項目的依賴范圍是compile,B項目對C項目的依賴范圍是provided,那么最終A項目對C項目的依賴范圍是啥子呢?以下是Maven官方給的一個表格
compile | provided | runtime | test | |
compile | compile | - | runtime | - |
provided | provided | - | provided | - |
runtime | runtime | - | runtime | - |
test | test | - | test | - |
在這個表格中第一列代表的是一級依賴,第一行代表的是二級依賴,如表格中的紅包部分,A項目對B項目的依賴范圍是provided,B項目對C項目的依賴是compile,那么最終A項目對C項目的依賴是provided,在表格中-代表的是在傳遞性依賴中該依賴會被忽略
好了現在可以回答剛才提的問題,A項目對B項目是compile,B項目對C項目是provided那么最終的結果是A項目不對C項目進行依賴~
解答,A項目對B項目有依賴,說明B項目必須要在A項目的編譯路徑下,B項目對C項目是provided說明該依賴是由容器來提供的,在正式的發布包中B項目是不包含C項目的Jar包,因此A項目對B項目依賴后,會認為C項目是由容器提供,因此會忽略該依賴,默認認為容器中有,此時也不會報錯,因為B項目已經編譯打包了~