maven
是一款優(yōu)秀的服務(wù)構(gòu)建工具兜喻,基于約定優(yōu)于配置原則涩哟,提供標(biāo)準(zhǔn)的服務(wù)構(gòu)建流程番甩。maven
的優(yōu)點(diǎn)不僅限于服務(wù)構(gòu)建侵贵,使用maven
能夠做到高效的依賴管理,并且提供有中央倉庫可以完成絕大多數(shù)依賴的下載使用缘薛。
maven
自身提供有豐富的插件窍育,可以在不使用額外插件的條件下完成服務(wù)的編譯、測試宴胧、打包漱抓、部署等服務(wù)構(gòu)建流程,即maven
對服務(wù)的構(gòu)建過程是通過多個插件完成的恕齐,且maven
已經(jīng)自定義了插件的行為乞娄。可以理解為每一個插件都是對接口的實(shí)現(xiàn)显歧,可以自定義插件补胚,以完成自定義功能,例如完成對不同編程語言的服務(wù)構(gòu)建過程追迟。不過相對于gradle
的自定義插件行為溶其,maven
的實(shí)現(xiàn)過程略微復(fù)雜。
IDEA
配置maven
工具
File > Other Settings > Default Settings
進(jìn)入Default Settings
設(shè)置框
Build, Execution, Deployment > Build Tools > Maven
進(jìn)入maven
工具配置
上圖中展示了三項(xiàng)配置敦间,Maven home directory
指向maven
工具根目錄瓶逃,User settings file
指向conf
下的settings.xml
文件,表示使用全局的settings.xml
文件廓块,Local repository
指向本地倉庫地址厢绝。
settings.xml
文件用于記錄本地倉庫、遠(yuǎn)程倉庫以及認(rèn)證信息等maven
工程使用的元素带猴,該文件有兩種級別昔汉,用戶級別和全局級別,存放位置一般為${maven.home}/conf/settings.xml
和${user.home}/.m2/settings.xml
拴清。Local repository
本地倉庫用于存放自動下載后的依賴文件和安裝到本地的服務(wù)靶病。
settings.xml
文件
settings.xml
文件起到的作用為全局作用会通,該文件中定義的行為一般作用于多個工程,或者所有工程娄周。其中有幾個較為重要的元素:
localRepository
本地倉庫的地址涕侈,在maven
工程中依賴的構(gòu)件,首先到本地倉庫進(jìn)行查找煤辨,查找不到才會到遠(yuǎn)程倉庫查找裳涛。servers
在工程中進(jìn)行構(gòu)件部署或者依賴下載時,添加的repositories,distributionManagement
元素中定義了服務(wù)器的地址众辨,登錄服務(wù)器需要的認(rèn)證信息端三,例如秘鑰或者用戶名密碼需要與工程分離,所以定義在該標(biāo)簽中與工程進(jìn)行關(guān)聯(lián)鹃彻。mirrors
當(dāng)遠(yuǎn)程倉庫的連接速度較慢時郊闯,或者使用私服進(jìn)行依賴控制時,可以配置鏡像服務(wù)器來替代某個或所有遠(yuǎn)程服務(wù)器浮声。profiles
提供多套配置虚婿,根據(jù)環(huán)境不同、指定的條件判斷結(jié)果泳挥,選擇使用某種配置然痊。例如在某個profile
中配置遠(yuǎn)程倉庫和插件倉庫,根據(jù)使用的操作系統(tǒng)是windows
或者unix
屉符,選擇性激活不同的配置剧浸。activeProfiles
手動激活使用某一個profile
配置。
建立maven
工程
File > New > Project
建立maven
工程
此處填寫的為項(xiàng)目的坐標(biāo)矗钟,GroupId
表示公司或組織唆香,ArtifactId
表示產(chǎn)品,Version
表示產(chǎn)品版本號吨艇。
使用這三個字段形成一個坐標(biāo)躬它,完成對此工程的表述。在
maven
的世界中东涡,對所有依賴的引用都是通過坐標(biāo)完成的冯吓,即使用GAV(GroupId,ArtifactId,Version)
進(jìn)行定位。
pom.xml
文件
上圖所示為工程根目錄下的pom.xml
文件內(nèi)容疮跑,modelVersion
表示當(dāng)前POM
模型的版本组贺,對于當(dāng)前的maven 3
而言,元素值為4.0.0
祖娘,groupId,artifactId,version
則是前面提到過的工程坐標(biāo)失尖。
POM(Project Object Model)
作為項(xiàng)目對象模型,用于描述工程信息、依賴信息掀潮,并且定義構(gòu)建過程中的操作菇夸。該文件為maven
構(gòu)建服務(wù)流程中最重要的文件,雖然默認(rèn)情況下文件內(nèi)容很少胧辽,只描述了工程的坐標(biāo)信息峻仇,那是因?yàn)橐磺袠?gòu)建操作都是按照約定進(jìn)行執(zhí)行的公黑,即約定優(yōu)于配置(Convention Over Configuration)
邑商。
目錄結(jié)構(gòu)
由上圖可知,maven
默認(rèn)生成的源碼目錄為src\main\java
凡蚜,默認(rèn)的資源目錄為src\main\resources
人断,默認(rèn)的測試目錄為src\test\java
。如果此工程已經(jīng)完成朝蜘,直接進(jìn)行編譯恶迈、測試等構(gòu)建過程的話,則會直接到默認(rèn)目錄執(zhí)行編譯谱醇、測試活動暇仲。
該目錄結(jié)構(gòu)屬于約定的一種內(nèi)容,因?yàn)槠綍r建立工程目錄時多按照該結(jié)構(gòu)設(shè)計(jì)副渴,所以在
maven
中直接生成該目錄結(jié)構(gòu)奈附,避免了人工的操作≈缶纾可以自定義源碼和編譯后目錄斥滤,只需要在pom.xml
文件中指定,則編譯構(gòu)建服務(wù)時按照指定的目錄進(jìn)行勉盅。
以源碼目錄佑颇、測試目錄和資源目錄三種為例,可以指定源路徑以及編譯后目錄:
<build>
<!--編譯服務(wù)后的目標(biāo)生成目錄-->
<directory>${basedir}\target</directory>
<!--源碼目錄和編譯后源碼生成目錄-->
<sourceDirectory>${basedir}\src\main\java</sourceDirectory>
<outputDirectory>${build.directory}\classes</outputDirectory>
<!--源碼下資源路徑和編譯后資源生成路徑-->
<resources>
<resource>
<directory>${basedir}\src\main\resources</directory>
<targetPath>${build.directory}\classes</targetPath>
</resource>
</resources>
<!--測試路徑和編譯后測試生成路徑-->
<testSourceDirectory>${basedir}\src\test\java</testSourceDirectory>
<testOutputDirectory>${build.directory}\test-classes</testOutputDirectory>
</build>
<build>
標(biāo)簽中自定義文件路徑時草娜,可以使用文件系統(tǒng)的全路徑挑胸,也可以使用maven
的內(nèi)置屬性進(jìn)行定義。這里的${basedir}
表示工程根目錄宰闰,${build.directory}
表示編譯服務(wù)后的目標(biāo)生成目錄茬贵,即<directory>
標(biāo)簽定義的目錄。
maven
工程內(nèi)部存在許多可以使用的內(nèi)置屬性议蟆,以源碼和測試為例:
${build.sourceDirectory}
: 源碼文件目錄
${build.outputDirectory}
: 源碼編譯后目錄
${build.testSourceDirectory}
: 測試文件目錄
${build.testOutputDirectory}
: 測試文件編譯后目錄
服務(wù)構(gòu)建
maven
工程的構(gòu)建過程存在三個生命周期:clean
,default
和site
闷沥,每個生命周期存在多個階段。clean
生命周期的作用為清理工程編譯后生成信息咐容;site
生命周期用于為工程生成站點(diǎn)舆逃,可以通過瀏覽器查看各項(xiàng)站點(diǎn)信息;下面主要討論default
生命周期的作用,該生命周期包含多個階段路狮,主要完成工作如下:
-
validate:
對工程信息進(jìn)行校驗(yàn)虫啥,判斷是否缺失必要的文件 -
compile:
編譯源碼 -
test:
使用測試框架執(zhí)行測試文件 -
package:
對編譯后文件進(jìn)行打包,生成jar
或war
等格式文件 -
verify:
對集成測試結(jié)果進(jìn)行校驗(yàn)奄妨,判斷是否達(dá)到質(zhì)量標(biāo)準(zhǔn) -
install:
按照打包文件到本地倉庫 -
deploy:
將打包文件部署到遠(yuǎn)程服務(wù)器
在生命周期內(nèi)涂籽,對指定階段的執(zhí)行,會執(zhí)行該階段前的所有階段砸抛,例如執(zhí)行
mvn test
命令评雌,實(shí)際執(zhí)行的階段有validate、compile直焙、test
景东。
之前提到過,maven
的服務(wù)構(gòu)建過程是通過插件來完成的奔誓,即每個階段要執(zhí)行的操作斤吐,都是通過插件定義實(shí)現(xiàn)的。每個插件可以定義多個goal
厨喂,所以并不是每個階段對應(yīng)一個插件和措,而是對應(yīng)插件的一個goal
。通過將生命周期的階段與插件的goal
進(jìn)行綁定蜕煌,在使用過程中只需要聲明要執(zhí)行的聲明周期階段派阱,即可調(diào)用綁定的插件goal
完成操作。例如執(zhí)行mvn install
命令幌绍,實(shí)際執(zhí)行的是install
生命周期階段綁定的插件的goal
颁褂。
上圖中Profiles
表示使用到的配置,Lifecycle
列舉了常用的生命周期階段傀广,Plugins
列舉了常用插件及插件的goal
颁独,這里并沒有顯示出階段與goal
的綁定關(guān)系。下面展示的是maven 3.6.0
版本中伪冰,打包類型為jar
時誓酒,default
生命周期中各階段與插件goal
的綁定關(guān)系:
<component>
<role>org.apache.maven.lifecycle.mapping.LifecycleMapping</role>
<role-hint>jar</role-hint>
<implementation>org.apache.maven.lifecycle.mapping.DefaultLifecycleMapping</implementation>
<configuration>
<lifecycles>
<lifecycle>
<id>default</id>
<!-- START SNIPPET: jar-lifecycle -->
<phases>
<process-resources>
org.apache.maven.plugins:maven-resources-plugin:2.6:resources
</process-resources>
<compile>
org.apache.maven.plugins:maven-compiler-plugin:3.1:compile
</compile>
<process-test-resources>
org.apache.maven.plugins:maven-resources-plugin:2.6:testResources
</process-test-resources>
<test-compile>
org.apache.maven.plugins:maven-compiler-plugin:3.1:testCompile
</test-compile>
<test>
org.apache.maven.plugins:maven-surefire-plugin:2.12.4:test
</test>
<package>
org.apache.maven.plugins:maven-jar-plugin:2.4:jar
</package>
<install>
org.apache.maven.plugins:maven-install-plugin:2.4:install
</install>
<deploy>
org.apache.maven.plugins:maven-deploy-plugin:2.7:deploy
</deploy>
</phases>
<!-- END SNIPPET: jar-lifecycle -->
</lifecycle>
</lifecycles>
</configuration>
</component>
由綁定關(guān)系可知,執(zhí)行mvn install
命令時贮聂,install
階段運(yùn)行的是mvn org.apache.maven.plugins:maven-install-plugin:2.4:install
靠柑,該命令格式為mvn groupId:artifactId:version:goal
。
多模塊
以上示例展示了創(chuàng)建maven
工程時的默認(rèn)目錄結(jié)構(gòu)吓懈,并沒有存在繼承或者聚合的情況歼冰。在實(shí)際工作中,多數(shù)的項(xiàng)目結(jié)構(gòu)較為復(fù)雜耻警,例如工程中經(jīng)常需要劃分dao
層隔嫡、service
層和web
層甸怕,為了保證各層的獨(dú)立性和降低各層之間的耦合度,這種情況下可以給工程建立多個模塊分開管理腮恩。
右鍵工程梢杭,New > Module
,進(jìn)入模塊信息窗口
新建子模塊秸滴,默認(rèn)繼承自父模塊
可以觀察到父模塊的pom.xml
文件增加新內(nèi)容
<packaging>
類型自動變?yōu)?code>pom類型武契,<modules>
包含兩個新創(chuàng)建的子模塊。
maven
工程的默認(rèn)打包類型為jar
荡含,聚合情況下的父模塊打包類型默認(rèn)為pom
類型咒唆,因?yàn)橐话愀改K只存在一個pom.xml
文件,不包含其他類型文件内颗,其作用為提供根pom.xml
文件給多個子模塊使用钧排。
子模塊的pom.xml
文件內(nèi)容中敦腔,通過<parent>
標(biāo)簽聲明繼承關(guān)系均澳,繼承父模塊的groupId
和version
,所以子模塊pom
中只需要填寫一個artifactId
即可符衔。
繼承和聚合略有不同找前,繼承關(guān)系中,被繼承的
pom
并不知道自身被哪些工程繼承判族,具體工程則明確知道繼承哪個pom
躺盛;聚合關(guān)系中,根pom
中明確聲明了包含哪些子模塊形帮,模塊則并不感知被哪些pom
包含槽惫。在該示例中,繼承和聚合同時存在辩撑。
通過繼承pom
的方式界斜,可以有效的在多模塊工程中對依賴的構(gòu)件進(jìn)行版本控制,避免不同模塊之間對同一個依賴構(gòu)件的使用合冀,存在版本不一致問題各薇。各模塊示例依賴如下:
module_A:
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.7</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
</dependencies>
模塊module_A
依賴junit
和log4j
module_B:
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.7</version>
<scope>test</scope>
</dependency>
</dependencies>
模塊module_B
依賴junit
當(dāng)module_A
和module_B
都存在對同一個構(gòu)件junit:junit:4.7
的依賴時,可以將該構(gòu)件提取到根pom
文件的<dependencies>
中君躺,子模塊繼承根pom
時會自動添加對該構(gòu)件的使用峭判。如下所示:
root:
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.7</version>
<scope>test</scope>
</dependency>
</dependencies>
在根pom
中聲明對構(gòu)件junit:junit:4.7
的依賴。
module_A:
<dependencies>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
</dependencies>
模塊module_A
繼承對junit
的依賴棕叫,只需要聲明log4j
依賴即可林螃。
模塊module_B
同樣繼承對junit
的依賴,<dependencies>
聲明依賴為空俺泣,不需要該標(biāo)簽疗认。
在根
pom
中聲明依賴急侥,子模塊自動繼承依賴的方式,雖然可以統(tǒng)一工程中使用的構(gòu)件版本號侮邀,但是當(dāng)根pom
中聲明依賴較多時坏怪,可能會造成一些構(gòu)件泛濫,即有些子模塊并需要如此多的依賴绊茧。
可以在根pom
中配置<dependencyManagement>
標(biāo)簽铝宵,在標(biāo)簽內(nèi)列出子模塊可能需要使用的構(gòu)件及版本,當(dāng)子模塊使用到其中的構(gòu)件時华畏,在子模塊內(nèi)部聲明構(gòu)件的groupId
和artifactId
即可鹏秋,以此進(jìn)行版本控制。示例如下:
root:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.7</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
</dependencies>
</dependencyManagement>
在根pom
中列出子模塊可能使用的依賴亡笑,此時并不會下載使用這些依賴侣夷。
module_A:
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</dependency>
</dependencies>
模塊module_A
中聲明使用junit
和log4j
依賴,此時子模塊會下載使用根pom
中聲明版本號的對應(yīng)依賴仑乌。
module_B:
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
</dependencies>
模塊module_B
中聲明使用junit
依賴百拓,此時子模塊會下載使用根pom
中聲明版本號的對應(yīng)依賴。
依賴范圍與依賴傳遞
觀察之前對junit
構(gòu)件的依賴聲明:
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.7</version>
<scope>test</scope>
</dependency>
在聲明的依賴信息中晰甚,除了坐標(biāo)之外衙传,還具有scope
屬性,該屬性即為構(gòu)件的范圍屬性厕九。某些scope
屬性指示了構(gòu)件是否具有傳遞性蓖捶。
當(dāng)工程
A
依賴工程B
,工程B
依賴工程C
時扁远,則工程A
可能會加載工程B
的依賴缚俏,即工程C
肉拓,該情況稱為依賴傳遞,A
對B
、B
對C
稱為直接依賴碗暗,A
對C
稱為間接依賴虾啦。
maven
依賴聲明中主要有以下六種依賴范圍:
compile:
默認(rèn)依賴范圍乓梨,作用于工程的編譯瞄桨、測試和運(yùn)行期,并且會傳遞到依賴該模塊的工程中provided:
作用于工程的編譯和測試階段洒宝,在運(yùn)行期不起作用购公,用于表示運(yùn)行期對該構(gòu)件的依賴已經(jīng)由容器提供,該依賴范圍不具有傳遞性runtime:
作用于測試和運(yùn)行階段雁歌,在編譯期不起作用宏浩,具有傳遞性test:
作用于測試和運(yùn)行階段,在編譯期不起作用靠瞎,且不具有傳遞性system:
與provided
類似比庄,作用于工程的編譯和測試階段求妹,在運(yùn)行期不起作用,不過需要<systemPath>
標(biāo)簽顯式指明使用的是系統(tǒng)上的某個依賴import:
只能使用于<dependencyManagement>
標(biāo)簽中對包類型為pom
的構(gòu)件依賴佳窑,使用該范圍后制恍,會將依賴的pom
中<dependencyManagement>
標(biāo)簽內(nèi)的依賴加載到當(dāng)前<dependencyManagement>
標(biāo)簽中。該范圍對傳遞性沒有影響
各范圍傳遞性:
依賴范圍 | 編譯期 | 測試期 | 運(yùn)行期 | 傳遞性 |
---|---|---|---|---|
compile | Y | Y | Y | Y |
test | - | Y | Y | - |
provided | Y | Y | - | - |
runtime | - | Y | Y | Y |
若A
對B
依賴范圍定義如左側(cè)一列神凑,B
對C
依賴范圍如上面一行净神,則A
對C
的依賴性如下:
system
與provided
類似,import
只做引入<dependencyManagement>
標(biāo)簽內(nèi)容的作用溉委,對傳遞性沒有影響鹃唯,所以這兩個scope
屬性沒有列出來。
compile | test | provided | runtime | |
---|---|---|---|---|
compile | compile | - | - | runtime |
test | test | - | - | test |
provided | provided | - | - | provided |
runtime | runtime | - | - | runtime |
根據(jù)表格結(jié)果可知瓣喊,test坡慌、provided
兩個scope
屬性不具有傳遞性,所以B
對C
依賴范圍為這兩個屬性值的列藻三,A
對C
的依賴不存在洪橘;A
對B
的依賴范圍為這兩個屬性值的行,傳遞過來的依賴性降低為這兩個屬性值趴酣。compile梨树、runtime
兩個scope
屬性具有傳遞性,runtime
作用范圍低于compile
岖寞,按照木桶原則,構(gòu)件傳遞時按照最小范圍傳遞柜蜈,A
對B
的依賴范圍為runtime
的行仗谆,A
對C
的依賴性降為runtime
;A
對B
的依賴范圍為compile
的行淑履,A
對C
的依賴性降低為B
對C
的依賴性隶垮。
因?yàn)榇嬖趥鬟f依賴的情況,所以可能會存在間接依賴構(gòu)件版本不一致的情況秘噪,即依賴沖突狸吞。
maven
選擇使用的構(gòu)件版本規(guī)則為 最短路徑優(yōu)先,若存在A->B->C->D 2.0
與A->E->D 1.0
的情況指煎,則在工程A
中選擇的構(gòu)件D
版本為1.0
蹋偏。若兩條路徑長度相同,則選擇最先聲明使用的構(gòu)件至壤,即 最先聲明優(yōu)先威始。
參考
https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html
https://blog.csdn.net/luanlouis/article/details/50492163
http://www.cnblogs.com/dcba1112/archive/2011/05/01/2033805.html
https://haoran-10.iteye.com/blog/2307081