Maven簡介

Maven簡介

Maven出來很多年了,我大概是在2008年,透過DSpace開源專案,第一次實際使用maven的,在之前,多半是寫ANT指令碼來構建Java專案,都這麼古典的知識了,為什麼還要介紹一下?主要是為了給公司新同事提綱挈領的快速過一遍,不求看完就能精通,但希望透過本文能對Maven做到心中有數。

一、Maven與其他構建工具的不同

1、ANt工具

早期的專案,我都是寫Ant來構建的,這裡不光是Java專案,實際上,在上家公司,我們的Delphi專案也是依靠Ant來構建系統的,為什麼要用ANt構建專案?

這裡需要灌輸的一個理念:能夠自動化的,一定不要手動做,哪怕就點一下滑鼠,都不能!一定要有工具化的理念,只有這個概念紮根腦海,你才會在做系統功能的同時,考慮提供各種自動化(測試)介面或者工具,才會在做UI的同時,去提供CLI版本的實現,這樣就能倒逼自己將專案模組化,將功能與UI和CLI呈現層面的東西隔離開。

ANt是什麼?ANt你可以認為是一個以xml語言來定義工作的自動化工具,與shell指令碼或者bat指令碼定義批處理自動化工具是類似的。

可以對比兩者

Maven簡介

有了Ant,為什麼還需要Maven?這個疑問在我第一次接觸Maven的時候也有過,總覺得老外為什麼喜歡重新做輪子!但真到你使用Maven之後,你會發現,Maven與Ant有太多的不同,正是這些不同,導致了目前Java體系中,Maven幾乎就是事實標準的構建工具,而且,其影響力遠超Java體系,npm等新語言的構建工具,都能看到Maven設計思想的影子。

Maven自身的定位是個工程(專案)管理工具!從其配置檔案pom。xml(Project Object Model)命名就能明白其定位了。

1、Maven採用COC(約定優於配置)的思想來標準化專案模板,其構建僅針對支援的模板;這與Ant不一樣,Ant太靈活,可以遷就各種專案目錄格式,甚至,Java檔案可以放在不同的Folder中,只要編譯後Copy到一個Classes裡面即可,看起來雖然很強,但很亂,每個專案,每個模組,甚至每個開發小組,都可以定義自己的組織方式和目錄結構,然後丟出一個build。xml(ant的構建指令碼定義檔案),這對於大型專案就是一個災難!Maven從一開始就杜絕這種專案管理方式,採取了約定專案結構的方式,每種專案型別,都有其固定的目錄結構,後續maven的命令,將根據專案不同,針對標準的目錄結構做編譯,測試,打包,釋出等工作。

2、軟體倉庫(中央/私有)

Ant僅僅是個自動化工具,它不負責依賴的管理,我們需要自己將專案依賴的jar包從官網或者其他途徑下載下來,然後放在Classpath(Libs)裡面,這會帶來以下幾個問題:

2。1、版本依賴難於控制,可能出現Libs裡面出現幾個相同的,版本不同的Jar包,你根本不知道應該刪除哪個;

2。2、依賴隨著專案推進,無效依賴Jar越來越多,重構程式碼後,你引入新的依賴,但卻不敢刪除舊的依賴(大型專案尤其如此),這樣會導致Libs裡面的jar越來越多;

2。3、專案程式碼分享必須帶Jars,由於手工管理依賴包,在專案分享(如提交到git或者svn倉庫)的時候,你必須將這些依賴包也一併提交,導致程式碼倉庫耗費儲存容量巨大。

Maven採取倉庫的思想(與AppStore和Android應用商店一樣),所有的依賴都放在倉庫裡面,專案所有的依賴關係由pom。xml統一定義,在需要的時候(如編譯,測試,打包等)再從倉庫拉取。

Maven簡介

將所有的依賴透過定義方式,統一處理,每個專案的構建環境能做到一致,並且將依賴,依賴的依賴統一處理,在程式碼管理方面,再也不需要將依賴jar包複製來複製去了,可以避免及解決上述三個問題。

二、Maven中的配置

maven最重要的配置檔案就兩個

1、settings。xml

settings。xml有兩個地方可以存在,一個是$M2_HOME/conf的settings。xml,這個是全域性的maven配置檔案,一個是$HOME/。m2的settings。xml,是當前使用者的maven配置,是可選的。

在maven執行的時候,如果存在個人配置檔案settings。xml,會override全域性settings設定。

settings。xml裡面比較重要的幾個配置項:

localRepository:配置本地倉庫的地址(本機目錄),預設是$HOME/。m2

mirrors:映象伺服器配置,如果有私服,就需要配置這個選項,其實映象庫在工作方式上,你可以認為是網路上的localRepository,它遇到沒有的依賴包,也會從其配置的中央倉庫或者上級倉庫拉取(兩圖相似)。

Maven簡介

settings。xml每個配置項都有詳細的註釋,並提供了一個配置模板,大家可以自己看看。

在各個IDE(Eclipse,IDEA,NetBeans)中,maven外掛安裝好之後,第一件事情就是配置其settings。xml指向,以便IDE能獲取本地Maven的配置(如果沒有設定,IDE的Maven外掛一般會用預設配置)

Maven簡介

2、pom。xml

第二個重要的配置檔案是pom。xml,這是每個專案的配置檔案。

pom。xml是maven管理專案的關鍵(所有配置均在這裡),最常見的pom。xml如下:

Maven簡介

這個檔案中,我們需要注意的有:

modelVersion:一定是4。0。0,不管你是maven2,3,4,這個模型版本都是4

packaging:打包的目標形態,有war,jar,ejb等等

GAV座標:這是唯一定位依賴的座標體系。

Maven簡介

GAV不光在專案定義中存在,在dependency中一樣存在,它是整個maven依賴管理的核心概念。

groupId:一般就是組織名稱,包名,比如 org。springframework

artifactId:專案名稱,比如 spring-core

version:版本號,這裡支援多種版本定義,比如RELEASE和SNAPSHOT,如果是定義了-SNAPSHOT版本,每次構建的時候,都會從倉庫(非本地)拉取最新的版本,如果是RELEASE版本(或者沒有指定SNAPSHOT),會從本地倉庫拉取,如果要更新,必須更改版本號才會拉取新的RELEASE版本依賴(當然,你可以刪除本地倉庫指定的目錄,以迫使maven再次拉取這個依賴)。

在dependency中,除了GAV,有時候我們還會用到 scope 選項,這個選項指定了依賴存在時期,支援:compile,runtime,provided,test,system。

簡單說明一下:compile就是編譯,測試,執行期間會用到的依賴,會打包到目標檔案中,是預設的scope(可以不寫);runtime是執行期間會用到的依賴,不參與編譯,需要打包到目標檔案;provided是外部會提供的依賴,但編譯的時候會用到,會在編譯期間拉取依賴,但打包不會打包到目標檔案中;test是僅僅是單元測試的時候會用到(如junit。jar等),正式環境及打包都不會加入;system與provided一樣,只是不從倉庫拉取,而直接使用本地的jar。

plugins選項,maven是微服務架構,整個專案很小(解壓後9MB),下載下來的maven僅僅包含必要的核心和cli工具,剩下的所有工具(命令)都是作為外掛下載的,比如你第一次輸入命令

mvn idea:idea

就會看到cli開始downloding一堆相關的jar包了。

Maven簡介

maven內建了很多外掛,那maven是如何將命令對映到pom。xml配置的呢?有興趣可以檢視localRepository目錄中org/apache/maven/plugins 子目錄中所有的外掛明細(maven本身的依賴及工具,也是使用同一套體系建立起來的——自舉能力)具體每個外掛繫結到哪個階段,以及使用的別名,在pom。xml都有定義(這裡給出一個compile外掛的pom點選檢視 ),但細節部分,涉及到maven元件開發,有興趣可以參考https://yq。aliyun。com/articles/488086 。

三、Maven中的生命週期

因為Maven定位是專案管理工具,並將參與專案構建全過程,包括編譯,測試(單元測試),打包,釋出等過程。所以Maven將專案的生命週期做了抽象,目前有3種生命週期定義:clean、default、site。

Maven簡介

clean主要用於清理編譯期生成的臨時檔案;

default是編譯、測試、打包的主要過程;

site是生成專案相關文件,測試報告的過程。

上面的default部分,僅列出主要階段,如果詳細列出的話,是有23個階段的。每個週期都是依據順序,從上至下依次執行的,如果某個階段出現錯誤,整個構建過程就會終止,仔細觀察順序,其實就是我們平時手工構建Java程式碼的順序,先驗證,然後編譯,如果編譯沒有問題,再進行單元測試,如果單元測試都通過了,才進行打包,這中間任何步驟出錯,均需要停止構建工作。

