第六章 倉庫

座標和依賴是任何一個構件在Maven世界中的邏輯表示方式;而構件的物理表示方式就是文件,Maven經過倉庫來統一管理這些文件。 ###6.1 何爲Maven倉庫###   在Maven世界中,任何一個依賴、插件或者項目構建的輸出,均可以稱爲構件。例如,依賴log4j-1.2.15.jar是一個構件,插件maven-compiler-plugin-2.0.2.jar是一個構件。任何一個構件都有一組座標惟一標識。
  得益於座標機制,任何Maven項目使用任何一個構件的方式都是徹底相同的。在此基礎上,Maven能夠在某個位置統一存儲全部Maven項目共享的構件,這個統一的位置就是倉庫。實際的Maven項目將再也不各自存儲其餘依賴文件,它們只須要聲明這些依賴的座標,在須要的時候(例如,編譯項目的時候須要將依賴加入到classpath中),Maven會自動根據座標找到倉庫中的構件,並使用它們。
  爲了實現重用,項目構建完畢後生成的構件也能夠安裝或者部署到倉庫中,供其餘項目使用。 ###6.2 倉庫的佈局###   任何一個構件都有其惟一的座標,根據這個座標能夠定義其在倉庫中的惟一儲存路徑,這即是Maven的倉庫佈局方式。例如,log4j:log4j:1.2.15這個依賴,其對應的倉庫路徑爲log4j/log4j/1.2.15/log4j-1.2.15.jar,該路徑與座標的大體對應關係爲groupId/artifactId/version/artifactId-version(-classifier).packaging。   Maven倉庫是基於簡單文件系統存儲的。 ###6.3 倉庫的分類###   對於Maven來講,倉庫只有兩類:本地倉庫和遠程倉庫。當Maven根據座標尋找構件的時候,它首先會查看本地倉庫,若是本地倉庫存在此構件,則直接使用;若是本地倉庫不存在此構件,或者須要查看是否有更新的構件版本,Maven就會去遠程倉庫查找,發現須要的構件以後,下載到本地倉庫再使用。若是本地倉庫和遠程倉庫都沒有須要的構件,Maven就會報錯。
  有幾類特殊的遠程倉庫。中央倉庫是Maven核心自帶的遠程倉庫,它包含了絕大部分開源的構件。在默認配置下,當本地倉庫沒有Maven須要的構件的時候,它就會嘗試從中央倉庫下載。私服是另外一種特殊的遠程倉庫,爲了節省帶寬和時間,應該在局域網內架設一個私有的倉庫服務器,用其代理全部外部的遠程倉庫。內部的項目還能部署到私服上供其餘項目使用。除了中央倉庫和私服,還有不少其餘公開的遠程倉庫,常見的有Java.net Maven庫和JBoss Maven庫。
  輸入圖片說明 ####6.3.1 本地倉庫####   默認狀況下,無論是Windows仍是linux上,每一個用戶在本身的用戶目錄下都有一個路徑名爲.m2/repository/的倉庫目錄。注意,在Linux系統中,以點(.)開頭的文件或目錄默認是隱藏的,可使用ls-a命令顯示隱藏的文件或目錄。
  有時候,由於某些緣由(例如c盤空間不夠),用戶會想要自定義本地倉庫目錄地址。這時,能夠編輯文件~/.m2/settings.xml,設置localRepository元素的值爲想要的倉庫地址。例如:java

<Settings>
    <localRepository>D:\java\repository\</localRepository>
</settings>

  這樣, 該用戶的本地倉庫地址就設置成了D:\java\repository。 ####6.3.2 遠程倉庫####   對maven用戶來講,每一個用戶只有一個本地倉庫可是能夠有多個遠程倉庫。 ####6.3.3 中央倉庫####   中央倉庫是Maven的默認使用的遠程倉庫,Maven的安裝文件自帶了中央倉庫的配置。可使用解壓工具打開jar文件$M2_HOME/lib/maven-model-builder-3.0.jar,而後訪問路徑org/apache/maven/model/pom-4.0.0.xml。能夠看到:linux

