關於maven的一些問題

關於maven

Maven 是 Apache 組織下的一個跨平臺的項目管理工具,主要服務於基於Java平臺的項目構建,項目管理和項目信息管理,提供了幫助管理構建、文檔、報告、依賴、scms、發佈、分發的方法。可以方便的編譯代碼、進行依賴管理、管理二進制庫等等。Maven 提供了標準的軟件生命週期模型和構建模型,通過配置就能對項目進行全面的管理。它的跨平臺性保證了在不同的操作系統上可以使用相同的命令來完成相應的任務。Maven 將構建的過程抽象成一個個的生命週期過程,在不同的階段使用不同的已實現插件來完成相應的實際工作,這種設計方法極大的避免了設計和腳本編碼的重複,極大的實現了複用。

maven的靈魂POM

pom全稱爲Project Object Model, 簡單說就是要對構建的項目進行建模,將要構建的項目看成是一個對象Object,這個對象就會有相應的屬性,屬性在maven中用座標表示,座標元素包括groupId、artifactId、version、packaging、classfier。

1、座標

元素 描述 ext
groupId 定義當前模塊隸屬的實際Maven項目, 表示方式與Java包類似 groupId不應直接對應項目隸屬的公司/組織(一個公司/組織下可能會有很多的項目)
artifactId 定義實際項目中的一個Maven模塊 推薦使用項目名作爲artifactId前綴, 因爲Maven打包默認以artifactId作爲前綴
version 定義當前項目所處版本(如1.0-SNAPSHOT、4.2.7.RELEASE、1.2.15、14.0.1-h-3 等) Maven版本號定義約定: <主版本>.<次版本>.<增量版本>-<里程碑版本>
packaging 定義Maven項目打包方式, 通常打包方式與所生成構件擴展名對應 有jar(默認)、war、pom、maven-plugin等
classifier 用來幫助定義構建輸出的一些附屬構件(如javadoc、sources) 不能直接定義項目的classifier(因爲附屬構件不是由項目默認生成, 須有附加插件的幫助)

2、依賴

Maven最著名的(也是幾乎每個人最先接觸到的)就是Maven的依賴管理, 它使得我們不必再到開源項目的官網一個個下載開源依賴包, 然後再一個個放入classpath. 一個依賴聲明可以包含如下元素:

元素 描述 ext
groupId、artifactId和version 依賴的基本座標(最最重要)
type 依賴的類型, 對應於項目座標定義的packaging 默認jar
scope 依賴的範圍, 用來控制依賴與三種classpath(編譯classpath、測試classpath、運行classpath)的關係 包含compile、provided、runtime、test、system和import 6種
optional 依賴是否可選 假如一個Jar包支持MySQL與Oracle兩種DB, 因此其構建時必須添加兩類驅動, 但用戶使用時只會選擇一種DB. 此時對A、B就可使用optional可選依賴
exclusions 排除傳遞性依賴 傳遞性依賴極大的簡化了項目依賴的管理, 但也會引入Jar包版本衝突等問題, 此時就需要對傳遞性依賴進行排除

重點注意

1、依賴關係
  • 傳遞依賴:如果我們的項目引用了一個Jar包,而該Jar包又引用了其他Jar包,那麼在默認情況下項目編譯時,Maven會把直接引用和簡潔引用的Jar包都下載到本地。
  • 排除依賴:如果我們只想下載直接引用的Jar包,那麼需要在pom.xml中做如下配置:(將需要排除的Jar包的座標寫到下面)
 <exclusions>
      <exclusion>
             <groupId>io.lettuce</groupId>
             <artifactId>lettuce-core</artifactId>
      </exclusion>
 </exclusions>

