1. 程式人生 > >《Maven權威指南》讀書筆記

《Maven權威指南》讀書筆記

一、apache  maven  介紹

      1、maven是什麼

絕大部分Maven使用者都稱Maven是一個"構建工具":一個用來把原始碼構建成可釋出的構件的工具。 構建工程師和專案經理會說Maven是一個更復雜的東西:一個專案管理工具。那麼區別是什麼? 像Ant這樣的構建工具僅僅是關注預處理,編譯,打包,測試和分發。像 Maven 這樣的一個專案管理工具提供了構建工具所提供功能的超集。 除了提供構建的功能,Maven還可以生成報告,生成Web站點,並且幫助推動工作團 隊成員間的交流。

一個更正式的 Apache Maven1 的定義: Maven是一個專案管理工具,它包含了一個專案物件模型 (Project Object Model),一組標準集合,一個專案生 命週期(ProjectLifecycle),一個依賴管理系統(Dependency Management System),和用來執行定義在生命週期階段(phase)中外掛(plugin)目標(goal)的邏輯。 當你 使用Maven的時候,你用一個明確定義的專案物件模型來描述你的專案,然後 Maven 可以應用橫切的邏輯,這些邏輯來自一組共享的(或者自定義的 )外掛。

2、約定優於配置 

 Maven通過給專案提供明智的預設行為來融合這個概念。 在沒有自定義的情況下,原始碼假定是在 /content-zh/src/main/java,資原始檔假定是在/contentzh/src/main/resources。測試程式碼假定是在 /content-zh/src/test 。專案假定會產生一個 JAR 檔案。Maven假定你想要把編譯好的位元組碼放到/contentzh/target/classes 並且在 /content-zh/target 建立一個可分發的 JAR 檔案。

Maven 對約定優於配置的應用不僅僅是簡單的目錄位置,Maven 的核心外掛使用了一組通用的約定,以用來編譯原始碼,打包可分發的構件,生成 we b 站點,還有許多其他的過程。

3、基於maven外掛的全域性性重用

Maven 的核心其實不做什麼實際的事情,除了解析一些 XML 文件,管理生命週期與外掛之外,它什麼也不懂。Maven 被設計成將主要的職責委派給一組Maven 外掛,這些外掛可以影響 Maven 生命週期,提供對目標的訪問。絕大多數 Maven 的動作發生於Maven 外掛的目標,如編譯原始碼,打包二進位制程式碼,釋出站點和其它構建任務。 你從Apache 下載的 Maven 不知道如何打包 WAR 檔案,也不知道如何執行單元測試,Maven大部分的智慧是由外掛實現的,而外掛從 Maven 倉庫獲得。事實上,第一次 你用全新的 Maven 安裝執行諸如 mvn install 命令的時候,它會從中央 Maven 倉庫下載大部分核心 Maven 外掛。這不僅僅是一個最小化 Maven 分發包大小的技巧,這種方 式更能讓你升級外掛以給你專案的構建提高能力。Maven 從遠端倉庫獲取依賴和外掛的這一事實允許了構建邏輯的全域性性重用。

Maven Surefire 外掛是負責執行單元測試的外掛。

專案使用 Compiler 外掛進行編譯

4、apache   maven

• Maven 擁有約定,因為你遵循了約定,它已經知道你的原始碼在哪裡。它把位元組碼放到 target/classes ,然後在 target 生成一個 JAR 檔案。
• Maven 是宣告式的。你需要做的只是建立一個 pom.xml 檔案然後將原始碼放到預設的目錄。Maven 會幫你處理其它的事情。
• Maven 有一個生命週期,當你執行 mvn install 的時候被呼叫。這條命令告訴 Maven 執行一系列的有序的步驟,直到到達你指定的生命週期。遍歷生命週期旅途中的一個影響就是,Maven 運行了許多預設的外掛目標,這些目標完成了像編譯和建立一個 JAR 檔案這樣的工作。

Maven 以外掛的形式為一些一般的專案任務提供了內建的智慧。如果你想要編寫執行單元測試,你需要做的只是編寫測試然後放到 /content-zh/src/test/java ,新增一個對於 TestNG 或者 JUnit 的測試範圍依賴,然後執行 mvn test 。如果你想要部署一個web 應用而非 JAR ,你需要做的是改變你的專案型別為 war ,然後把你文件根目錄置為/content-zh/src/main/webapp 。

二、一個簡單的maven專案

1、建立一個簡單的專案

開始一個新的Maven專案,在命令列使用MavenArchetype外掛。

$ mvn archetype:create -DgroupId=org.sonatype.mavenbook.ch03 \
-DartifactId=simple \
-DpackageName=org.sonatype.mavenbook

archetype:create稱為一個Maven目標 (goal)。如果你熟悉Apache Ant,一個Maven目標類似於一個Ant目標 (target);它們都描述了將會在構建
中完成的工作單元 (unit of work)。而像-Dname=value這樣的對是將會被傳到目標中的引數,它們使用-D屬性這樣的形式1,類似於你通過命令列向Java虛擬 機傳遞系統屬性。archetype:create這個目標的目的通過archetype快速建立一個專案。在這裡,一個archetype被定義為“一個原始的模型或者型別,在它之後 其它類似的東西與之匹配;一個原型(prototype)”。Maven有許多可用的archetype,從生成一個簡單的Swing應用,到一個複雜的Web應用。本章我們用最基 本的archetype來建立一個入門專案的骨架。這個外掛的字首是“archetype”,目標為”create”。


¶ Maven Archtype外掛建立了一個與artifactId匹配的目錄——simple。這是專案的基礎目錄。
· 每個專案在檔案pom.xml裡有它的專案物件模型 (POM)。這個檔案描述了這個專案,配置了外掛,聲明瞭依賴。

我們專案的原始碼了資原始檔被放在了src/main目錄下面。在我們簡單Java專案這樣的情況下,這個目錄包含了一下java類和一些配置檔案。在其它的專案中,它可能是web應用的文件根目錄,或者還放一些應用伺服器的配置檔案。在一個Java專案中,Java類放在src/main/java下面,而classpath資原始檔放在src/main/resources下面。

我們專案的測試用例放在src/test下。在這個目錄下面,src/test/java存放像使用JUnit或者TestNG這樣的Java測試類。目錄src/test/resources下存放測試classpath資原始檔。

2、構建專案 mvn install

想要構建打包這個應用,在包含pom.xml的目錄下執行mvn install。

你已經建立了,編譯了,測試了,打包了,並且安裝了(installed)最簡單的Maven專案

最開始的幾個元素——groupId,artifactId, packaging, version——是Maven的座標(coordinates),它們唯一標識了一個專案。name和url是POM提供的描述性元素,它們給人提供了可閱讀的名字,將一個專案關聯到了專案web站點。

3、maven外掛和目標

一個Maven外掛是一個單個或者多個目標的集合。Maven外掛的例子有一些簡單但核心的外掛,像Jar外掛,它包含了一組建立JAR檔案的目標,Compiler外掛,它包含了一組編譯原始碼和測試程式碼的目標,或者Surefire外掛,它包含一組執行單元測試和生成測試報告的目標。

一個目標是一個明確的任務,它可以作為單獨的目標執行,或者作為一個大的構建的一部分和其它目標一起執行。一個目標是Maven中的一個“工作單元(unit ofwork)”。目標的例子包括Compiler外掛中的compile目標,它用來編譯專案中的所有原始檔,或者Surefire外掛中的test目標,用來執行單元測試。目標通過配置屬性進行配置,以用來定製行為。

Maven的核心對你專案構建中特定的任務幾乎毫無所知。就它本身來說,Maven不知道如何編譯你的程式碼,它甚至不知道如何製作一個JAR檔案,它把所有這些任務代理給了Maven外掛,像Compiler外掛和Jar外掛,它們在需要的時候被下載下來並且定時的從Maven中央倉庫更新。當你下載Maven的時候,你得到的是一個包含了基本軀殼的Maven核心,它知道如何解析命令列,管理classpath,解析POM檔案,在需要的時候下載Maven外掛。通過保持Compiler外掛和Maven核心分離,並且提供更新機制,使用者很容易能使用編譯器最新的版本。通過這種方式,Maven外掛提供了通用構建邏輯的全域性重用性,有不會在構建週期中定義編譯任務,有使用了所有Maven使用者共享的Compiler外掛。如果有個對Compiler外掛的改進,每個使用Maven的專案可以立刻從這種變化中得到好處。

4、maven生命週期   

命令列並沒有指定一個外掛目標,而是指定了一個Maven生命週期階段。一個階段是在被Maven稱為“構建生命週期”中的一個步驟。生命週期是包含在一個專案構建中的一系列有序的階段。Maven可以支援許多不同的生命週期,但是最常用的生命週期是預設的Maven生命週期,這個生命週期中一開始的一個階段是驗證專案的基本完整性,最後的一個階段是把一個專案釋出成產品。生命週期的階段被特地留得含糊,單獨的定義為驗證(validation),測試(testing),或者釋出(deployment),而他們對不同專案來說意味著不同的事情。

外掛目標可以附著在生命週期階段上。隨著Maven沿著生命週期的階段移動,它會執行附著在特定階段上的目標。每個階段可能綁定了零個或者多個目標。

在Maven經過它生命週期中package之前的階段的時候,這些目標被運行了;Maven執行一個階段的時候,它首先會有序的執行前面的所有階段,到命令列指定的那個階段為止。

當Maven經過以package為結尾的預設生命週期的時候,下面的目標按順序被執行:
resources:resources
Resources外掛的resources目標繫結到了resources 階段。這個目標複製src/main/resources下的所有資源和其它任何配置的資源目錄,到輸出目錄。
compiler:compile
Compiler外掛的compile目標繫結到了compile 階段。這個目標編譯src/main/java下的所有原始碼和其他任何配置的資源目錄,到輸出目錄。
resources:testResources
Resources外掛的testResources目標繫結到了test-resources 階段。這個目標複製src/test/resources下的所有資源和其它任何的配置的測試資源目錄,到測試輸出目錄。
compiler:testCompile
Compiler外掛的testCompile目標繫結到了test-compile 階段。這個目標編譯src/test/java下的測試用例和其它任何的配置的測試資源目錄,到測試輸出
目錄。
surefire:test
Surefire外掛的test目標繫結到了test 階段。這個目標執行所有的測試並且建立那些捕捉詳細測試結果的輸出檔案。預設情況下,如果有測試失敗,這個目標會終止。

jar:jar
Jar外掛的jar目標繫結到了package 階段。這個目標把輸出目錄打包成JAR檔案。


5、maven座標

這就是專案物件模型(POM),一個專案的宣告性描述。當Maven執行一個目標的時候,每個目標都會訪問定義在專案POM裡的資訊。當jar:jar目標需要建立一個JAR檔案的時候,它通過觀察POM來找出這個Jar檔案的名字。當compiler:compile任務編譯Java原始碼為位元組碼的時候,它通過觀察POM來看是否有編譯目標的引數。目標在POM的上下文中執行。目標是我們希望針對專案執行的動作,而專案是通過POM定義的。

groupId, artifactId, version和packaging。這些組合的就像任何其它的座標系統,一個Maven座標是一個地址,即“空間”裡的某個點:從一般到特殊。當一個專案通過依賴,外掛或者父專案引用和另外一個專案關聯的時候,Maven通過座標來精確定位一個專案。Maven座標通常用冒號來作為分隔符來書寫,像這樣的格式:groupId:artifactId:packaging:version。

6、maven 倉庫

當你第一次執行Maven的時候,你會注意到Maven從一個遠端的Maven倉庫下載了許多檔案。如果這個簡單的專案是你第一次執行Maven,那麼當觸發resources:resource目標的時候,它首先會做的事情是去下載最新版本的Resources外掛。在Maven中,構件和外掛是在它們被需要的時候從遠端的倉庫取來的。初始的Maven下載包的大小相當的小(1.8兆),其中一個原因是事實上這個初始Maven不包括很多外掛。它只包含了幾近赤裸的最少值,而在需要的時候再從遠端倉庫去取。Maven自帶了一個用來下載Maven核心外掛和依賴的遠端倉庫地址(http://repo1.maven.org/maven2)。
你常常會寫這樣一個專案,這個專案依賴於一些既不免費也不公開的包。在這種情況下,你需要要麼在你組織的網路裡安裝一個定製的倉庫,要麼手動的安裝這些依賴。預設的遠端倉庫可以被替換,或者增加一個你組織維護的自定義Maven倉庫的引用。有許多現成的專案允許組織管理和維護公共Maven倉庫的映象。

Maven從遠端倉庫下載構件和外掛到你本機上,儲存在你的本地Maven倉庫裡。一旦Maven已經從遠端倉庫下載了一個構件,它將永遠不需要再下載一次,因為maven會首先在本地倉庫查詢外掛,然後才是其它地方。。Maven倉庫既是一個從遠端倉庫下載的構件的快取,也允許你的專案相互依賴

7、maven  依賴管理

一個複雜的專案將會包含很多依賴,也有可能包含依賴於其它構件的依賴。這是Maven最強大的特徵之一,它支援了傳遞性依賴(transitive dependencies)。假如你的專案依賴於一個庫,而這個庫又依賴於五個或者十個其它的庫(就像Spring或者Hibernate那樣)。你不必找出所有這些依賴然後把它們寫在你的pom.xml裡,你只需要加上你直接依賴的那些庫,Maven會隱式的把這些庫間接依賴的庫也加入到你的專案中。Maven也會處理這些依賴中的衝突,同時能讓你自定義預設行為,或者排除一些特定的傳遞性依賴。

需要注意的是Maven不只是下載JUnit的JAR檔案,它同時為這個JUnit依賴下載了一個POM檔案。Maven同時下載構件和POM檔案的這種行為,對Maven支援傳遞性依賴來說非常重要。
當你把專案的構件安裝到本地倉庫時,你會發現在和JAR檔案同一目錄下,Maven釋出了一個稍微修改過的pom.xml的版本。儲存POM檔案在倉庫裡提供給其它專案了該專案的資訊,其中最重要的就是它有哪些依賴。如果專案B依賴於專案A,那麼它也依賴於專案A的依賴。當Maven通過一組Maven座標來處理依賴構件的時候,它也會獲取POM,通依賴的POM來尋找傳遞性依賴。那些傳遞性依賴就會被新增到當前專案的依賴列表中。

在Maven中一個依賴不僅僅是一個JAR。它是一個POM檔案,這個POM可能也聲明瞭對其它構件的依賴。這些依賴的依賴叫做傳遞性依賴,Maven倉庫不僅僅存貯二進位制檔案,也儲存了這些構建的元資料(metadata),才使傳遞性依賴成為可能。

Maven也提供了不同的依賴範圍(dependency scope)。Simple專案的pom.xml包含了一個依賴——junit:junit:jar:3.8.1——範圍是test。當一個依賴的範圍是test的時候,說明它在Compiler外掛執行compile目標的時候是不可用的。它只有在執行compiler:testCompile和surefire:test目標的時候才會被加入到classpath中。

當為專案建立JAR檔案的時候,它的依賴不會被捆綁在生成的構件中,他們只是用來編譯。

8、多模組專案

一個多模組專案通過一個父POM引用一個或多個子模組來定義

注意simple-parent定義了一組Maven坐
標:groupId是org.sonatype.mavenbook,artifactId是simple-parent,version是1.0。
這個父專案不像之前的專案那樣建立一個JAR或者一個WAR,它僅僅是一個引用其它
Maven專案的POM。像simple-parent這樣僅僅提供專案物件模型的專案,正確的的打包
型別是pom。pom.xml中下一部分列出了專案的子模組。這些模組在modules元素中定義,
每個modules元素對應了一個simple-parent/目錄下的子目錄。Maven知道去這些子目錄
尋找pom.xml檔案,並且,在構建的simp-parent的時候,它會將這些子模組包含到要構
建的專案中。

當Maven執行一個帶有子模組的專案的時候,Maven首先載入父POM,然後定位所有的子
模組POM。Maven然後將所有這些專案的POM放入到一個稱為Maven 反應堆(Reactor)的
東西中,由它負責分析模組之間的依賴關係。這個反應堆處理元件的排序,以確保相互
獨立的模組能以適當的順序被編譯和安裝。
注意
除非需要做更改,反應堆一直維持定義在POM中的模組的順序。為此一個有
幫助的思維模型是,那些依賴於兄弟專案的專案在列表中被“向下按”,直
到依賴順序被滿足。在很少的情況下,重新安排你構建的模組順序可能很方
便——例如你想要一個頻繁的不穩定的模組接近構建的開端。
一旦反應堆解決了專案構建的順序,Maven就會在多模組構建中為每個模組執行特定的
目標。本例中,你能看到Maven在simple-webapp之前構建了simple-weather,為每個子
模組執行了mvn clean install。
注意
當你在命令列執行Maven的時候你經常會在任何其它生命週期階段前指
定clean生命週期階段。當你指定clean,你就確認了在編譯和打包一個應用
之前,Maven會移除舊的輸出。執行clean不是必要的,但這是一個確保你正
執行“乾淨構建”的十分有用的預防措施。