<repositories>
    <repository>
      <id>central</id>
      <name>Central Repository</name>
      <url>http://repo.maven.apache.org/maven2</url>
      <layout>default</layout>
      <snapshots>
        <enabled>false</enabled>
      </snapshots>
    </repository>
  </repositories>

  包含這段配置的文件是全部Maven項目都會繼承的超級POM。這段配置使用的id central對中央倉庫進行惟一標識,其名稱是Maven repository Switchborad,它使用default倉庫佈局。最後須要注意的是snapshots元素,其子元素enabled的值爲false,表示不從該中央倉庫下載快照版本的構件。
  中央倉庫包含了這個世界上絕大多數流行的開源Java軟件,以及源碼、做者信息、SCM、信息、許可證信息等。通常來講,一個簡單的Maven所須要的全部依賴構件都能從中央倉庫下載到。
####6.3.4 私服####   私服是一種特殊的遠程倉庫,它是架設在局域網中的倉庫服務,私服代理廣域網上的遠程倉庫,供局域網內的Maven用戶使用。當Maven須要下載構件的時候,它從私服請求,若是私服上不存在該構件,則從外部的遠程倉庫下載,緩存在私服上以後,再爲Maven的下載請求提供服務。此外,一些沒法從外部倉庫下載到的構件也能從本地上傳到私服上供你們使用(這點很重要,公司內部不少項目都是須要互通的,可是要求公司外部沒法訪問這些資源)。
  私服的用途:
  輸入圖片說明
  私服的好處相似於計算機上使用的緩存機制。詳細來講有如下幾點:apache

  • 節省本身的外網帶寬:大量的對於外部倉庫的重複請求會消耗很大的帶寬,利用私服代理外部倉庫以後,對外的重複構件下載便得以消除,即下降外網帶寬的壓力。
  • 加速Maven構建:不停地鏈接請求外部倉庫是十分耗時的,使用私服能夠很好的解決這一問題,當Maven只須要檢查局域網內私服的數據時,構建的速度便能獲得很大程度的提升。
  • 部署第三方構件:創建私服後,能夠將內部構件部署到這個內部的倉庫中,供內部的Maven項目使用。
  • 提升穩定性,加強控制:Maven構件高度依賴於遠程倉庫,所以,當Internet不穩定時,Maven的構建也會不穩定,甚至沒法構建。使用私服,即便暫時沒有Internet連接,Maven也能正常的使用。此外,一些私服軟件(如nexus)還提供了不少額外的功能,如權限管理、REALEASE/SNAPSHOT分區等,管理員能夠對倉庫進行一些更高級的控制。
  • 下降中央倉庫的負荷 ###6.4 遠程倉庫的配置###   在不少狀況下,默認的遠程倉庫沒法知足項目的需求,可能項目須要的構件存在於另一個遠程倉庫中,如JBoss Maven倉庫。這時,能夠在POM中配置該倉庫(在settings.xml中能夠配置遠程倉庫,其做用範圍不一樣):
<repositories>
        <repository>
          <id>jboss</id>
          <name>JBoss Repository</name>
          <url>http://repository.jboss.com/maven2</url>
          <releases>
            <enabled>true</enabled>
          </releases>
          <snapshots>
            <enabled>false</enabled>
          </snapshots>
          <layout>default</layout>
        </repository>
      <repositories>

  在repositories元素中,能夠包含一個或多個用repository子元素聲明的遠程倉庫。任何一個倉庫使用id必須是惟一的,因爲Maven使用的中央倉庫id是central,若是其餘倉庫聲明也使用了這個id,就會覆蓋中央倉庫的配置。該配置中的url值指向了倉庫的地址,通常來講,該地址都基於http協議,Maven用戶能夠瀏覽器中打開倉庫地址瀏覽構件。
  配置中的release和snapshots元素用來控制Maven對於發佈版構件和快照版構件的下載。關於下面的enabled元素,若是enabled爲true,則表示支持其對應的release或者snapshots版本下載,反之亦然。除了enabled,它們還包含另外兩個子元素updatePolicy和checksumPolicy:瀏覽器

<snapshots>
     <enabled>true</enabled>
     <updatePolicy>daily</updatePolicy>
     <checksumPolicy>ignore</checksumPolicy>
</snapshots>

  元素updatePolicy用來配置Maven從遠程倉庫檢查更新的頻率,默認的值是daily,表示Maven天天檢查一次。其餘可用的值包括:never-從不檢查更新;always-每次構建都檢查更新;interval:X - 每隔X分鐘檢查一次更新(X爲任意整數)。
  元素checksumPolicy用來配置Maven檢查檢驗和文件的策略。當構件被部署到Maven倉庫中時,會同時部署對應的檢驗和文件。checksumPolicy用來決定當下載構件時驗證校驗和失敗所採起的策略:當checksumPolicy的值爲默認的warn時,Maven會在執行構件時輸出警告信息,其餘可用的值包括:fail-Maven遇到檢驗和錯誤就讓構件失敗;ignore-使Maven徹底忽略校驗和錯誤。
####6.4.1 遠程倉庫的配置####   大部分遠程倉庫不須要認證,有時出於安全考慮,好比某個公司本身搭建的遠程倉庫服務器,咱們須要提供認證信息才能訪問。這時,爲了能讓Maven訪問倉庫內容,就須要配置認證信息。
  配置認證信息和配置倉庫信息不一樣,認證信息只能配置在settings.xml文件中,這是由於POM每每是被提交到代碼倉庫中供全部成員訪問的,而setting.xml通常只放在本機。所以,在settings.xml中配置認證信息更爲安全。
  假設須要一個id爲my-proj的倉庫配置認證信息,編輯settings.xml文件見代碼以下:緩存

<settings>
...
  <servers>
    <server>
      <id>my-proj</id>
      <username>repo-user</username>
      <password>repo-pwd</password>
    <server>
  <servers>	
...
</settings>

  settings.xml中server元素的id必須與POM中須要認證的repository元素的id徹底一致。換句話說,正是這個id將認證信息與倉庫配置聯繫在了一塊兒。 ####6.4.2 部署至遠程倉庫####   私服的一大做用是部署第三方構件,包括組織內部生成的構件以及一些沒法從外部倉庫直接獲取的構件,供其餘團隊使用。
  首先,須要編輯項目的pom,xml文件。配置distributionManagement元素見代碼以下:安全

<project>
...
  <distributionManagement>
    <repository>
      <id>proj-release</id>
      <name>Proj Release Repository</name>
      <url>http://192.168.1.100/content/repositories/proj-releases</url>
    </repository>
    <snapshotRepository>
      <id>proj-snapshots</id>
      <name>Proj Snapshot Repository</name>
      <url>http://192.168.1.100/content/repositories/proj-snapshots</url>
    </snapshotRepository>		
   <distributionManagement>
