Maven解讀:強大的依賴體系


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持久化的處理,如果將持久層模塊的OracleMysql驅動均設置為非可選依賴,那么依賴這個持久層框架的項目類路徑中將同時出現Mysql以及Oracle的驅動Jar包,如果真的這樣,你會不會感覺到這種設計很奇葩~,因此MavenOptional關鍵字就誕生了~~~

重復引入的處理?

在什么情況下,會出現依賴的重復引入呢?

場景一:項目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項目已經編譯打包了~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM