1.前言 我為什麼要學習Maven呢。是因為現在在公司實習,公司需要使用Maven,這樣,只有把Maven好好的學習一下,以便能夠更好地工作了!
Maven,發音是[`meivin],"專家"的意思。它是一個很好的專案管理工具,很早就進入了我的必備工具行列,寫這個學習筆記的目的,一個是為了自己備忘,二則希望能夠為其他人學習使用maven 縮短一些時間。 2. maven概要
首先我把maven的概念快速的梳理一下,讓我們快速地建立起一個比較精確的maven應用情境。 2.1 maven不是什麼
讀書時候要先限定範圍,避免一些有害的遐想。要說maven不是什麼,我們可以從如下幾個要點來展開 maven不是ant,也不是make。 ant和maven有很大的區別:
| |
Maven |
Ant |
| 標準構建檔案 |
project.xml 和 maven.xml |
build.xml |
| 特性處理順序 |
${maven.home}/bin/driver.properties ${project.home}/project.properties ${project.home}/build.properties ${user.home}/build.properties 通過 -D 命令列選項定義的系統特性 最後一個定義起決定作用。 |
通過 -D 命令列選項定義的系統特性 由 任務裝入的特性 第一個定義最先被處理。 |
| 構建規則 |
構建規則更為動態(類似於程式設計語言);它們是基於 Jelly 的可執行 XML。 |
構建規則或多或少是靜態,除非使用<script>任務 |
| 擴充語言 |
外掛程式是用 Jelly(XML)編寫的。 |
外掛程式是用 Java 語言編寫的。 |
| 構建規則可擴充性 |
通過定義 <preGoal> 和 <postGoal> 使構建 goal 可擴充。 |
構建規則不易擴充;可通過使用 <script> 任務類比 <preGoal> 和 <postGoal> 所起的作用。 |
我們以前接觸的構建工具,需要寫一些詳細的步驟,比如: compile project1/src/*.java 等類似的語句。這些語句正是我們使用ant和make所要編寫的東西。maven採用了"約定優於配置"的方法,一些開發常用的操作和步驟已經固化在 maven中,所以使用者不再需要去編寫那些煩人的語句了。同時,maven內建了開發流程的支援,它不僅能夠編譯,同樣能夠打包、發布,也能夠一氣呵成做完這些所有的步驟。 maven不是ivy
依賴管理是maven的功能之一,雖然很多人包括我以前都是只用它的依賴管理功能,但是要深入運用的話,我們就可以看到更多的內容。更重要的是,maven在依賴關係中加入了scope的概念,進一步細化了依賴關係的劃分。
2.2 maven是什麼
maven將自己定位為一個專案管理工具。它負責管理項目開發過程中的幾乎所有的東西: 版本
maven有自己的版本定義和規則 構建
maven支援許多種的應用程式類型,對於每一種支援的應用程式類型都定義好了一組構建規則和工具集。 輸出物管理
maven可以管理項目構建的產物,並將其加入到使用者庫中。這個功能可以用於項目組和其他部門之間的交付行為。 依賴關係
maven對依賴關係的特性進行細緻的分析和劃分,避免開發過程中的依賴混亂和相互汙染行為 文檔和構建結果
maven的site命令支援各種文檔資訊的發布,包括構建過程的各種輸出,javadoc,產品文檔等。 項目關係
一個大型的項目通常有幾個小項目或者模組組成,用maven可以很方便地管理 移植性管理
maven可以針對不同的開發情境,輸出不同種類的輸出結果。 2.3 maven的生命週期
maven把項目的構建劃分為不同的生命週期(lifecycle),在我看來,劃分的已經是非常仔細了,大家可以參考這裡 。
粗略一點的話,它這個過程(phase)包括:編譯、測試、打包、整合測試、驗證、部署。
maven中所有的執行動作(goal)都需要指明自己在這個過程中的執行位置,然後maven執行的時候,就依照過程的發展依次調用這些goal進行各種處理。
這個也是maven的一個基本調度機制。一般來說,位置稍後的過程都會依賴於之前的過程。當然,maven同樣提供了設定檔,可以依照使用者要求,跳過某些階段。 2.4 maven的"約定優於配置"
所謂的"約定優於配置",在maven中並不是完全不可以修改的,他們只是一些配置的預設值而已。但是使用者除非必要,並不需要去修改那些約定內容。maven預設的檔案存放結構如下: /項目目錄 pom.xml 用於maven的設定檔 /src 原始碼目錄 /src/main 工程原始碼目錄 /src/main/java 工程java原始碼目錄 /src/main/resource 工程的資來源目錄 /src/test 單元測試目錄 /src/test/java /target 輸出目錄,所有的輸出物都存放在這個目錄下 /target/classes 編譯之後的class檔案
每一個階段的任務都知道怎麼正確完成自己的工作,比如compile任務就知道從src/main/java下編譯所有的java檔案,並把它的輸出class檔案存放到target/classes中。
對maven來說,採用"約定優於配置"的策略可以減少修改配置的工作量,也可以降低學習成本,更重要的是,給項目引入了統一的規範。 2.5 maven的版本規範
maven使用如下幾個要素來唯一定位某一個輸出物: groupId:artifactId:packaging:version 。比如 org.springframework:spring:2.5 。每個部分的解釋如下: groupId
團體,公司,小組,組織,項目,或者其它團體。團體標識的約定是,它以建立這個項目的組織名稱的逆向網域名稱(reverse domain name)開頭。來自Sonatype的項目有一個以com.sonatype開頭的groupId,而Apache Software的項目有以org.apache開頭的groupId。 artifactId
在groupId下的表示一個單獨項目的唯一識別碼。比如我們的tomcat, commons等。不要在artifactId中包含點號(.)。 version
一個項目的特定版本。發布的項目有一個固定的版本標識來指向該項目的某一個特定的版本。而正在開發中的項目可以用一個特殊的標識,這種標識給版本加上一個"SNAPSHOT"的標記。
雖然項目的打包格式也是Maven座標的重要組成部分,但是它不是項目唯一識別碼的一個部分。一個項目的 groupId:artifactId:version使之成為一個獨一無二的項目;你不能同時有一個擁有同樣的groupId, artifactId和version標識的項目。 packaging
項目的類型,預設是jar,描述了項目打包後的輸出。類型為jar的項目產生一個JAR檔案,類型為war的項目產生一個web應用。 classifier
很少使用的座標,一般都可以忽略classifiers。如果你要發布同樣的代碼,但是由於技術原因需要產生兩個單獨的構件,你就要使用一個分類器(classifier)。例如,如果你想要構建兩個單獨的構件成JAR,一個使用Java 1.4編譯器,另一個使用Java 6編譯器,你就可以使用分類器來產生兩個單獨的JAR構件,它們有同樣的groupId:artifactId:version組合。如果你的項目使用本地擴充類庫,你可以使用分類器為每一個目標平台產生一個構件。分類器常用於打包構件的源碼,JavaDoc或者二進位集合。
maven有自己的版本規範,一般是如下定義 <major version>.<minor version>.<incremental version>-<qualifier> ,比如1.2.3-beta-01。要說明的是,maven自己判斷版本的演算法是major,minor,incremental部分用數字比較,qualifier部分用字串比較,所以要小心 alpha-2和alpha-15的比較關係,最好用 alpha-02的格式。
maven在版本管理時候可以使用幾個特殊的字串 SNAPSHOT ,LATEST ,RELEASE 。比如"1.0-SNAPSHOT"。各個部分的含義和處理邏輯如下說明: SNAPSHOT
如果一個版本包含字串"SNAPSHOT",Maven就會在安裝或發布這個組件的時候將該符號展開為一個日期和時間值,轉換為UTC時間。例如,"1.0-SNAPSHOT"會在2010年5月5日下午2點10分發布時候變成1.0-20100505-141000-1。
這個詞只能用於開發過程中,因為一般來說,項目組都會頻繁發布一些版本,最後實際發布的時候,會在這些snapshot版本中尋找一個穩定的,用於正式發布,比如1.4版本發布之前,就會有一系列的1.4-SNAPSHOT,而實際發布的1.4,也是從中拿出來的一個穩定版。 LATEST
指某個特定構件的最新發行,這個發布可能是一個發布版,也可能是一個snapshot版,具體看哪個時間最後。 RELEASE
指最後一個發布版。 2.6 maven的組成部分
maven把整個maven管理的項目分為幾個部分,一個部分是原始碼,包括原始碼本身、相關的各種資源,一個部分則是單元測試用例,另外一部分則是各種maven的外掛程式。對於這幾個部分,maven可以獨立管理他們,包括各種外部依賴關係。 2.7 maven的依賴管理
依賴管理一般是最吸引人使用maven的功能特性了,這個特性讓開發人員只需要關注代碼的直接依賴,比如我們用了spring,就加入spring依賴說明就可以了,至於spring自己還依賴哪些外部的東西,maven幫我們搞定。
任意一個外部依賴說明包含如下幾個要素:groupId, artifactId, version, scope, type, optional。其中前3個是必須的,各自含義如下: groupId 必須 artifactId 必須 version 必須。
這裡的version可以用區間運算式來表示,比如(2.0,)表示>2.0,[2.0,3.0)表示2.0<=ver<3.0;多個條件之間用逗號分隔,比如[1,3),[5,7]。 scope 範圍限制 type 一般在pom引用依賴時候出現,其他時候不用 optional 是否可選依賴
maven認為,程式對外部的依賴會隨著程式的所處階段和應用情境而變化,所以maven中的依賴關係有範圍(scope)的限制。在maven中,scope包含如下的取值: compile(編譯範圍)
compile是預設的範圍;如果沒有提供一個範圍,那該依賴的範圍就是編譯範圍。編譯範圍依賴在所有的classpath中可用,同時它們也會被打包。 provided(已提供範圍)
provided依賴只有在當JDK或者一個容器已提供該依賴之後才使用。例如,如果你開發了一個web應用,你可能在編譯classpath中需要可用的Servlet API來編譯一個servlet,但是你不會想要在打包好的WAR中包含這個Servlet API;這個Servlet API JAR由你的應用伺服器或者servlet容器提供。已提供範圍的依賴在編譯classpath(不是運行時)可用。它們不是傳遞性的,也不會被打包。 runtime(運行時範圍)
runtime依賴在運行和測試系統的時候需要,但在編譯的時候不需要。比如,你可能在編譯的時候只需要JDBC API JAR,而只有在啟動並執行時候才需要JDBC驅動實現。 test(測試範圍)
test範圍依賴 在一般的 編譯和運行時都不需要,它們只有在測試編譯和測試回合階段可用。 system(系統範圍)
system範圍依賴與provided類似,但是你必須顯式的提供一個對於本地系統中JAR檔案的路徑。這麼做是為了允許基於本機物件編譯,而這些對象是系統類別庫的一部分。這樣的構件應該是一直可用的,Maven也不會在倉庫中去尋找它。如果你將一個依賴範圍設定成系統範圍,你必須同時提供一個systemPath元素 。注意該範圍是不推薦使用的(你應該一直盡量去從公用或定製的Maven倉庫中引用依賴)。
另外,代碼有代碼自己的依賴,各個maven使用的外掛程式也可以有自己的依賴關係。依賴也可以是可選的,比如我們代碼中沒有任何cache依賴,但是hibernate可能要配置cache,所以該cache的依賴就是可選的。 2.8 多專案管理
maven的多專案管理也是非常強大的。一般來說,maven要求同一個工程的所有子項目都放置到同一個目錄下,每一個子目錄代表一個項目,比如 總項目/ pom.xml 總項目的pom設定檔 子項目1/ pom.xml 子項目1的pom檔案 子項目2/ pom.xml 子項目2的pom檔案
按照這種格式存放,就是繼承方式,所有具體子項目的pom.xml都會繼承總項目pom的內容,取值為子項目pom內容優先。
要設定繼承方式,首先要在總項目的pom中加入如下配置 < modules >
< module >simple-weather </ module >
< module >simple-webapp </ module >
</ modules >
其次在每個子項目中加入 < parent >
< groupId >org.sonatype.mavenbook.ch06 </ groupId >
< artifactId >simple-parent </ artifactId >
< version >1.0 </ version >
</ parent >
即可。以上的內容還是比較好理解的 。非常類似於Spring中的設定檔中的繼承吧。
當然,繼承不是唯一的設定檔共用方式,maven還支援引用方式。引用pom的方式更簡單,在依賴中加入一個type為pom的依賴即可。
< project >
< description >This is a project requiring JDBC </ description >
...
< dependencies >
...
< dependency >
< groupId >org.sonatype.mavenbook </ groupId >
< artifactId >persistence-deps </ artifactId >
< version >1.0 </ version >
< type >pom </ type >
</ dependency >
</ dependencies >
</ project >
2.9 屬性
使用者可以在maven中定義一些屬性,然後在其他地方用${xxx}進行引用。比如:
< project >
< modelVersion >4.0.0 </ modelVersion >
...
< properties >
< var1 >value1 </ var1 >
</ properties >
</ project >
maven提供了三個隱式的變數,用來訪問系統內容變數、POM資訊和maven的settings: env
暴露作業系統的環境變數,比如env.PATH project
暴露POM中的內容,用點號(.)的路徑來引用POM元素的值,比如${project.artifactId}。另外,java的系統屬性比如user.dir等,也暴露在這裡。 settings
暴露maven的settings的資訊,也可以用點號(.)來引用。maven把系統設定檔存放在maven的安裝目錄中,把使用者相關的設定檔存放在~/.m2/settings.xml(unix)或者%USERPROFILE%/.m2/settings.xml(windows)中。 2.10 maven的profile
profile是maven的一個重要特性,它可以讓maven能夠自動適應外部的環境變化,比如同一個項目,在linux下編譯linux的版本,在win下編譯win的版本等。一個項目可以設定多個profile,也可以在同一時間設定多個profile被啟用(active)的。自動啟用的 profile的條件可以是各種各樣的設定條件,組合放置在activation節點中,也可以通過命令列直接指定。profile包含的其他配置內容可以覆蓋掉pom定義的相應值。如果認為profile設定比較複雜,可以將所有的profiles內容移動到專門的profiles.xml 檔案中,不過記得和pom.xml放在一起。
activation節點中的啟用條件中常見的有如下幾個: os
判斷作業系統相關的參數,它包含如下可以自由組合的子節點元素 message - 規則失敗之後顯示的訊息 arch - 匹配cpu結構,常見為x86 family - 匹配作業系統家族,常見的取值為:dos,mac,netware,os/2,unix,windows,win9x,os/400等 name - 匹配作業系統的名字 version - 匹配的作業系統版本號碼 display - 檢測到作業系統之後顯示的資訊 jdk
檢查jdk版本,可以用區間表示。 property
檢查屬性值,本節點可以包含name和value兩個子節點。 file
檢查檔案相關內容,包含兩個子節點:exists和missing,用於分別檢查檔案存在和不存在兩種情況。 3. maven的操作和使用
maven的操作有兩種方式,一種是通過mvn命令列命令,一種是使用maven的eclipse外掛程式。因為使用eclipse的maven外掛程式操作起來比較容易,這裡就只介紹使用mvn命令列的操作。 3.1 maven的設定檔
maven的主執行程式為mvn.bat,linux下為mvn.sh,這兩個程式都很簡單,它們的共同用途就是收集一些參數,然後用 java.exe來運行maven的Main函數。maven同樣需要有設定檔,名字叫做settings.xml,它放在兩個地方,一個是maven 安裝目錄的conf目錄下,對所有使用該maven的使用者都起作用,我們稱為主設定檔,另外一個放在 %USERPROFILE%/.m2/settings.xml下,我們成為使用者設定檔,只對目前使用者有效,且可以覆蓋主設定檔的參數內容。還有就是項目層級的配置資訊了,它存放在每一個maven管理的項目目錄下,叫pom.xml,主要用於設定項目相關的一些內容,當然,如果有必要,使用者也可以在 pom中寫一些配置,覆蓋住設定檔和使用者設定檔的設定參數內容。
一般來說,settings檔案配置的是比如repository庫路徑之類的全域資訊,具體可以參考官方網站的文章 。 3.2 建立新工程
要建立一個新的maven工程,我們需要給我們的工程指定幾個必要的要素,就是maven產品座標的幾個要素:groupId, artifactId,如果願意,你也可以指定version和package名稱。我們先看一個簡單的建立命令:
d:\work\temp>mvn archetype:create -DgroupId=com.abc -DartifactId=product1 -DarchetypeArtifactId=maven-archetype-webapp
首先看這裡的命令列參數的傳遞結構,怪異的 -D參數=值 的方式是 java.exe 要求的方式。這個命令建立一個web工程,目錄結構是一個標準的maven結構,如下: D:.
└─mywebapp
│ pom.xml
│
└─src
└─main
├─resources
└─webapp
│ index.jsp
│
└─WEB-INF
web.xml
大家要注意,這裡目錄結構的布局實際上是由參數 archetypeArtifactId 來決定的,因為這裡傳入的是 maven-archetype-webapp 如果我們傳入其他的就會建立不同的結構,預設值為maven-archetype-quickstart ,有興趣的讀者可以參考