...
<project>

  distributionManagement包含repository和snapshotRepository子元素,前者表示發佈版本構件的倉庫,後者表示快照版本的倉庫。這兩個元素下都須要配置id、name和url,id爲該遠程倉庫的惟一標識,那麼是爲了方便閱讀,關鍵的url表示該倉庫的地址。
  往遠程倉庫部署構件的時候,每每須要認證。簡而言之,就是須要在settings,xml中建立一個server元素,其id與倉庫的id匹配,並配置正確的認證信息。不管從遠程倉庫下載構件,仍是部署構件至遠程倉庫,當須要認證的時候,配置的方式是同樣的。
  配置正確後,在命令行運行 mvn clean deploy ,Maven就會將項目構建輸出的構件部署到配置對應的遠程倉庫,若是項目當前的版本是快照版本,則部署到快照版本倉庫地址,不然就部署到發佈版本倉庫地址。 ###6.5 快照版本###   在Maven的世界中,任何一個項目或者構件都必須有本身的版本。版本的值多是1.0.0,1.3-alpha-4,2.0,2.1-SNAPSHOT或者2.1-20091214.221414-13。其中,1.0、1.3-alpha-4和2.0是穩定的發佈版本,而2.1-SNAPSHOT和2.1-20091214.221414-13是不穩定的快照版本。
  Maven爲何要區分發布版本和快照版本呢?簡單的1.0.0、1.二、2.1等不就夠了嗎?爲何還要2.1-SNAPSHOT,甚至是長長的2.1-20091214.221414-13?試想一下這樣的狀況,小張在開發模塊A的2.1版本,該版本還未正式發佈,與模塊A一同開發的還有模塊B,它由小張的同事季MM開發,B的功能依賴於A。在開發的過程當中,小張須要常常將本身最新的構建輸出,交給季MM,供她開發和集成調試,問題是,這個工做如何進行呢?
  若是不停更新版本2.1.一、2.1.二、2.1.3....呢?首先,小張和季MM兩人都須要頻繁地更改POM,若是有更多的模塊依賴於模塊A,就會涉及更多的POM更改;其次,大量的版本其實僅僅包含了微小的差別,這樣也會形成爲版本號的濫用。
  Maven的快照版本機制就是爲了解決上述問題。在該例中,小張只須要將模塊A的版本設定爲2.1-SNAPSHOT,而後發佈到私服中,在發佈的過程當中,Maven會自動爲構件打上時間戳。好比:2.1-20091214.221414-13就表示2009年12月14日 22點14分14秒的第13次快照。有了該時間戳,Maven就能隨時找到倉庫中該構件2.1-SNAPSHOT版本最新的文件。這時,季MM配置對於模塊A的2.1-SNAPSHOT版本的依賴,當她構件模塊B的時候,Maven會自動從倉庫中檢查模塊A的2.1-SNAPSHOT的最新構件,當發現有更新時便進行下載。默認狀況下,Maven天天檢查一次更新(由倉庫配置的updatePolicy控制),用戶也可使用命令行-U參數強制讓Maven檢查更新,如:mvn clean install-U。
  基於快照版本機制,小張在構建成功以後才能將構件部署至倉庫,而季MM能夠徹底不用考慮模塊A的構建,而且她能確保隨時獲得模塊A的最新可用的快照構件,而這一切都不須要額外的手工操做。 ###6.6 從倉庫解析依賴的機制###   Maven是根據怎樣的規則從倉庫解析並使用依賴構件的呢?
  當本地倉庫沒有依賴構件的時候,Maven會自動從遠程倉庫下載。當依賴版本爲快照版本的時候,Maven會自動找到最新的的快照。這背後的依賴解析機制能夠歸納以下: 1.當依賴的範圍是system的時候,Maven直接從本地文件系統解析構件。 2.根據依賴座標計算倉庫路徑後,嘗試直接從本地倉庫尋找構件,若是發現相應構件,則解析成功。 3.在本地倉庫不存在相應構件的狀況下,若是依賴的版本是顯式的發佈版本構件,如:1.2,2.1等,則遍歷全部的遠程倉庫,發現後,下載並解析使用。 4.若是依賴的版本是RELEASE或者LATEST,則基於更新策略讀取全部遠程倉庫的元數據groupId/artifactId/mavenmetadata.xml,將其與本地倉庫的對應元數據合併後,計算出RELEASE或者LATEST真實的值,而後基於這個真實的值檢查本地和遠程倉庫,如步驟1和3。 5.若是依賴的版本是SNAPSHOT,則基於更新策略讀取全部遠程倉庫的元數據groupId/artifactId/version/mavenmetadata.xml,將其與本地倉庫的對應元數據合併後,獲得最新快照版本的值,而後基於該值檢查本地倉庫,或者從遠程倉庫下載。 6.若是最後解析獲得的構件版本是時間戳格式的快照,如:1.4-20091104.121450-121,則複製其時間戳格式的文件到非時間戳格式,如:SNAPSHOT,並使用該非時間戳格式的構件。
  當依賴的版本不明晰的時候,如:RELEASE,LATEST和SNAPSHOT,Maven就須要基於更新遠程倉庫的更新策略來檢查更新。在前面的倉庫配置blog中,有一些配置與此有關;首先是<releases><enabled>和<snapshots><enabled>,只有倉庫開啓了對於發佈版本的支持時,才能訪問該倉庫的發佈版本構件信息,對於快照版本也是同理;其次要注意的是<releases>和<snapshots>的子元素<updatePolicy>,該元素配置了檢查更新的頻率。最後,用戶還能夠從命令行加入參數-U,強制檢查更新,使用參數後,Maven就會忽略<updatePolicy>的配置。
  當Maven檢查完更新策略,並決定檢查依賴更新的時候,就須要檢查倉庫元數據maven-metadata.xml。回顧一下前面提到的RELEASE和LATEST版本,它們分別對應了倉庫中存在的該構件的最新發布版本和最新版本(包含快照),而這兩個"最新"是基於groupId/artifactId/maven-metadata.xml計算出來的,如:服務器

