【構建工具】《Maven實戰》讀書筆記
- 2019 年 11 月 2 日
- 筆記
目錄
Maven是我們在做Java開發過程中用經常用到的一個輔助工具。本篇部落格是我學習Maven的一個記錄部落格,學習過程主要參考《Maven實戰》這本書。同時也參考了Maven的官方文檔。
1. Maven簡介
1.1 什麼是Maven
Maven是一個開源的Java項目構建、依賴管理和項目資訊管理工具。
1.2 何為構建
所謂構建是指項目編譯、運行單元測試、生成項目文檔、打包和項目部署等一系列操作。
1.3 Maven的優點
Maven是一個優秀的構建工具:幫我們規範了項目的構建過程,我們不在需要像以前那樣自己編寫不通用的項目構建腳本,大大降低自寫腳本出錯的效率,同時也規範了團隊項目的標準化。只需執行一些簡單的Maven命令就可以實現項目構建工作。另外,Maven還提供了很多現成插件來完成各種構建任務,不需要我們自己去寫腳本。
Maven抽象了一個完整的項目構建的生命周期模型,這個模型吸取了其他構建工具的優點,總結了大量項目的實際需求。如果遵循這個模型,可以避免很多不必要的錯誤。Maven已經提供了大量成熟的Maven插件來完成它抽象的這套生命周期模型,如果我們的項目有特殊的構建需求,可以通過實現自己的構建插件來完成項目特殊的構建需求。
統一的依賴管理(中央倉庫)。
Maven還可以管理如下資訊:項目描述、開發者列表、版本控制系統地址、許可證和缺陷管理系統地址等。
1.3 同類技術對比
在Maven之前,Make和Ant也是兩個比較流行的項目構建工具。但是使用這兩個工具,針對每個項目我們必須自己編寫項目構建腳本,而且這個兩個工具都沒有依賴管理的功能(Ant依賴於Lvy進行依賴管理),所以這兩個工具逐漸成為過去式,Maven成為了Java世界中項目構建的標準。(Gradle也是一款不錯的項目構建工具)
2. Maven安裝配置
2.1 windows環境安裝
step1:安裝JDK,配置JAVA_HOME,添加到path; step2:下載Maven安裝包,解壓到指定目錄; step3:將Maven的安裝目錄添加到path; step3:使用mvn -v校驗是否安裝成功。
配置M2_HOME 和 MAVEN_HOME兩個環境變數(指代Maven的安裝目錄,到bin那層目錄)
通常需要設置MAVEN_OPTS的值為-Xms128m -Xmx512m,因為Java默認的最大可用記憶體往往不能夠滿足Maven運行的需要,比如在項目較大時,使用Maven生成項目站點需要佔用大量的記憶體,如果沒有該配置,則很容易得到java.lang.OutOfMemeoryError異常。因此,一開始就配置該環境變數是推薦的做法。
2.2 Linux環境安裝
安裝方式和上述類似
2.3 .m2目錄
安裝完Maven後,一般會在用戶的主目錄下有一個.m2目錄。這個下面會有一個倉庫目錄,默認情況下,快取下來的Jar包會存放在這個目錄下。我們可以在這個目錄下添加settings.xml文件配置倉庫目錄。注意這個settings文件只對當前用戶生效。M2_HOME/config下的settings文件對所有用戶生效。推薦使用用戶範圍的settings。
2.4 配置Http代理
有時候公司為了安全起見,內部網路不能直接訪問外部互聯網。但是提供了代理伺服器進行外部網路訪問,這是想要讓Maven訪問到外部的Maven中心倉庫需要配置代理。
<settings> ... <proxies> <proxy> <id>optional</id> <active>true</active> <protocol>http</protocol> <host>10.47.22.247</host> <port>80</port> <username>xx</username> <password>xx</pawwword> <nonProxyHosts>some.host.com|*.google.com</nonProxyHosts> </proxy> </proxies> ... </settings>
proxies下面可以配置多個代理,默認情況下第一個被激活的代理生效。如果代理伺服器需要認證,那我們還需要提供用戶名密碼。標籤用來指定訪問哪個地址時不需要經過這個代理。
2.5 Maven安裝最佳實踐
2.5.1 配置MAVEN_OPTS
通常需要設置MAVEN_OPTS的值為-Xms128m -Xmx512m,因為Java默認的最大可用記憶體往往不能夠滿足Maven運行的需要,比如在項目較大時,使用Maven生成項目站點需要佔用大量的記憶體,如果沒有該配置,則很容易得到java.lang.OutOfMemeoryError異常。因此,一開始就配置該環境變數是推薦的做法。
2.5.2 配置用戶範圍的settings.xml
我們可以配置M2_HOME/conf/settings.xml,也可以配置~/.m2/settings.xml。前者會對所有用戶生效,後者只會對當前用戶生效。推薦配置用戶範圍內的setting,這樣不會影響其他用戶的配置,在升級Maven時也不需要重新配置。當然,如果你需要做全局配置,就需要配置做全局配置。
3. Maven坐標和依賴
3.1 Maven坐標
在Maven中,坐標可以理解為一個依賴的唯一標識。Maven的坐標元素包括groupId、artifactId、version、packaging、classfier。只要我們提供正確的坐標元素,Maven就能找到對應的構件,首先去你的本地倉庫查找,沒有的話再去遠程倉庫下載。如果沒有配置遠程倉庫,會默認從中央倉庫地址(http://repo1.maven.org/maven2)下載構件,該中央倉庫包含了世界上大部分流行的開源項目構件,但不一定所有構件都有。下面介紹下坐標中每個標籤的含義:
- groupId:定義當前Maven項目的隸屬的實際項目。比如xx公司xx團隊的xx項目,xx項目下又包含幾個模組項目。這時groupId我們就可以命名為com.xxCompany.xxTeam.xxProject,子項目的名字可以通過artifactId來指定;
- artifactId:定義實際項目中的一個Maven模組(子項目)的名字。一般推薦用項目的名稱作為前綴。比如我們整個大項目的名稱是xxProject,這個子模組的名稱是core。那artifactId可以設置為xxProject-core。
- version:項目的版本
- packaging:項目打包方式(jar、war、pom等)默認的是jar。
- classifier:用來定義javadoc和源程式碼source等構件,這個元素不能直接定義。
郵件測試時我們可以考慮使用GreenMail這個類庫
3.2 dependeny標籤的內容
dependeny標籤下面可以有下面的標籤:
- groupId、artifactId和version這幾個標籤來定位具體的依賴;
- type這個標籤對用maven坐標中的packaging屬性,一般不需要指定,默認是jar;
- scope:見3.3節詳解;
- optional:值是true或false,可選依賴不會被傳遞;
- exclusions:排除傳遞依賴。
3.3 scope屬性詳解
Maven環境下有三種classpath,分別是編譯classpath、測試classpa和運行classpath。scope屬性和這三種classpath有關。scope的值可以是下面幾種:
- compile:如果將一個依賴的scope屬相設置為compile(默認就是compile),那麼這個依賴對於三種classpath都有效;
- test:編譯測試classpath有效,這種Jar包不會被打包進最終的項目;
- provide:對於編譯和測試有效,運行時無效。說明在項目運行時這個依賴會被提供,就不需要我們打進項目了。典型的就是Servlet-api(這個依賴在運行時會由web容器提供)
- runtime:典型的應用就是JDBC的驅動實現(就是這個依賴的Jar只在項目運行是才會用到)
- system:依賴的Jar包在本地(不建議使用這個屬性)
<dependency> <groupId>org.xx.xx</groupId> <artifactId>xx-web</artifactId> <scope>system</scope> <systemPath>{java.home}/lib/xx.jar</systemPath> </dependency>
- import:該屬性只會在dependenceManagement屬性中生效。用來導入類型為pom的依賴,典型的應用看Springboot。
| Scope | 編譯classpath | 測試classpath | 運行時classpath | 列子 |
|---|---|---|---|---|
| compile | Y | Y | Y | Spring-core |
| test | – | Y | – | junit |
| provide | Y | Y | – | servlet-api |
| runtime | – | Y | Y | jdbc驅動實現 |
| system | Y | Y | – |
3.4 傳遞依賴
Maven的依賴是具有傳遞性的,比如A->B,B->C,那麼A間接的依賴於C,這就是依賴的傳遞性,其中A對於B是第一直接依賴,B對於C是第二直接依賴,C為A的傳遞性依賴。這裡還有一個依賴scope的問題。如果A->B的scope是compile,B->C的scope也是compile,那麼A->C的scope也是compile。
表格:傳遞依賴
| |compile|test|provided|runtime|
|—–|:—–:|—-|——:|——-|
|compile|compile| – | – | runtime |
|test|test| – | – | test |
|provided|provided| – | provided | provided |
|runtime|runtime| – | – | runtime |
上表中最左邊一列表示A->B的第一直接依賴,最上面一行表示B->C的第二直接依賴,表格中的內容表示A->C的依賴內容。舉個列子,當A->B的直接依賴範圍是test,B->C的第二直接依賴是compile,那麼A->C的依賴範圍是test。
3.5 依賴調解
下面我們來思考這樣一個問題,如果A->B->C->X(1.0),A->D-X(2.0),即A間接依賴X,我們可以看到有兩條路徑都依賴X,那麼maven將會選擇哪個版本的X?maven當中有一套自己的規則,我們來說明一下,maven傳遞性依賴的一些規則以及如何排除依賴衝突。
Maven裡面對於傳遞性依賴有以下幾個規則:
-
最短路徑原則:如果A對於依賴路徑中有兩個相同的jar包,那麼選擇路徑短的那個包,路徑最近者優先,上述會選X(2.0)。
-
第一聲明優先原則:如果A對於依賴路徑中有兩個相同的jar包,路徑長度也相同,那麼依賴寫在前面的優先。例如:A->B->F(1.0),A->C->F(2.0),會選F(1.0)。
-
可選依賴不會被傳遞,如A->B,B->C,B->D,A對B直接依賴,B對C和D是可選依賴,那麼在A中不會引入C和D。可選依賴通過optional元素配置,true表示可選。如果要在A項目中使用C或者D則需要顯式地聲明C或者D依賴。
3.6 依賴排除
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>3.2.8</version> <exclusions> <exclusion> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> </exclusion> </exclusions> </dependency
4. Maven倉庫
Maven的倉庫分為本地倉庫和遠程倉庫。Maven在尋找依賴的時候會先從本地倉庫尋找,本地沒有的話會從遠程倉庫去下載到本地然後再使用。
4.1 倉庫的分類
4.1.1 本地倉庫
本地倉庫需要我們在settings.xml配置文件中配置。注意,默認情況下.m2目錄下是沒有這文件的,需要我們自己配置。所有的依賴都會被快取到本地倉庫,本地沒有的話回去遠程倉庫拿。
<localRepository>D:softwaremavenRepository</localRepository>
4.1.2 遠程倉庫
遠程倉庫有好幾種分類:Maven的中央倉庫、Nexus搭建的私有伺服器(如果Nexus上沒有相應的依賴,Nexus會自動從外部的倉庫下載)和JBoss、Google的中央倉庫等。一般我們只配置一個遠程倉庫。如果中央倉庫中沒有相應的依賴,就會報錯。下面是Maven配置的默認的遠程倉庫,這個倉庫中包含了絕大多數的依賴構建,如果我們沒做其他配置,Maven就會從這個倉庫中下載依賴。
<repositories> <repository> <id>central</id> <name>Central Repository</name> <url>https://repo.maven.apache.org/maven2</url> <layout>default</layout> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories>
Maven還允許用戶配置私有伺服器。如果我們將遠程倉庫配置成我們自己搭建的私有伺服器,那麼Maven每次都會從私有伺服器上去請求依賴,如果私服上不存在我們需要的依賴,私服會先從外部倉庫下載依賴然後快取到伺服器上再提供給我們。另外私服也允許我們自己上傳依賴。架設Maven私服有如下優點:
- 節省公司外網貸款;
- 因為依賴會被快取到私服上,所以依賴的下載速度會很快;
- 方便上傳團隊內部的依賴,統一管理,共享。
4.2 遠程倉庫的配置
Maven已經默認為我們配置了一個遠程倉庫,在maven安裝目錄下的:/lib/maven-model-builder-${version}.jar中我們可以找到這個配置。
<repositories> <repository> <id>central</id> <name>Central Repository</name> <url>https://repo.maven.apache.org/maven2</url> <layout>default</layout> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories>
這裡我們只要知道,中央倉庫的id為central,遠程url地址為http://repo.maven.apache.org/maven2,它關閉了snapshot版本構件下載的支援。
當然我們也可以自己在pom中配置repository,有可能你依賴的一個jar在central中找不到,它只存在於某個特定的公共倉庫,這樣你也不得不添加那個遠程倉庫的配置。
<project> ... <repositories> <repository> <id>maven-net-cn</id> <name>Maven China Mirror</name> <url>http://maven.net.cn/content/groups/public/</url> <releases> <enabled>true</enabled> </releases> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories> <pluginRepositories> <pluginRepository> <id>maven-net-cn</id> <name>Maven China Mirror</name> <url>http://maven.net.cn/content/groups/public/</url> <releases> <enabled>true</enabled> </releases> <snapshots> <enabled>false</enabled> </snapshots> </pluginRepository> </pluginRepositories> ... </project>
我們先看一下的配置,你可以在它下面添加多個 ,每個都有它唯一的ID,一個描述性的name,以及最重要的,遠程倉庫的url。此外,true告訴Maven可以從這個倉庫下載releases版本的構件,而false告訴Maven不要從這個倉庫下載snapshot版本的構件。禁止從公共倉庫下載snapshot構件是推薦的做法,因為這些構件不穩定,且不受你控制,你應該避免使用。當然,如果你想使用區域網內組織內部的倉庫,你可以激活snapshot的支援。
這邊整理下Maven查詢倉庫的順序:
- 先查詢本地倉庫,如果本地倉庫中沒有,進入第二步;
- 查詢Maven給central倉庫配置鏡像,如果配置了鏡像就去鏡像查,如果沒有,就去中央倉庫查;
- 查詢Maven有沒給你自己配置的倉庫指定鏡像,如果配置了鏡像就去鏡像查,否則就從你自己配置的倉庫查。
4.2.1 遠程倉庫的認證
大部分公共的遠程倉庫無須認證就可以直接訪問,但我們在平時的開發中往往會架設自己的Maven遠程倉庫,出於安全方面的考慮,我們需要提供認證資訊才能訪問這樣的遠程倉庫。配置認證資訊和配置遠程倉庫不同,遠程倉庫可以直接在pom.xml中配置,但是認證資訊必須配置在settings.xml文件中。這是因為pom往往是被提交到程式碼倉庫中供所有成員訪問的,而settings.xml一般只存在於本機。因此,在settings.xml中配置認證資訊更為安全。
<servers> <server> <id>releases</id> <username>pub_mvn_deploy</username> <password>mvn.Deploy</password> </server> </servers>
上面程式碼我們配置了一個id為releases的遠程倉庫認證資訊。Maven使用settings.xml文件中的servers元素及其子元素server配置倉庫認證資訊。認證用戶名為admin,認證密碼為admin123。這裡的關鍵是id元素,settings.xml中server元素的id必須與pom.xml中需要認證的repository元素的id完全一致。正是這個id將認證資訊與倉庫配置聯繫在了一起。
4.2.2 部署構建到遠程倉庫
<distributionManagement> <repository> <id>releases</id> <name>public</name> <url>http://59.50.95.66:8081/nexus/content/repositories/releases</url> </repository> <snapshotRepository> <id>snapshots</id> <name>Snapshots</name> <url>http://59.50.95.66:8081/nexus/content/repositories/snapshots</url> </snapshotRepository> </distributionManagement>
distributionManagement包含repository和snapshotRepository子元素,前者表示發布版本(穩定版本)構件的倉庫,後者表示快照版本(開發測試版本)的倉庫。這兩個元素都需要配置id、name和url,id為遠程倉庫的唯一標識,name是為了方便人閱讀,關鍵的url表示該倉庫的地址。往遠程倉庫部署構件的時候,往往需要認證,配置認證的方式同上。
配置正確後,運行命令mvn clean deploy,Maven就會將項目構建輸出的構件部署到配置對應的遠程倉庫,如果項目當前的版本是快照版本,則部署到快照版本的倉庫地址,否則就部署到發布版本的倉庫地址。
4.3 快照版本
在Maven世界中版本分為發布版本和快照版本。1.0.0、1.3-alpha-4和2.0這種版本是穩定的發布版本。2.1-SNAPSHOT或者2.1–20091214-13這種版本是快照版本。
如果我們依賴一個版本為快照的依賴,這種情況下我們我們每次build的時候都會從遠程倉庫拉取這個依賴的最新版本。(遠程倉庫會給這個依賴維護一個時間戳,所以Maven能知道最新的版本)
release(最新發布版本)、latest(最新版本)、snapshot這三種版本都是上面的策略。在我們平時使用時不建議使用release和latest版本,因為這些版本更新比較頻繁,你的項目依賴了這些版本的Jar包,今天還能編譯通過,明天可能就編譯不過了。推薦使用穩定發布版本。
<repository> <releases> <enabled></enabled> </releases> <snapshots> <enabled></enabled> </snapshots> </repository>
4.4 從倉庫解析依賴的機制
。。。待完成
4.5 鏡像
如果倉庫X可以提供倉庫Y存儲的所有內容,那麼就可以認為X是Y的一個鏡像。關於鏡像的一個更為常見的用法是結合私服。由於私服可以代理任何外部的公共倉庫(包括中央倉庫),因此,對於組織內部的Maven用戶來說,使用一個私服地址就等於使用了所有需要的外部倉庫,這可以將配置集中到私服,從而簡化Maven本身的配置。在這種情況下,任何需要的構件都可以從私服獲得,私服就是所有倉庫的鏡像。這時,可以配置這樣的一個鏡像:
<mirrors> <mirror> <!--This sends everything else to /public --> <id>nexus</id> <mirrorOf>*</mirrorOf> <url>http://maven.dev.xxxx.com/nexus/content/groups/public</url> </mirror> </mirrors>
mirrorOf標籤可以有多種配置方式:
<!-- 匹配所有倉庫 --> <mirrorOf>*</mirrorOf> <!-- 匹配倉庫rep1和rep2 --> <mirrorOf>rep1,rep2</mirrorOf> <!-- 匹配rep1之外的所有 --> <mirrorOf>*,!rep1</mirrorOf> <!-- 匹配不在本機上的遠程倉庫 --> <mirrorOf>external:*</mirrorOf>
5. Maven生命周期和插件
在Maven中,我們需要掌握的重要概念有坐標、依賴、倉庫、生命周期、插件。這個章節我們講解生命周期和插件的主題。
Maven通過對大量項目構建過程的總結,抽象出了一套高度完善的項目構建的生命周期。整個生命周期包含項目的清理、初始化、編譯、測試、打包、集成測試、驗證、部署、站點生成等幾乎所有的過程。Maven的整個生命周期是抽象的,這意味著整個生命周期本身不會幹任何事情。在Maven項目的整個構建過程中,實際的構建任務都是由Maven插件完成的。Maven本身內置了很多插件,所以我們在項目進行編譯、測試、打包的過程是沒有感覺到。比如編譯就是通過maven-compile-plugin實現的、測試是通過maven-surefire-plugin實現的。
5.1 生命周期詳解
Maven擁有3套獨立的生命周期:clean周期、default周期和site生命周期。clean周期的主要作用是清理項目,default周期的作用是構建項目,site周期的作用是生成項目站點。每個生命周期都若干個階段,比如使用mvn clean命令,就是調用了clean生命周期的clean階段。
5.1.1 clean生命周期
Clean生命周期一共包含了三個階段:
- pre-clean:執行一些需要在clean之前完成的工作。
- clean:移除所有上一次構建生成的文件。
- post-clean:執行一些需要在clean之後立刻完成的工作。
mvn clean中的clean就是上面的clean,在一個生命周期中,運行某個階段的時候,它之前的所有階段都會被運行,也就是說,mvn clean等同於 mvn pre-clean clean,如果我們運行 mvn post-clean,那麼 pre-clean、clean 都會被運行。這是Maven很重要的一個規則,可以大大簡化命令行的輸入。但是執行clean生命周期的某個階段,不會觸發其他生命周期的階段,因為Maven的每個生命周期都是相互獨立的。
5.1.2 site生命周期
下面看一下Site生命周期的各個階段:
- pre-site:執行一些需要在生成站點文檔之前完成的工作。
- site:生成項目的站點文檔。
- post-site:執行一些需要在生成站點文檔之後完成的工作,並且為部署做準備。
- site-deploy:將生成的站點文檔部署到特定的伺服器上。
這裡經常用到的是site階段和site-deploy階段,用以生成和發布Maven站點,這可是Maven相當強大的功能,Manager比較喜歡,文檔及統計數據自動生成,很好看。
5.1.3 default生命周期
最後,來看一下Maven的最重要的default生命周期,絕大部分工作都發生在這個生命周期中,這裡我只解釋一些比較重要和常用的階段:
- validate
- initialize
- generate-sources
- process-sources:處理項目主資源文件,一般來說,是對src/main/resources目錄的內容進行變數替換等工作後,複製到項目輸出的主classpath目錄中;
- generate-resources
- process-resources
- compile 編譯項目的源程式碼,一般來說,是編譯src/main/java目錄下的Java文件至項目輸出的主classpath目錄中。
- process-classes
- generate-test-sources
- process-test-sources:處理項目測試資源文件,一般來說,是對src/test/resources目錄的內容進行變數替換等工作後,複製到項目輸出的測試classpath目錄中。
- generate-test-resources
- process-test-resources
- test-compile:編譯項目的測試源程式碼,一般來說,是編譯src/test/java目錄下的Java文件至項目輸出的測試classpath目錄中。
- process-test-classes
- test:使用合適的單元測試框架運行測試。這些測試程式碼不會被打包或部署。
- prepare-package
- package:接受編譯好的程式碼,打包成可發布的格式,如 JAR 。
- pre-integration-test
- integration-test
- post-integration-test
- verify
- install:將包安裝至本地倉庫,以讓其它項目依賴。
- deploy:將最終的包複製到遠程的倉庫,以讓其它開發人員與Maven項目使用。
基本上,根據名稱我們就能猜出每個階段的用途,關於階段的詳細解釋以及其她階段的解釋,請參考 http://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html 。
5.1.4 命令行和生命周期
mvn clean install site-deploy
5.2 插件詳解
這邊先理解一個重要的概念:插件目標(goal)。每個插件的單個功能就可以理解為是一個插件的目標。舉個例子,maven-dependency-plugin這個插件可以分析項目的依賴樹、可以分析潛在無用的依賴,這一個個功能我們就可以理解為插件的goal。
5.2.1 默認插件綁定
Maven生命周期的某個階段會和插件的某個goal綁定來完成某個特定的任務。Maven已經在生命周期的某個階段默認綁定了一些goal,讓用戶可以直接使用。
表格:clean生命周期綁定的插件
| 生命周期階段 | 插件目標 |
|————|———-|
| pre-clean | – |
| clean | maven-clean-plugin:clean|
| post-clean | – |
表格:site生命周期綁定的插件目標
| 生命周期階段 | 插件目標 |
|————|———-|
| pre-site | – |
| site | maven-site-plugin:site|
| post-site | – |
| site-deploy | maven-site-plugin:deploy |
表格:default生命周期綁定的插件目標
| 生命周期階段 | 插件目標 | 執行的任務 |
|————|———-|———–|
| process-resources | maven-resources-plugin:resources |複製主資源文件到主輸出目錄 |
| compile | maven-compiler-plugin:compile| 編譯主程式碼到主輸出目錄 |
| process-test-resources | maven-resources-plugin:testResources | -|
| test-compile | maven-compiler-plugin:testCompile | – |
| test | maven-surefire-plugin:test | 執行測試用例 |
| package | maven-jar-plugin:jar | 項目打包(如果打成war包的話會綁定其他插件) |
| install | maven-install-plugin:install | 安裝到本地倉庫 |
| deploy | maven-deploy-plugin | 項目部署 |
上表只是列出了擁有插件綁定關係的階段,default生命周期還有很多其他階段,默認他們沒有綁定任何插件,因此這些階段沒有任何實際行為。
除了默認的打包類型jar之外,常見的打包類型還有war、pom、maven-plugin和ear等。
5.2.2 自定義插件綁定
除了內置的綁定之外,用戶還能自己選擇將某個插件的目標綁定到生命周期的某個階段上。這種自定義的綁定方式能讓Maven執行更多功能。下面是一個插件配置的列子:
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-source-plugin</artifactId> <version>2.1.2</version> <executions> <execution> <goals> <goal>jar-no-fork</goal> </goals> <phase>package</phase> </execution> </executions> </plugin> </plugins> </build>
在上面的列子中我們將>maven-source-plugin插件的jar-no-fork目標綁定到了default周期的package階段。所以在執行到package階段的時候jar-no-fork會被調用。有時候我們會發現我們沒配置任何phase,插件的目標也能被調用。這是因為插件已經為目標默認綁定到生命周期的某個階段上了。使用下面的命令可以查看某個目標綁定到了哪個階段上
mvn help:describe -Dplugin=org.apache.maven.plugins:maven-dependency-plugin -Ddetail mvn help:describe -Dplugin=dependency -Ddetail
插件倉庫和依賴的倉庫不是公用的,需要我們另外配置。
<pluginRepositories> <pluginRepository> <id>central</id> <url>http://central</url> <releases><enabled>true</enabled></releases> <snapshots><enabled>true</enabled></snapshots> </pluginRepository> </pluginRepositories>
6. Maven聚合和繼承
6.1 聚合
聚合最主要的表現形式是使用modle標籤來管理多個子模組的構建:
<modules> <module>spring-boot-quickstart</module> <module>spring-boot-web</module> </modules>
6.2 繼承
如果多個模組出現相同的依賴包,這樣在pom.xml文件的內容出現了冗餘、重複的內容,解決這個問題其實使用Maven的繼承機制即可,就像Java的繼承一樣,父類就像一個模板,子類繼承自父類,那麼有些通用的方法、變數都不必在子類中再重複聲明了。Maven的繼承機制類似,在一個父級別的Maven的pom文件中定義了相關的常量、依賴、插件等等配置後,實際項目模組可以繼承此父項目 的pom文件,重複的項不必顯示的再聲明一遍了,相當於父Maven項目就是個模板,等著其他子模組去繼承。不過父Maven項目要高度抽象,高度提取公共的部分(交集),做到一處聲明,多處使用。
可繼承的POM元素:
groupId和version是可以被繼承的,那麼還有哪些POM元素可以被繼承呢?以下是一個完整的列表,並附帶了簡單的說明:
- groupId:項目組 ID,項目坐標的核心元素;
- version:項目版本,項目坐標的核心元素;
- description:項目的描述資訊;
- organization:項目的組織資訊;
- inceptionYear:項目的創始年份;
- url:項目的 url 地址;
- develoers:項目的開發者資訊;
- contributors:項目的貢獻者資訊;
- distributionManagerment:項目的部署資訊;
- issueManagement:缺陷跟蹤系統資訊;
- ciManagement:項目的持續繼承資訊;
- scm:項目的版本控制資訊;
- mailingListserv:項目的郵件列表資訊;
- properties:自定義的 Maven 屬性;
- dependencies:項目的依賴配置;
- dependencyManagement:醒目的依賴管理配置;
- repositories:項目的倉庫配置;
- build:包括項目的源碼目錄配置、輸出目錄配置、插件配置、插件管理配置等;
- reporting:包括項目的報告輸出目錄配置、報告插件配置等。
聚合 VS 繼承
雖然聚合通常伴隨著父POM的繼承關係,但是這兩者不是必須同時存在的,從上面兩者的介紹可以看出來,這兩者的都有不同的作用,他們的作用不依賴於另一個的配置。
父POM是為了抽取統一的配置資訊和依賴版本控制,方便子POM直接引用,簡化子POM的配置。聚合(多模組)則是為了方便一組項目進行統一的操作而作為一個大的整體,所以要真正根據這兩者不同的作用來使用,不必為了聚合而繼承同一個父POM,也不比為了繼承父POM而設計成多模組。
6.3 插件管理
和依賴管理(dependencyManagement)的理念相同,我們可以在父pom中配置插件管理。這樣子模組就可以繼承父模組的插件。
<build> <pluginManagement> <plugins> </plugins> </pluginManagement> </build>
7. Nexus創建私服
私服是一種特殊的Maven遠程倉庫。Nexus分為免費版和專業版。安裝包分為附帶web容器的Bundle版本,和不帶web容器的war包版本。
7.1 Nexus倉庫類型
倉庫有四種類型:
- group(倉庫組) :沒有實際的內容,就是將若干個其他倉庫組合成一個組,在這種類型的倉庫中下載依賴的時候,Maven會從這個組中包含的倉庫依次輪詢,知道下載到依賴為止;
- hosted(宿主) :建在Nexus一台機器上的本地Maven倉庫;
- proxy(代理) :是一個代理,代理了其他遠程倉庫,比如Maven的central倉庫;
- virtual(虛擬):兼容Maven1 版本的jar或者插件。
每個倉庫的格式為maven2或者maven1。此外,倉庫還有一個屬性為Policy(策略),表示該倉庫為發布(Release)版本倉庫還是快照(Snapshot)版本倉庫。最後兩列的值為倉庫的狀態和路徑。
- Maven Central:該倉庫代理Maven中央倉庫,其策略為Release,因此只會下載和快取中央倉庫中的發布版本構件。
- Releases:這是一個策略為Release的宿主類型倉庫,用來部署組織內部的發布版本構件。
- Snapshots:這是一個策略為Snapshot的宿主類型倉庫,用來部署組織內部的快照版本構件。
- 3rd party:這是一個策略為Release的宿主類型倉庫,用來部署無法從公共倉庫獲得的第三方發布版本構件。
- Apache Snapshots:這是一個策略為Snapshot的代理倉庫,用來代理Apache Maven倉庫的快照版本構件。
- Codehaus Snapshots:這是一個策略為Snapshot的代理倉庫,用來代理Codehaus Maven倉庫的快照版本構件。
- Google Code:這是一個策略為Release的代理倉庫,用來代理Google Code Maven倉庫的發布版本構件。
- java.net-Maven 2:這是一個策略為Release的代理倉庫,用來代理java.net Maven倉庫的發布版本構件。
- Public Repositories:該倉庫組將上述所有策略為Release的倉庫聚合併通過一致的地址提供服務。
- Public Snapshot Repositories:該倉庫組將上述所有策略為Snapshot的倉庫聚合併通過一致的地址提供服務。
7.2 配置Nexus倉庫
- 在POM中配置倉庫
<project> <repositories> <repository> <id>maven-net-cn</id> <name>Maven China Mirror</name> <url>http://maven.net.cn/content/groups/public/</url> <releases> <enabled>true</enabled> </releases> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories> <pluginRepositories> <pluginRepository> <id>maven-net-cn</id> <name>Maven China Mirror</name> <url>http://maven.net.cn/content/groups/public/</url> <releases> <enabled>true</enabled> </releases> <snapshots> <enabled>false</enabled> </snapshots> </pluginRepository> </pluginRepositories> </project>
這種配置方式只會對當前項目生效,如果想對所有項目生效,可以在settings文件中配置。
- 在Settings.xml中配置
<settings> ... <profiles> <profile> <id>Nexus</id> <!-- repositories and pluginRepositories here--> </profile> </profiles> <activeProfiles> <activeProfile>Nexus</activeProfile> </activeProfiles> ... </settings>
- 使用鏡像
如果你的地理位置附近有一個速度更快的central鏡像,或者你想覆蓋central倉庫配置,或者你想為所有POM使用唯一的一個遠程倉庫(這個遠程倉庫代理的所有必要的其它倉庫)
<settings> ... <mirrors> <mirror> <id>maven-net-cn</id> <name>Maven China Mirror</name> <url>http://maven.net.cn/content/groups/public/</url> <mirrorOf>*</mirrorOf> </mirror> </mirrors> ... </settings>
7.3 部署構建到Nexus
…
8. Maven測試
Maven的測試插件會自動執行src/test/java/下面的
- **/Test*.java;
- **/*Test.java;
- **/*TestCase.java
以上形式的類。
8.1 跳過測試
mvn clean install -DskipTests <properties> <skipTests>true</skipTests> </properties>
8.2 指定運行某個測試用例
mvn test -Dtest=Random1Test,Random2Test mvn test -Dtest=Random*Test
8.3 排除測試
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>3.0.0-M3</version> <configuration> <includes> <include>**/*Tests.java</include> </includes> <excludes> <exclude>**/TestCircle.java</exclude> <exclude>**/TestSquare.java</exclude> </excludes> </configuration> </plugin> </plugins> </build>
9. 持續集成
Hudson+Maven+git版本控制–>持續集成
10. 構建Web項目
使用Cargo可以進行遠程Web應用部署…
11. Maven版本管理
Maven的版本管理一般會遵循:
<主版本>.<次版本>.<增量版本>-<里程碑版本>
- 主版本號:表示項目架構有重大的變更;
- 次版本號:較大範圍的功能增加和變化,以及Bug修復,但總體項目架構沒什麼變化;
- 增量版本:一般表示重大Bug修復,例如1.4.0發布後發現一個重大Bug,發現一個重大Bug修復後發布1.4.1;
-
里程碑版本:SNAPSHOT–>alpha–>beta–>release–>GA
SNAPSHOT:正在開發中的版本
Alpha: 內部測試的版本(項目組內部測試)
Beta:用戶下載下來測試使用
Release: 用戶使用下來沒什麼問題就可以發布Release版本
GA:穩定版本
11.1 GPG簽名
使用GPG簽名來校驗依賴包有沒被第三方惡意篡改
12. 靈活的構建
12.1 Maven屬性
Maven中有6中基本屬性:
-
內置屬性
主要有兩個常用內置屬性——${basedir}表示項目根目錄,即包含pom.xml文件的目錄;${version}表示項目版本。 -
POM屬性
pom中對應元素的值,例如${project.artifactId}對應了元素的值,常用的POM屬性包括:
- ${project.build.sourceDirectory}:項目的主源碼目錄,默認為src/main/java/;
- ${project.build.testSourceDirectory}:項目的測試源碼目錄,默認為/src/test/java/;
- ${project.build.directory}:項目構建輸出目錄,默認為target/;
- ${project.build.outputDirectory}:項目主程式碼編譯輸出目錄,默認為target/classes/;
- ${project.build.testOutputDirectory}:項目測試程式碼編譯輸出目錄,默認為target/testclasses/;
- ${project.groupId}:項目的groupId;
- ${project.artifactId}:項目的artifactId;
- ${project.version}:項目的version,於${version}等價;
- ${project.build.finalName}:項目打包輸出文件的名稱,默認為${project.artifactId}${project.version}.
-
自定義屬性
在pom中元素下自定義的Maven屬性。 -
Settings屬性
與POM屬性同理。如${settings.localRepository}指向用戶本地倉庫的地址。 -
Java系統屬性
所有Java系統屬性都可以使用Maven屬性引用,例如${user.home}指向了用戶目錄。可以通過命令行mvn help:system查看所有的Java系統屬性 -
環境變數屬性
所有環境變數都可以使用以env.開頭的Maven屬性引用。例如${env.JAVA_HOME}指代了JAVA_HOME環境變數的值。也可以通過命令行mvn help:system查看所有環境變數。
12.2 資源過濾
<build> <resources> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> <excludes> <exclude>**/config/*.*</exclude> </excludes> </resource> <resource> <directory>src/main/resources/config</directory> <filtering>true</filtering> <includes> <include>application-${profiles.active}.yml</include> </includes> </resource> </resources> </build>
12.3 Maven profile
12.3.1 針對不同環境profile
<project> ... <profiles> <profile> <id>dev</id> <properties> <profiles.active>dev</profiles.active> </properties> <activation> <!-- **這邊可以配置各種激活條件** -- > <property> <name></name> <value></value> </property> <file> <exists></exists> </file> <os> ... </os> <jdk> ... </jdk> <activeByDefault>true</activeByDefault> </activation> </profile> <profile> <id>stg</id> <properties> <profiles.active>stg</profiles.active> </properties> </profile> <profile> <id>prod</id> <properties> <profiles.active>prod</profiles.active> </properties> </profile> </profiles> ... </project>
12.3.2 激活profile
-
命令行激活
mvn clean install -Pdev
- settings文件顯示激活
如果你希望某個profile一直處於激活狀態,可以直接在settings文件中配置:
<profiles> <profile> <id>nexus</id> <repositories> <repository> <id>central</id> <url>http://central</url> <releases><enabled>true</enabled></releases> <snapshots><enabled>true</enabled></snapshots> </repository> </repositories> <pluginRepositories> <pluginRepository> <id>central</id> <url>http://central</url> <releases><enabled>true</enabled></releases> <snapshots><enabled>true</enabled></snapshots> </pluginRepository> </pluginRepositories> </profile> </profiles> <activeProfiles> <!--make the profile active all the time --> <activeProfile>nexus</activeProfile> </activeProfiles
12.3.3 Web項目打包
…
13. 項目站點生成
…後續完成…
14. 編寫Mavem插件
…後續完成…
附錄
A. 常用命令
下面我們總結下Maven中常用的命令。
- mvn help:system 調用help這個插件,列印出jvm的系統屬性和作業系統的環境變數
- mvn help:describe -Dplugin=org.apache.maven.plugins:maven-dependency-plugin -Ddetail
- mvn archetype:generate 這個命令可以幫助我們生成一個Maven項目的骨架
|--Pom.xml |--src |--main |--java |--resource |--test |--java |--resource
- mvn dependency:list
- mvn denpedency:tree
- mvn dependency:analyze 可以分析當前項目依賴的問題
- mvn clean install -DskipTests
- mvn clean deploy 編譯打包並發布到遠程倉庫
- mvn clean install site
B. POM文件總結
元素名稱 簡 介 <project> POM的xml根元素 <parent> 聲明繼承 <modules> 聲明聚合 <groupId> 坐標元素之一 <artifactId> 坐標元素之一 <version> 坐標元素之一 <packaging> 坐標元素之一,默認值jar <name> 名稱 <description> 描述 <organization> 所屬組織 <licenses><license> 許可證 <mailingLists><mailingList> 郵件列表 <developers><developer> 開發者 <contributors><contributor> 貢獻者 <issueManagement> 問題追蹤系統 <ciManagement> 持續集成系統 <scm> 版本控制系統 <prerequisites><maven> 要求Maven最低版本,默認值為2.0 <build><sourceDirectory> 主源碼目錄 <build><scriptSourceDirectory> 腳本源碼目錄 <build><testSourceDirectory> 測試源碼目錄 <build><outputDirectory> 主源碼輸出目錄 <build><testOutputDirectory> 測試源碼輸出目錄 <build><resources><resource> 主資源目錄 <build><testResources><testResource> 測試資源目錄 <build><finalName> 輸出主構件的名稱 <build><directory> 輸出目錄 <build><filters><filter> 通過properties文件定義資源過濾屬性 <build><extensions><extension> 擴展Maven的核心 <build><pluginManagement> 插件管理 <build><plugins><plugin> 插件 <profiles><profile> POM Profile <distributionManagement><repository> 發布版本部署倉庫 <distributionManagement> <snapshotRepository> 快照版本部署倉庫 <distributionManagement> <site> 站點部署 <repositories><repository> 倉庫 <pluginRepositories><pluginRepository> 插件倉庫 <dependencies><dependency> 依賴 <dependencyManagement> 依賴管理 <properties> Maven屬性 <reporting><plugins> 報告插件
C. settings文件例子
<settings> <localRepository>D:softwaremavenRepository</localRepository> <proxies> <proxy> <id>optional</id> <active>true</active> <protocol>http</protocol> <host>10.47.22.247</host> <port>80</port> <nonProxyHosts>some.host.com</nonProxyHosts> </proxy> </proxies> <servers> <server> <id>releases</id> <username>pub_mvn_deploy</username> <password>mvn.Deploy</password> </server> <server> <id>snapshots</id> <username>pub_mvn_deploy</username> <password>mvn.Deploy</password> </server> <server> <id>nexus</id> <username>pub_mvn_deploy</username> <password>mvn.Deploy</password> </server> </servers> <mirrors> <mirror> <id>nexus</id> <mirrorOf>*</mirrorOf> <url>http://maven.dev.xxx.com/nexus/content/groups/public</url> </mirror> </mirrors> <profiles> <profile> <id>nexus</id> <repositories> <repository> <id>central</id> <url>http://central</url> <releases> <enabled>true</enabled> </releases> <snapshots> <enabled>true</enabled> </snapshots> </repository> </repositories> <pluginRepositories> <pluginRepository> <id>central</id> <url>http://central</url> <releases> <enabled>true</enabled> </releases> <snapshots> <enabled>true</enabled> </snapshots> </pluginRepository> </pluginRepositories> </profile> <!-- if you want to be able to switch to the defaultprofile profile put this in the active profile --> <profile> <id>defaultprofile</id> <repositories> <repository> <id>maven.default</id> <name>default maven repository</name> <url>http://repo1.maven.org/maven2</url> <snapshots> <enabled>true</enabled> </snapshots> </repository> <repository> <id>maven.snapshot</id> <name>Maven snapshot repository</name> <url>http://people.apache.org/repo/m2-snapshot-repository</url> <snapshots> <enabled>true</enabled> </snapshots> </repository> </repositories> </profile> </profiles> <activeProfiles> <!--make the profile active all the time --> <activeProfile>nexus</activeProfile> </activeProfiles> </settings>
D. settings文件元素參考表
1,<settings>,settings.xml文檔的根元素 2,<localRepository>,本地倉庫 3,<interactiveMode>,Maven是否與用戶交互,默認值true 4,<offline>,離線模式,默認值false 5,<pluginGroups> <pluginGroup>,插件組 6,<servers> <server>,下載與部署倉庫的認證資訊 7,<mirrors> <mirror>,倉庫鏡像 8,<proxies> <proxy>,代理 9,<profiles> <profile>,Settings Profile 10,<activeProfiles> <activeProfile>,激活Profile
E. 常用插件總結
插件名稱 用途 來源 maven-clean-plugin 清理項目 Apache maven-compiler-plugin 編譯項目 Apache maven-deploy-plugin 部署項目 Apache maven-install-plugin 安裝項目 Apache maven-resources-plugin 處理資源文件 Apache maven-site-plugin 生成站點 Apache maven-surefire-plugin 執行測試 Apache maven-jar-plugin 構建jar項目 Apache maven-war-plugin 構建war項目 Apache maven-shade-plugin 構建包含依賴的jar包 Apache maven-changelog-plugin 生成版本控制變更報告 Apache maven-checkstyle-plugin 生成CheckStyle報告 Apache maven-javadoc-plugin 生成javadoc文檔 Apache maven-jxr-plugin 生成源碼交叉引用文檔 Apache maven-pmd-plugin 生成pmd報告 Apache maven-project-info-reports-plugin 生成項目資訊報告 Apache maven-surefire-report-plugin 生成單元測試報告 Apache maven-antrun-plugin 調用Ant任務 Apache maven-archetype-plugin 基於Archetype生成項目骨架 Apache maven-assembly-plugin 構建自定義格式的分發包 Apache maven-dependency-plugin 依賴分析及控制 Apache maven-enforcer-plugin 定義規則並強制要求項目遵守 Apache maven-pgp-plugin 為項目構件生成pgp簽名 Apache maven-help-plugin 獲取項目及Maven環境的資訊 Apache maven-invoker-plugin 自動運行Maven項目構建並驗證 Apache maven-release-plugin 自動化項目版本發布 Apache maven-scm-plugin 集成版本控制系統 Apache maven-source-plugin 生成源碼包 Apache maven-eclipse-plugin 生成eclipse項目環境配置 Apache build-helper-maven-plugin 包含各種支援構建生命周期的目標 Codehaus exec-maven-plugin 運行系統程式或者java程式 Codehaus jboss-maven-plugin 啟動、停止jboss、部署項目 Codehaus properties-maven-plugin 從properties文件讀寫Maven屬性 Codehaus sql-maven-plugin 運行sql腳本 Codehaus tomcat-maven-plugin 啟動、停止tomcat、部署項目 Codehaus versions-maven-plugin 自動化批量更新pom版本 Codehaus cargo-maven-plugin 啟動/停止/配置各類web容器自動化部署web項目 Cargo jetty-maven-plugin 集成jetty容器、實現快速開發測試 Eclipse mave-gae-plugin 集成google app engine Googlecode maven-license-plugin 自動化添加許可證證明至源碼文件 Googlecode maven-andmid-plugin 構建Android項目 Googlecode
F. 超級POM
這個POM會被每個POM繼承。它的定義在model-builder.jar這個包下面。我們解壓就可以看到。
G. 圖片工具
http://techsmith.com/jing這個網站提供了一個不錯的製圖工具,有時間可以研究下。
H. 參考書籍
- 《Maven實戰》
- 《Maven權威指南》