Maven實戰:pom.xml與settings.xml

五月的倉頡發表於2016-07-30

pom.xml與settings.xml

pom.xml與setting.xml,可以說是Maven中最重要的兩個配置檔案,決定了Maven的核心功能,雖然之前的文章零零碎碎有提到過pom.xml和settings.xml裡面的內容,但都是大略帶過,學習與研究地並不細緻,本文的目的就是詳細研究下這兩個Maven重要的配置檔案,從這兩個配置檔案可以牽出非常多的Maven話題。

 

Maven座標

首先談一下為什麼要使用Maven座標。

Maven世界擁有數量非常巨大的構件,也就是平時使用的一些jar、war等檔案,在Maven為這些構件引入座標概念之前,我們無法使用任何一種方式來唯一標識所有這些構件。因此,如果需要使用Spring依賴,那麼就去Spring官網尋找;如果需要使用log4j依賴,那麼又去Apache官網尋找。又因為各個網站風格迥異,大量時間花費在了搜尋和瀏覽網頁的工作上。沒有統一規範與法則,工作就無法自動化,重複性的勞動本來就應該交給機器來做

Maven定義了這樣一組規則:世界上任何一個構件都可以使用Maven座標唯一標識,Maven座標元素包括groupId、artifactId、version、packaging、classifier,現在只要我們提供正確的元素座標,Maven就能找到對應的構件。至於去哪裡下載,Maven本身內建了一箇中央倉庫的地址"http://repo1.maven.org/maven2",該中央倉庫包含了世界上絕大部分流行的開源專案構件,Mavne會在需要的時候去那裡下載,當然也可以配置自己的中央倉庫地址,去自己的中央倉庫下載構件。

舉個例子,Spring的context:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>4.2.6.RELEASE</version>
</dependency

看一下下屬的各個元素:

  • groupId:定義當前Maven專案隸屬的實際專案。由於Maven專案和實際專案未必是一對一的關係,比如SpringFramework這個實際專案可能對應的Maven專案有很多,像core、context、expression等等,因此groupId不應該對應專案隸屬的公司或組織,否則artifact將很難定義
  • artifactId:定義實際專案中的一個Maven模組,推薦的做法是使用實際專案名稱作為artifactId的字首,這樣會很方便去尋找實際構件
  • version:定義Maven專案當前所處的版本,如上面的spring-context就是4.2.6的,RELEASE表示正式發行版本
  • packing:定義Maven專案的打包方式,這項不是必須的,沒列出來,不定義預設就是jar的打包方式
  • classifier:幫助定義構件輸出的一些附屬構件,比如xxx-javadoc.jar、xxx-sources.jar,附屬構件與主構件對應,這項是不能直接定義的

Maven座標的概念大致上就是這樣,理解Maven座標,是理解Maven很重要的一步。

 

傳遞性依賴

什麼是傳遞性依賴,以Spring舉一個例子。使用Spring的時候會依賴於其他開源的類庫,此時有兩種做法:

1、下載一個很大的.zip包,裡面包含了所有Spring的jar,但是這麼做往往就引入了許多不必要的依賴

2、只下載spring相關的.zip包,不包含依賴,實際使用的時候根據出錯資訊,加入需要的其他依賴

顯然這兩種做法都非常麻煩,Maven的傳遞性依賴機制很好地解決了這一問題。開啟spring-core-4.1.0.RELEASE的pom.xml,我擷取一段關鍵部分:

<dependencies>
    <dependency>
      <groupId>commons-codec</groupId>
      <artifactId>commons-codec</artifactId>
      <version>1.9</version>
      <scope>compile</scope>
      <optional>true</optional>
    </dependency>
    <dependency>
      <groupId>commons-logging</groupId>
      <artifactId>commons-logging</artifactId>
      <version>1.1.3</version>
      <scope>compile</scope>
    </dependency>
    ...
</dependencies>

比如A專案依賴了spring-core,spring-core又依賴了commons-codec和commons-logging,那麼commons-codec和commons-logging就是A專案的一個傳遞性依賴。有了傳遞性依賴機制,在使用spring-core的時候就不用去考慮它依賴了什麼,也不用擔心引入多餘的依賴,Maven會解析各個直接依賴的POM,將那些必要的間接依賴,以傳遞性依賴的形式引入到當前的專案中去。

有了傳遞性依賴機制,一方面大大簡化和方便了依賴宣告,另一方面在大部分情況下我們只需要關心專案的直接依賴是什麼而不用考慮這些直接依賴會引入什麼傳遞性依賴,不過有時候傳遞性依賴也會有一些問題,此時我們就需要清除地知道該傳遞性依賴是從哪條路徑引入的,這就叫依賴調解,依賴調解主要有兩點原則:

1、A->B->C->X(1.0),A->D->X(2.0),此時兩條依賴路徑上有兩個版本的X,此時遵循路徑最近者優先,因此X(2.0)將被解析使用

2、A->B->Y(1.0),A->C->Y(2.0),Y(1.0)和Y(2.0)的依賴長度是一樣的,從Maven2.0.9開始,此時遵循第一宣告者優先,即順序最靠前的那個依賴優先

 

排除依賴