<?xml version="1.0" encoding="UTF-8"?>  
<metadata>  
  <groupId>org.sonatype.nexus</groupId>  
  <artifactId>nexus</artifactId>  
  <versioning>  
    <latest>1.4.2-SNAPSHOT</latest>  
    <release>1.4.0</release>  
    <versions>  
      <version>1.3.5</version>  
      <version>1.3.6</version>  
      <version>1.4.0-SNAPSHOT</version>  
      <version>1.4.0</version>  
      <version>1.4.0.1-SNAPSHOT</version>  
      <version>1.4.1-SNAPSHOT</version>  
      <version>1.4.2-SNAPSHOT</version>  
    </versions>  
  </versioning>  
</metadata>

  在XML文件列出了倉庫中存在的該構件全部可用的版本,同時latest元素指向了這些版本中最新的那個版本。而release元素指向了這些版本中最新的發佈版本。Maven經過合併多個遠程倉庫及本地倉庫的元數據,就能計算出基於全部倉庫的latest和release分別是什麼,而後再解析具體的構件。
  須要注意的是,在依賴聲明中使用LATEST和RELEASE是不推薦的作法,由於Maven隨時均可能解析到不一樣的構件,可能今天LATEST是1.3.6,明天就成了1.4.0-SNAPSHOT了,且Maven不會明確告訴用戶這樣的變化。當這種變化形成構建失敗的時候,發現問題變得比較困難。RELEASE由於對應的是最新發布版構建,還相對可靠,LATEST就很是不可靠了,爲此,Maven3再也不支持在插件配置中使用LATEST和RELEASE。若是不設置插件版本,其效果就和RELEASE同樣,Maven只會解析最新的發佈版本構件。不過即便這樣,也還存在潛在的問題。例如,某個依賴的1.1版本與1.2版本可能發佈一些接口的變化,從而致使當前Maven構建失敗。 當依賴的版本設爲快照版本的時候,Maven也須要檢查更新,這時,Maven會檢查倉庫元數據groupId/artifactId/version/maven-metadata.xml,如例:maven

<?xml version="1.0" encoding="UTF-8"?>  
<metadata>  
  <groupId>org.sonatype.nexus</groupId>  
  <artifactId>nexus</artifactId>  
  <version>1.4.2-SNAPSHOT</version>  
  <versioning>  
    <snapshot>  
      <timestamp>20091214.221414</timestamp>  
      <buildNumber>13</buildNumber>  
    </snapshot>  
    <lastUpdated>20091214221558</lastUpdated>  
  </versioning>  