當然,如果需要skip某個過程,可以透過命令或者pom的設定來作特殊處理。

我們常見的命令:

mvn clean package -DskipTests=true

就是按順序使用了clean命令,清理上次編譯期生成的臨時檔案,package命令繫結的是default週期的package階段,會順序執行validate,comiple,test,和package的任務,由於這個命令加入了-DskipTest=true引數,所以構建過程中會跳過test步驟。以上cli命令都對應了一堆內建的核心外掛。對於第三方的外掛,我們可以在pom。xml中加入相應的plugin,然後直接執行,上面舉例的idea:idea就是一個三方的外掛,它將當前maven工程轉為idea專案結構。

下圖是springboot的外掛,可以直接使用springboot:run任務來啟動該springboot專案。

Maven簡介

在idea等主流IDE中都提供的maven外掛,可以直接給出執行goal的選單,雙擊即可執行該maven的task或goal。

Maven簡介

這裡給出一個建議,如果是web專案,建議在plugins中加入jetty(嵌入式容器引擎)外掛,這樣可以快速啟動web專案而不需要將war部署到tomcat或者其他容器,在開發測試階段比較方便。

關於生命週期,可以參考文章http://www。cnblogs。com/davidwang456/p/3915031。html

四、Maven版本處理

最後一部分,簡單說一下依賴的版本處理方式,maven提供的版本依賴處理主要有兩種

1、路徑最短原則

當A包依賴B包,B包依賴C包(C1),同時專案中D包依賴C包的另外一個版本(C2)。

這個時候會採用路徑最短規則。

Maven簡介

如上圖所示,根據最短路徑原則,最後Libs包含的jar包只會有C(2)版本的jar

2、宣告優先原則

當兩條依賴路徑一樣,如何處理呢?

如下圖

Maven簡介

這個取決於你在pom。xml中dependencies中各個依賴的定義順序。

提問:maven如何知道各個依賴的二級依賴的版本?

檢視各個依賴的pom。xml就可以分析出二級依賴,然後再透過二級依賴的GAV再從倉庫下載二級依賴,然後根據二級依賴的pom。xml獲取三級依賴的資訊,如此遞迴,直到最後所有依賴分析完畢,然後maven會處理相應的依賴。所以得出結論,在專案中,maven會把所有的依賴jar和pom。xml都更新到本地倉庫,然後再分析及處理依賴間的版本問題,最後打包的時候,會分揀出必要的jar。

五、其他

1、關於私服

可以使用nexus快速搭建一個maven私服,然後將該伺服器地址配置到mirrors裡面即可;

可以參考https://yq。aliyun。com/articles/7427我們公司的私有倉庫也是採用nexus部署的。

2、國內的映象庫

推薦阿里雲

http://maven。aliyun。com/mvn/view

nexus-aliyun * Nexus aliyun http://maven。aliyun。com/nexus/content/groups/public

3、如何將已有的歷史專案移植為maven專案

如果之前採用的Ant或者其他構建方式,建議先梳理好Libs中的依賴關係,並從https://mvnrepository。com/中分別找到他們,這裡僅關心一級依賴,將二級依賴的jar先去除,讓maven自己從依賴自身的依賴定義來管理二級,三級。。。的依賴包。

如果發現私有jar或者倉庫中沒有的jar,可以使用

mvn install:install-file

命令來將本地的jar按照maven的方式(GAV)安裝到本地倉庫,然後就可以在pom。xml中使用了。

install-file與標準提交到倉庫的依賴有什麼不同呢?雖然都是jar,但由於install-file提交的依賴,其pom。xml只有自身(jar)的GAV資訊,其dependencies是空的,所以,它的依賴(二級。。。依賴)你還得自己處理,這就是私有包與maven管理包的最大區別。

下圖是我們內部系統的一個私有jar包安裝到本地倉庫的指令碼

Maven簡介

4、Gradle號稱Maven終結者,它們的區別在哪裡?

Gradle採用groovy語言(基於JVM的指令碼語言)作為配置領域語言(採用groovy語言特性,使其看起來與配置檔案無異),非常簡潔高效。由於配置檔案採用指令碼語言編寫,所以,除了能夠覆蓋到maven定義的要素,還能做原來Ant和Shell指令碼做的事情,無疑,其擴充套件能力從簡單程度上就大大優於maven了(maven需要開發外掛)。

但在其他方面,特別是思想上,兩者是相似的。各位掌握maven了,再使用Gradle一定會得心應手,如魚得水。