若項目中多個Jar同時引用了相同的Jar時,會產生依賴衝突,但Maven採用了兩種避免衝突的策略,因此在Maven中是不存在依賴衝突的。

  • 短路優先
    若本項目引用了A.jar,A.jar又引用了B.jar,B.jar又引用了X.jar,並且C.jar也引用了X.jar。在此時,Maven只會引用引用路徑最短的Jar。
  • 聲明優先:若引用路徑長度相同時,在pom.xml中誰先被聲明,就使用誰。
2、依賴範圍

其中依賴範圍scope用來控制依賴和編譯,測試,運行的classpath的關係,主要的是四種依賴範圍如下:

  • compile:默認編譯依賴範圍。對於編譯,測試,運行三種classpath都有效
  • test:測試依賴範圍。只對測試的classpath有效
  • provided:已提供的依賴範圍。對於編譯,測試的classpath都有效,但對於運行無效。因爲已經由容器提供。例如:servlet-api
  • runtime:運行時提供。例如:jdbc驅動
3、依賴包Snapshot版本與Release版本

Snapshot版本代表不穩定、尚處於開發中的版本 ;
Release版本則代表穩定的版本 。

1、什麼情況下該用SNAPSHOT?
協同開發時,如果A依賴構件B,由於B會更新,B應該使用SNAPSHOT來標識自己

  • 如果B不用SNAPSHOT,而是每次更新後都使用一個穩定的版本,那版本號就會升得太快,每天一升甚至每個小時一升,這就是對版本號的濫用。
  • 如果B不用SNAPSHOT, 但一直使用一個單一的Release版本號,那當B更新後,A可能並不會接收到更新。因爲A所使用的repository一般不會頻繁更新release版本的緩存(即本地repository),所以B以不換版本號的方式更新後,A在拿B時發現本地已有這個版本,就不會去遠程Repository下載最新的B。

2、不用Release版本,在所有地方都用SNAPSHOT版本行不行?
不行,正式環境中不得使用snapshot版本的庫。 比如說,今天你依賴某個snapshot版本的第三方庫成功構建了自己的應用,明天再構建時可能就會失敗,因爲今晚第三方可能已經更新了它的snapshot庫。你再次構建時,Maven會去遠程repository下載snapshot的最新版本,你構建時用的庫就是新的jar文件了,這時正確性就很難保證了。穩定版應該依賴Release版本的jar包,這樣更新時需要更新版本號,版本號沒變時,依賴的jar包也不會變。

構建

項目的構建過程對應的是PO對象的build屬性,對應pom.xml中也就是元素中的內容。
在maven中一個構建過程就對應一個Lifecycle,即生命週期。這個Lifecycle也分爲多個階段,每個階段叫做phase。一個標準的構建Lifecycle包含了如下的phase:

  • validate: 用於驗證項目的有效性和其項目所需要的內容是否具備
  • initialize:初始化操作,比如創建一些構建所需要的目錄等
  • generate-sources:用於生成一些源代碼,這些源代碼在compile phase中需要使用到
  • process-sources:對源代碼進行一些操作,例如過濾一些源代碼
  • generate-resources:生成資源文件(這些文件將被包含在最後的輸入文件中)
  • process-resources:對資源文件進行處理
  • compile:對源代碼進行編譯
  • process-classes:對編譯生成的文件進行處理
  • generate-test-sources:生成測試用的源代碼
  • process-test-sources:對生成的測試源代碼進行處理
  • generate-test-resources:生成測試用的資源文件
  • process-test-resources:對測試用的資源文件進行處理
  • test-compile:對測試用的源代碼進行編譯
  • process-test-classes:對測試源代碼編譯後的文件進行處理
  • test:進行單元測試
  • prepare-package:打包前置操作
  • package:打包
  • pre-integration-test:集成測試前置操作
  • integration-test:集成測試
  • post-integration-test:集成測試後置操作
  • install:將打包產物安裝到本地maven倉庫
  • deploy:將打包產物安裝到遠程倉庫