</metadata>

  該xml文件的snapshot元素包含timestamp和buildNumber兩個子元素,分別表明了這一快照的時間戳和構建號,基於這兩個元素能夠獲得該倉庫中此快照的最新構件版本實際爲1.4.2-20091213.221414-13。經過合併全部遠程倉庫和本地倉庫的元數據,Maven就能知道全部倉庫中該構件的最新快照。 ###6.7 鏡像###   若是倉庫X能夠提供倉庫Y存儲的全部內容,那麼就能夠認爲X是Y的一個鏡像。換句話說,任何一個能夠從倉庫Y得到的構件,都胡夠從它的鏡像中獲取。舉個例子,http://maven.net.cn/content/groups/public/ 是中央倉庫http://repo1.maven.org/maven2/ 在中國的鏡像,因爲地理位置的因素,該鏡像每每可以提供比中央倉庫更快的務。所以,能夠配置Maven使用該鏡像來替代中央倉庫。編輯settings.xml,代碼以下:工具

<settings>  
  ...  
  <mirrors>  
    <mirror>  
      <id>maven.net.cn</id>  
      <name>one of the central mirrors in china</name>  
      <url>http://maven.net.cn/content/groups/public/</url>  
      <mirrorOf>central</mirrorOf>  
    </mirror>  
  </mirrors>  
  ...  
</settings>

  該例中,<mirrorOf>的值爲central,表示該配置爲中央倉庫的鏡像,任何對於中央倉庫的請求都會轉至該鏡像,用戶也可使用一樣的方法配置其餘倉庫的鏡像。另外三個元素id,name,url與通常倉庫配置無異,表示該鏡像倉庫的惟一標識符、名稱以及地址。相似地,若是該鏡像需認證,也能夠基於該id配置倉庫認證。
  關於鏡像的一個更爲常見的用法是結合私服。因爲私服能夠代理任何外部的公共倉庫(包括中央倉庫),所以,對於組織內部的Maven用戶來講,使用一個私服地址就等於使用了全部須要的外部倉庫,這能夠將配置集中到私服,從而簡化Maven自己的配置。在這種狀況下,任何須要的構件均可以從私服得到,私服就是全部倉庫的鏡像。這時,能夠配置這樣的一個鏡像,如例:佈局

<settings>  
  ...  
  <mirrors>  
    <mirror>  
      <id>internal-repository</id>  
      <name>Internal Repository Manager</name>  
      <url>http://192.168.1.100/maven2</url>  
      <mirrorOf>*</mirrorOf>  
    </mirror>  
  </mirrors>  
  ...  
</settings>

  該例中<mirrorOf>的值爲星號,表示該配置是全部Maven倉庫的鏡像,任何對於遠程倉庫的請求都會被轉至http://192.168.1.100/maven2/。若是該鏡像倉庫須要認證,則配置一個Id爲internal-repository的<server>便可。爲了知足一些複雜的需求,Maven還支持更高級的鏡像配置:

  • <mirrorOf>*</mirrorOf> 匹配全部遠程倉庫。
  • <mirrorOf>external:*</mirrorOf> 匹配全部遠程倉庫,使用localhost的除外,使用file://協議的除外。也就是說,匹配全部不在本機上的遠程倉庫。
  • <mirrorOf>repo1,repo2</mirrorOf> 匹配倉庫repo1和repo2,使用逗號分隔多個遠程倉庫。
  • <mirrorOf>*,!repo1</miiroOf> 匹配全部遠程倉庫,repo1除外,使用感嘆號將倉庫從匹配中排除。   須要注意的是,因爲鏡像倉庫徹底屏蔽了被鏡像倉庫,當鏡像倉庫不穩定或者中止服務的時候,Maven仍將沒法訪問被鏡像倉庫,於是將沒法下載構件。
相關文章
相關標籤/搜索