傳遞性依賴會給專案隱式地引入很多依賴,這極大地簡化了專案依賴的管理,但是有時候這種特性也會帶來問題。比如有種情況:

當前專案依賴A,A由於某些原因依賴了另外一個類庫的SNAPSHOT版本,那麼這個SNAPSHOT就會成為當前專案的傳遞性依賴,二SNAPSHOT的不穩定性將直接影響到當前的專案,此時就需要排除該SNAPSHOT,並且在當前專案中宣告該類庫的某個正式釋出的版本

排除依賴很簡單,看一下寫法:

<dependency>
    <groupId>com.alibaba.rocketmq</groupId>
    <artifactId>rocketmq-client</artifactId>
    <version>3.2.7</version>
    <exclusions>
        <exclusion>
            <groupId>apache-lang</groupId>
            <artifactId>commons-lang</artifactId>
        </exclusion>
    </exclusions>
</dependency>

這裡我引入了rocketmq的依賴,但是我不想依賴rocketmq裡面的apache-lang,而想要自己引入依賴,所以我就把apache-lang給排除了。

這裡需要注意的是,宣告exclusion的時候只需要groupId和artifactId即可,而不需要version元素,這是因為只需要groupId和artifactId就能唯一定位依賴圖中的某個依賴。換句話說,Maven解析後的依賴中,不可能出現groupId和artifactId相同,但是version不同的兩個依賴。

 

settings.xml

settings.xml裡面是Maven的基本配置,元素比較多,逐一看一下

1、proxy

proxy表示Maven的代理,看一下寫法:

<proxies>
    <proxy>
      <id>optional</id>
      <active>true</active>
      <protocol>http</protocol>
      <username>proxyuser</username>
      <password>proxypass</password>
      <host>proxy.host.net</host>
      <port>80</port>
      <nonProxyHosts>local.net|some.host.com</nonProxyHosts>
    </proxy>
</proxies>

需要proxy是因為很多時候你所在的公司基於安全因素考慮,要求你使用通過安全認證的代理訪問因特網。這種情況下,就需要為Maven配置HTTP代理,才能讓它正常訪問外部倉庫,以下載所需要的資源。proxies下可以配置多個proxy元素,如果宣告瞭多個proxy元素,則預設情況下第一個被啟用的proxy會生效。active為true表示啟用該代理,protocol表示使用的代理協議,當然最重要的是指定正確的主機名(host)和埠(port),如果代理伺服器需要認證則配置username和password,nonProxyHost元素表示指定哪些主機名不需要代理,可以用"|"分隔多個主機名,也支援萬用字元"*"。

2、repository

repository表示Maven的中央倉庫,因為儘管預設的遠端倉庫中的構件非常龐大,但是總歸會有不滿足我們需求的時候,這時候就要用到別的中央倉庫了。看一下寫法:

<repository>
    <id>public</id>
    <name>local private nexus</name>
    <url>http://192.168.1.6:8081/nexus/content/groups/public</url>
    <releases>
        <enabled>true</enabled>
    </releases>
    <snapshots>
        <enabled>false</enabled>
    </snapshots>
</repository>

可以宣告多個repository。id必須是唯一的,尤其注意,Maven自帶的中央倉庫使用的id為central,如果其他倉庫宣告也用該id,就會覆蓋中央倉庫的配置。releases和snapshots比較重要,前者表示開啟倉庫的釋出版本下載支援,後者表示關閉倉庫的快照版本下載支援,這樣一來,Maven就會去倉庫下載釋出版本的構件而不會下載快照版本的構件了。

3、server

大部分遠端倉庫無須認證就可以訪問,但是有時候處於安全方面的因素考慮,需要提供認證資訊才能訪問一些遠端倉庫,處於安全考慮,認證資訊一般只放在settings.xml中,server就是認證元素。看一下配置:

<server>
    <id>nexus-releases</id>
    <username>deployment</username>
    <password>deployment</password>
</server>

這裡的關鍵是id,這個id必須與需要認證的repository元素的id完全一致才行,換句話說,正式這個id將認證資訊和倉庫配置聯絡在了一起。

4、mirror

如果倉庫X可以提供倉庫Y儲存的所有內容,那麼就可以認為倉庫X是倉庫Y的一個映象(mirror),換句話說,任何一個可以從Y中獲取到的構件夠可以從X中獲取到。舉個例子,"http://maven.net.cn/content/groups/public/"是中央倉庫"http://repo1.maven.org/maven2/"在中國的映象,由於地理位置的因素,該映象往往能夠提供比中央倉庫更快的服務,這就是為什麼要使用mirror的原因。

看一下mirror的配置:

<mirror>
    <id>nexus</id>
    <name>internal nexus repository</name>
    <url>http://192.168.1.6:8081/nexus/content/groups/public</url>
    <mirrorOf>*</mirrorOf>
</mirror>

該例子中,mirrof為*,表示該配置為所有中央倉庫的映象,任何對於中央倉庫的請求都會轉至該映象。另外三個元素id、name、url與一般倉庫配置無異,表示該映象倉庫的唯一識別符號、名稱以及地址。類似的,如果該映象需要認證,也可以基於該id配置倉庫認證。

相關文章