在maven中,你執行任何一個phase時,maven會將其之前的phase都執行。例如 mvn install,那麼maven會將deploy之外的所有phase按照他們出現的順序一次執行。
上面Lifecycle的定義,也就是說maven爲程序的構建定義了一套規範流程:第一步需要validate,第二步需要initialize… … compile,test,package,… … install,deploy,但是並沒有定義每一個phase具體應該如何操作。這裏的phase的作用有點類似於Java語言中的接口,只協商了一個契約,但並沒有定義具體的動作。比如說compile這個phase定義了在構建流程中需要經過編譯這個階段,但沒有定義應該怎麼編譯(編譯的輸入是什麼?用什麼編譯javac/gcc?)。這裏具體的動作就是由goal來定義,一個goal在maven中就是一個Mojo(Maven old java object)。Mojo抽象類中定義了一個execute()方法,一個goal的具體動作就是在execute()方法中實現。實現的Mojo類應該放在哪裏呢?答案是maven plugin裏,所謂的plugin其實也就是一個maven項目,只不過這個項目會引用maven的一些API,plugin項目也具備maven座標。

     <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <executions>
                    <execution>
                        <id>copy-dependencies</id>
                        <phase>prepare-package</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${project.basedir}/libs</outputDirectory>
                            <overWriteReleases>false</overWriteReleases>
                            <overWriteSnapshots>false</overWriteSnapshots>
                            <overWriteIfNewer>true</overWriteIfNewer>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
       </plugins>
    </build>

在執行具體的構建時,我們需要爲lifecycle的每個phase都綁定一個goal,這樣才能夠在每個步驟執行一些具體的動作。比如在lifecycle中有個compile phase規定了構建的流程需要經過編譯這個步驟,而maven-compile-plugin這個plugin有個compile goal就是用javac來將源文件編譯爲class文件的,我們需要做的就是將compile這個phase和maven-compile-plugin中的compile這個goal進行綁定,這樣就可以實現Java源代碼的編譯了。那麼有人就會問,在哪裏綁定呢?答案是在pom.xml元素中配置即可。
就將maven-dependency-plugin中的copy-dependencies這個goal綁定到了prepare-package這個phase,後續在maven執行到prepare-package phase時就會執行copy-dependencies goal。

倉庫

Maven倉庫可簡單分成兩類: 本地倉庫與遠程倉庫. 當Maven根據座標尋找構件時, 它會首先檢索本地倉庫, 如果本地存在則直接使用, 否則去遠程倉庫下載.

  • 本地倉庫: 默認地址爲~/.m2/, 一個構件只有在本地倉庫存在之後, 才能由Maven項目使用.
  • 遠程倉庫: 遠程倉庫又可簡單分成兩類: 中央倉庫和私服. 由於原始的本地倉庫是空的, Maven必須至少知道一個遠程倉庫才能在執行命令時下載需要的構件, 中央倉庫就是這樣一個默認的遠程倉庫.
私服

私服是一種特殊的遠程倉庫, 它設在局域網內, 通過代理廣域網上的遠程倉庫, 供局域網內的Maven用戶使用.
當需要下載構件時, Maven客戶端先向私服請求, 如果私服不存在該構件, 則從外部的遠程倉庫下載, 並緩存在私服上, 再爲客戶提供下載服務. 此外, 一些無法從外部倉庫下載到的構建也能從本地上傳到私服供大家使用(如公司內部二方包、Oracle的JDBC啓動等). 爲了節省帶寬和時間, 一般在公司內部都會架設一臺Maven私服, 但將公司內部項目部署到私服還需要對POM做如下配置:

<project >
    ...
    <distributionManagement>
        <repository>
            <id>releases</id>
            <url>http://mvn.server.com/nexus/content/repositories/releases/</url>
        </repository>
        <snapshotRepository>
            <id>snapshots</id>
            <url>http://mvn.server.com/nexus/content/repositories/snapshots/</url>
        </snapshotRepository>
    </distributionManagement>

</project>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章