一、maven的兩個(gè)作用
- 項(xiàng)目自動(dòng)化構(gòu)建,通過(guò)命令行就可以完成整個(gè)項(xiàng)目構(gòu)建過(guò)程,不需要我們手動(dòng)地進(jìn)行項(xiàng)目構(gòu)建
- 管理項(xiàng)目依賴和jar包
以上兩個(gè)就是maven的主要功能:項(xiàng)目依賴管理和項(xiàng)目構(gòu)建管理。
所以Maven是一個(gè)功能強(qiáng)大的構(gòu)建工具,可以幫助我們自動(dòng)化構(gòu)成過(guò)程,從清理、編譯、測(cè)試到生成報(bào)告,再到打包部署。同時(shí)Maven也是一個(gè)依賴管理和項(xiàng)目信息管理的工具,它能夠?qū)⑽覀冃枰蕾嚨陌ㄟ^(guò)pom.xml來(lái)配置對(duì)應(yīng)信息就可以進(jìn)行統(tǒng)一的幫我們管理整個(gè)項(xiàng)目的依賴關(guān)系和jar包。
二、項(xiàng)目構(gòu)建
有人認(rèn)為;從建立項(xiàng)目、編寫代碼到測(cè)試、打包、再到部署的整個(gè)流程及時(shí)項(xiàng)目構(gòu)建的過(guò)程。
自動(dòng)化構(gòu)建:
舉個(gè)例子,我今天從git上pull下了代碼,開始編碼工作,解決bug,編寫測(cè)試代碼,跑測(cè)試用例,解決問(wèn)題,打包,部署。發(fā)現(xiàn)問(wèn)題,重新修改代碼,測(cè)試,打包,部署。
項(xiàng)目構(gòu)建的整一個(gè)過(guò)程是非常繁瑣的,可能一天下來(lái),我們很多時(shí)間都花在打包,測(cè)試,部署身上了。真正專注在解決業(yè)務(wù)代碼需求的時(shí)候就少了很多。所以我們此時(shí)就會(huì)想有沒(méi)有東西可以幫我們一條龍的解決掉,而不需要程序員自己總是手動(dòng)的去構(gòu)建項(xiàng)目。
所謂自動(dòng)化構(gòu)建就是一個(gè)腳本或一個(gè)工具,幫你解決總是要手動(dòng)構(gòu)建的問(wèn)題。
三、maven的坐標(biāo)和依賴
3.1 坐標(biāo)
maven中的坐標(biāo)是用來(lái)描述一個(gè)構(gòu)件存在maven倉(cāng)庫(kù)中的地址,也就是maven是通過(guò)pom.xml文件中構(gòu)件的坐標(biāo)去倉(cāng)庫(kù)中尋找的。只有我們提供了正確的坐標(biāo)元素,maven才能在倉(cāng)庫(kù)中找到對(duì)應(yīng)的構(gòu)件,所以我們?cè)诙x一個(gè)maven項(xiàng)目時(shí),就必須為該maven項(xiàng)目定義一個(gè)項(xiàng)目坐標(biāo),去對(duì)外暴露地址。
3.2、maven坐標(biāo)的5個(gè)重要元素
maven坐標(biāo)含有5個(gè)重要的屬性:
- groupid:定義當(dāng)前maven項(xiàng)目隸屬的實(shí)際項(xiàng)目
- artifactid:該元素定義該groupid項(xiàng)目的實(shí)際maven項(xiàng)目時(shí)什么,我們也可以簡(jiǎn)單的當(dāng)成是當(dāng)前項(xiàng)目的項(xiàng)目名稱。
- version:即當(dāng)前maven項(xiàng)目的版本號(hào),一個(gè)項(xiàng)目可能存在很多個(gè)版本,需要為其定義一個(gè)版本號(hào),同時(shí)也可以為了讓其他人可以找到或者特定使用這個(gè)版本。
- packing:該元素定義了maven項(xiàng)目的打包方式,比如jar,pom,war等
- classifier:該元素幫助定義構(gòu)建輸出的一些附屬構(gòu)件,比如一些主構(gòu)件可能帶有附屬構(gòu)件,如javadoc,source等附屬構(gòu)件
注:上述5個(gè)元素中,groupid、artifactid、version是必須定義的,packaging是可選的,classifier是不可直接定義的
3.3、maven的依賴
在沒(méi)有maven的階段,我們的java項(xiàng)目如果需要用到第三方的jar包,我們必須去其官網(wǎng)下載,打包到我們項(xiàng)目目錄中。而有了maven,我們就不需要這么做了。而是通過(guò)在maven項(xiàng)目的pom.xml文件中配置需要的第三方j(luò)ar的坐標(biāo)信息就可以了。而在pom.xml文件中配置了第三方j(luò)ar的坐標(biāo)信息的過(guò)程,我們就稱為配置依賴,所以我們也可以簡(jiǎn)單的把該jar的配置信息或者說(shuō)所需的第三方j(luò)ar包就稱為當(dāng)前maven項(xiàng)目的依賴。
3.4maven的依賴配置和七個(gè)元素
那我們要怎么配置項(xiàng)目的依賴呢?
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.0.4.RELEASE</version>
</dependency>
</dependencies>
這就是一個(gè)maven的依賴,依賴所指向的jar包就是spring-boot-starter-web-2.0.4.RELEASE.jar。我們從標(biāo)簽意思也能很容易看出來(lái):dependencies標(biāo)簽就是依賴集合的意思,該標(biāo)簽包的就是一個(gè)個(gè)的依賴。
依賴配置的7個(gè)元素:
- groupid:所屬項(xiàng)目,也可以簡(jiǎn)單的當(dāng)做是該項(xiàng)目的存放路徑或者包
- artifactid:某項(xiàng)目的唯一標(biāo)示,項(xiàng)目名&代號(hào)&簡(jiǎn)稱等
- version:依賴的版本
- type:依賴的類型,對(duì)應(yīng)項(xiàng)目坐標(biāo)定義的packaging,一般不需要聲明,默認(rèn)為jar
- scope:依賴范圍
- optional:標(biāo)記該依賴是否可選
- exclusions:用來(lái)排除傳遞性依賴
3.5依賴范圍
maven的6中依賴范圍:
- compile
編譯依賴范圍,是默認(rèn)的依賴范圍,如果此依賴范圍,該依賴對(duì)編譯,測(cè)試,運(yùn)行三種classpath都有效果,也及時(shí)該依賴在三個(gè)階段都會(huì)被導(dǎo)入 - test
測(cè)試依賴范圍,使用該依賴范圍的依賴,只對(duì)測(cè)試classpath才會(huì)有效,就比如上面的spring-boot-starter-test - provided
已提供依賴范圍,使用該依賴范圍則代表該依賴只對(duì)編譯和測(cè)試兩種classpath有效,在運(yùn)行時(shí)無(wú)效,這是什么意思的。比如說(shuō)servlet-api,編譯和測(cè)試項(xiàng)目時(shí)都需要該依賴,但是運(yùn)行項(xiàng)目時(shí)則不需要了,因?yàn)檫\(yùn)行容器已經(jīng)提供有該依賴了,就不需要Maven重復(fù)引用了,所以稱為已提供依賴范圍 - runtime
運(yùn)行時(shí)依賴范圍,使用此依賴范圍的依賴,對(duì)于測(cè)試和運(yùn)行classpath才有效,在編譯主代碼時(shí)是無(wú)效的。比如JDBC的驅(qū)動(dòng),編譯時(shí)只需要JDK提供的驅(qū)動(dòng)接口,只要在執(zhí)行測(cè)試和運(yùn)行時(shí)才會(huì)用上具體的JDBC驅(qū)動(dòng) - system
和provide的功能一致,但是有區(qū)別,使用system依賴范圍時(shí),依賴必須通過(guò)systemPath元素顯示地依賴文件的路徑,具有本地局限性,會(huì)造成構(gòu)建的不可移植性,謹(jǐn)慎使用 - import
導(dǎo)入依賴范圍,這個(gè)依賴范圍與其他依賴范圍有些不一樣,需要用在dependencyManagement標(biāo)簽中,說(shuō)明當(dāng)前含有dependencyManagement的Maven項(xiàng)目將dependencyManagement標(biāo)簽內(nèi)某個(gè)依賴導(dǎo)入進(jìn)本項(xiàng)目中
3.6、傳遞性依賴
為什么要有傳遞性依賴?
當(dāng)我們需要一個(gè)第三方j(luò)ar包時(shí),我們引入了它的依賴,但這個(gè)第三方j(luò)ar又依賴其他jar包怎么辦,我們就要手動(dòng)為它也依賴其依賴的jar包,這個(gè)操作非常的繁瑣,甚至我還要查它依賴那些jar包。所以就引入了傳遞性依賴機(jī)制。我依賴什么,就直接導(dǎo)入什么,而不關(guān)系它又依賴什么。也可以稱為是一種一站式服務(wù)吧。
傳遞性依賴的依賴范圍會(huì)受影響:
如果直接依賴的依賴范圍時(shí)runtime時(shí),但間接依賴的范圍時(shí)compile時(shí),依賴范圍有點(diǎn)沖突,那怎么辦?窄化依賴范圍,取交集,沒(méi)有交集則該傳遞性依賴無(wú)效。
3.7依賴調(diào)節(jié)
這是因?yàn)镸aven引用了傳遞性依賴的機(jī)制,這雖然大大的化簡(jiǎn)了我們的依賴聲明,但是也會(huì)造成一個(gè)依賴沖突。就比如說(shuō)我有個(gè)項(xiàng)目X,這個(gè)項(xiàng)目依賴了A和B
但A和B都同時(shí)依賴了一個(gè)C。
A依賴了C,B依賴了D,D又依賴了C,也就是說(shuō)A直接依賴C,B間接依賴C。
對(duì)于上面兩種情況,都說(shuō)明因?yàn)閭鬟f性依賴機(jī)制,所以我的項(xiàng)目會(huì)間接依賴同樣的jar包,但是這個(gè)jar包的依賴信息可能會(huì)不一致,比如版本不一致,依賴范圍不同等等。那我的項(xiàng)目間接依賴C到底是根據(jù)A來(lái)選呢?還是根據(jù)B來(lái)選呢?
所以Maven又提供了傳遞調(diào)解機(jī)制,這樣我們就能清楚的知道項(xiàng)目該引用那個(gè)傳遞性依賴:
路徑最近者優(yōu)先
路徑相同,聲明優(yōu)先
比如說(shuō)項(xiàng)目X的依賴關(guān)系是 :(1)X -> A -> C ,(2)X -> B -> D -> C。 我們知道(1)的依賴路勁為2,(2)的依賴路勁為3,所以根據(jù)依賴調(diào)解原則,路勁最近者優(yōu)先,所以我們的項(xiàng)目X對(duì)C的依賴取用(1)方案。
又比如說(shuō)項(xiàng)目X的依賴關(guān)系是:(1)X -> A -> C ,(2)X -> B -> C,此時(shí)的(1)和(2)對(duì)C的依賴路勁大小都是2,那么怎么辦呢?此時(shí)根據(jù)依賴調(diào)解原則,路徑相同,看誰(shuí)先聲明,所以我們就看直接依賴A和B誰(shuí)先聲明,我們就用誰(shuí)的傳遞依賴。
3.8可選依賴
簡(jiǎn)單點(diǎn)說(shuō)的話,就是我有兩個(gè)項(xiàng)目A和B,項(xiàng)目A依賴項(xiàng)目B,項(xiàng)目B中有slf4j的依賴包,是可選的依賴,即<optional>true</optional>。那么這樣的話,slf4j是不會(huì)成為項(xiàng)目A的傳遞性依賴的。因?yàn)閟lf4j是可選依賴。如果你想讓它成為項(xiàng)目A的傳遞依賴。只需要將optional項(xiàng)目去掉或改為false
3.9排除依賴
排除依賴也很好理解,平時(shí)我們看已有的Maven項(xiàng)目時(shí),也常常能看到<exclusion>,他就是排除依賴的標(biāo)簽。
他的作用是什么呢?就比如說(shuō)我們有一個(gè)項(xiàng)目X,它有兩個(gè)依賴A和B,A和B都有一個(gè)slf4j的依賴,版本是SNAPSHOT不穩(wěn)定版。所以我不想用他們的傳遞性依賴,想自己引用一個(gè)穩(wěn)定版本。所以我們就需要在項(xiàng)目X的pom.xml文件中引用A和B依賴時(shí),添加<exclusion>標(biāo)簽。總之就是用來(lái)排除不想引用的傳遞性依賴
四、maven的倉(cāng)庫(kù)
4.1什么是倉(cāng)庫(kù)
maven倉(cāng)庫(kù)就是一個(gè)存放jar文件的地方,所有的maven項(xiàng)目可以從同一個(gè)maven倉(cāng)庫(kù)中獲取自己所需要的依賴jar包,這節(jié)省了磁盤資源也方便了管理。每一個(gè)jar包文件都有自己的坐標(biāo),我們?cè)趐om根據(jù)坐標(biāo)就可以從倉(cāng)庫(kù)中下載對(duì)應(yīng)的jar包了。
4,2倉(cāng)庫(kù)的分類
- 中央倉(cāng)庫(kù):就是maven為全世界程序員免費(fèi)提供的公共遠(yuǎn)程倉(cāng)庫(kù),是默認(rèn)的倉(cāng)庫(kù),不需要配置,坐標(biāo)id為central。
- 本地倉(cāng)庫(kù),可以在setting.xml中配置。
五、maven的生命周期和插件
5.1什么是maven的生命周期
在maven之前,項(xiàng)目構(gòu)建的生命周期就存在了。例如軟件開發(fā)人員每天在對(duì)項(xiàng)目進(jìn)行清理,編譯,測(cè)試,部署就是一個(gè)項(xiàng)目構(gòu)建的過(guò)程,也就是項(xiàng)目構(gòu)建的生命周期。那么什么是maven的生命周期呢?maven的生命周期也類似,有清理、測(cè)試、編譯、打包、部署等等階段,是一個(gè)抽象的概念。
簡(jiǎn)而言之,maven的生命周期就是對(duì)項(xiàng)目進(jìn)行構(gòu)建過(guò)程,從生命階段開始,到什么時(shí)候結(jié)束。
實(shí)際上maven有三套完整而相互獨(dú)立的生命周期:
clean生命周期,default生命周期,site生命周期。
5.2clean生命周期
clean生命周期的目的是清理項(xiàng)目,它包括三個(gè)階段
- pre-clean 執(zhí)行一些清理前需要完成的工作
- clean 清理上一次構(gòu)建生成的文件
- post-clean 執(zhí)行一些清理后需要完成的工作
5.3 default生命周期
default生命周期定義了真正項(xiàng)目構(gòu)建時(shí)所需要執(zhí)行的所有步驟,它是三套生命周期中的核心。我們常用的有complile,test-complile,test,package,install,deploy。
validate
initialize
generate-sources
process-sources //處理項(xiàng)目主資源文件,對(duì)src/main/resources目錄的內(nèi)容復(fù)制到項(xiàng)目輸出的目錄下
generate-resources
process-resources
compile //編譯項(xiàng)目的主源碼,編譯src/main/jaav目錄下的Java文件到項(xiàng)目輸出目錄下
process-classes
generate-test-sources
process-test-sources //處理項(xiàng)目測(cè)試資源文件
generate-test-resources
process-test-resources
test-compile
process-test-classes
test //使用單元測(cè)試框架運(yùn)行測(cè)試,測(cè)試代碼不會(huì)被打包或部署
prepare-package
package //接受編譯好的代碼,打包成可發(fā)布的格式,如JAR
pre-integration-test
integration-test
post-integration-test
verify
install //將包安裝到Maven本地倉(cāng)庫(kù)中
deploy //將包復(fù)制遠(yuǎn)程倉(cāng)庫(kù)中
5.4site生命周期
site生命周期的目的是建立和發(fā)布項(xiàng)目的站點(diǎn),Maven能夠基于POM所包含的信息,自動(dòng)生成一個(gè)友好的站點(diǎn),方便團(tuán)隊(duì)交流和發(fā)布項(xiàng)目信息。該生命周期包含如下階段:
- pre-site //執(zhí)行一項(xiàng)在生成項(xiàng)目站點(diǎn)之前需要完成的工作
- site //生成項(xiàng)目站點(diǎn)文檔
- post-site //執(zhí)行一些在生成項(xiàng)目站點(diǎn)之后需要完成的工作
- site-deploy //將生成的站點(diǎn)發(fā)布到服務(wù)器上
5.5生命周期和命令行的關(guān)系
從命令行執(zhí)行maven任務(wù)的最主要方式就是調(diào)用maven的生命周期階段。也就是說(shuō),命令行調(diào)用的是抽象的maven生命周期階段,還有一個(gè)重要的特性是,雖然各個(gè)生命周期是相互獨(dú)立的,但是每個(gè)生命周期的階段是有前后依賴關(guān)系的。比如:
- mvn clean
該命令調(diào)用的是clean生命周期的clean階段。實(shí)際執(zhí)行的階段是pre-clean->clean - mvn test
該命令調(diào)用的是default生命周期的test階段,實(shí)際執(zhí)行的是default生命周期從validate,initialize到test的所有階段 - mvn clean deploy site-deploy
該命令調(diào)用的是三個(gè)生命周期的三個(gè)階段,實(shí)際執(zhí)行的是clean生命周期的pre-clean到clean階段,default生命周期的所有階段,site生命周期的所有階段
小結(jié): - mvn命令調(diào)用的是抽象的生命周期階段
- 只要你執(zhí)行某個(gè)生命周期的某個(gè)階段,那么它就必須從哪個(gè)生命周期的起始階段執(zhí)行到要執(zhí)行的那個(gè)階段
- mvn命令空開的參數(shù)代表一個(gè)生命周期的一個(gè)階段
5.6插件目標(biāo)goal
maven的核心僅僅定義了抽象的生命周期,具體的任務(wù)是交給插件完成的,插件以獨(dú)立的構(gòu)件形式存在,因此maven核心的分發(fā)包只有不要3mb的大小,maven會(huì)在需要的時(shí)候下載并使用插件。
每個(gè)插件都有很多個(gè)目標(biāo),每個(gè)目標(biāo)對(duì)應(yīng)一個(gè)功能,就比如說(shuō)maven-dependency-plugin有十幾個(gè)目標(biāo),分別對(duì)應(yīng)十幾個(gè)該插件的功能,比如dependency:analyze,dependency:tree等等。當(dāng)我們直接執(zhí)行mvn dependency:tree就是執(zhí)行的是某個(gè)插件具體的目標(biāo),也就是功能。
通用表達(dá)方式
mvn dependency:tree命令執(zhí)行時(shí)的maven-dependency-plugin的tree目標(biāo)。插件名前綴:目標(biāo)是一個(gè)通用的表達(dá)方式。
5.7生命周期和插件的關(guān)系
當(dāng)我們了解了生命周期之后,我們就會(huì)知道生命周期只是maven提供的一個(gè)抽象的概念,它實(shí)際上不會(huì)做些什么,那么誰(shuí)去做生命周期代表的事情呢?就是插件。
比如我們執(zhí)行mvn clean命令時(shí),他代表著clean生命周期的clean階段。具體做這個(gè)clean階段的事情的工具就是maven-clean-plugin插件。當(dāng)然還有很多插件maven-jar-plugin等等。默認(rèn)情況下,maven生命周期的階段都有內(nèi)置綁定,即都有對(duì)應(yīng)一個(gè)或多個(gè)插件去實(shí)現(xiàn)(也或者是一個(gè)插件的多個(gè)goal)。我們可以把生命周期和插件的關(guān)系比喻成,將某個(gè)插件的某個(gè)行為綁定到Maven的某個(gè)生命周期中,當(dāng)執(zhí)行maven的某個(gè)生命周期時(shí),被綁定在該階段的行為就會(huì)被觸發(fā)。
插件綁定
Maven的生命周期和插件互相綁定,用以完成實(shí)際的構(gòu)建任務(wù)。具體而言,是生命周期的階段和插件的目標(biāo)相互綁定,以完成某個(gè)具體的任務(wù)構(gòu)建。
例如編譯這項(xiàng)任務(wù),它對(duì)應(yīng)了default生命周期的compile階段,而maven-compiler-plugin這一插件的compile目標(biāo)能夠完成該任務(wù)。因?yàn)閷⑺麄兘壎ň湍芡瓿稍撊蝿?wù)。
六、maven的聚合和繼承關(guān)系
6.1繼承
為何需要繼承?
我們知道Maven工程之間可以完成依賴的傳遞性,實(shí)際上就是各個(gè)jar包和war包之間存在依賴的傳遞性,但是必須是compile范圍的依賴才具有傳遞性,才可以根據(jù)傳遞性統(tǒng)一的管理一個(gè)依賴的版本。而對(duì)于test范圍的依賴,只是孤零零的存在于某個(gè)項(xiàng)目中,各個(gè)項(xiàng)目中的依賴版本可能不同,容易造成問(wèn)題,所以test范圍的依賴的統(tǒng)一版本的問(wèn)題依靠依賴的傳遞性是無(wú)法解決的。所以我們使用繼承這個(gè)概念來(lái)解決。
我們?cè)谏弦徽?Maven 依賴 中提到了依賴的范圍概念,了解到j(luò)unit 依賴是test范圍的依賴,是不可以傳遞的,因此在多模塊項(xiàng)目中我們?cè)诿總€(gè)模塊都需要依賴。那么問(wèn)題來(lái)了既然是多模塊那就是不同的組甚至是不同的部門來(lái)開發(fā),junit依賴的version 很有可能就會(huì)不一樣,但是為了項(xiàng)目后期維護(hù)以及項(xiàng)目人員調(diào)度盡可能的方便,我們需要將junit的版本設(shè)為統(tǒng)一的。所以就需要有一個(gè)機(jī)制來(lái)給統(tǒng)一這個(gè)標(biāo)準(zhǔn),maven提供了一種機(jī)制就是 繼承。
如何實(shí)現(xiàn)繼承?
繼承,顧名思義,存在著父子關(guān)系。我們需要定義一個(gè)統(tǒng)一管理某些test范圍依賴的父工程,它打包的方式是pom。在它的pom文件中聲明test范圍的依賴的坐標(biāo)。然后在子工程中第一以<parent>坐標(biāo)</parent>的形式聲明父工程的坐標(biāo)并且聲明test范圍的依賴坐標(biāo),子工程中的也要聲明test范圍的依賴的坐標(biāo),但是需要注意的是必須將坐標(biāo)中的版本號(hào)給去掉,才可以完成統(tǒng)一管理test范圍依賴的版本。
62.聚合
為何需要聚合
我們最終都要講各個(gè)Maven工程安裝到倉(cāng)庫(kù)中,但是由于存在繼承關(guān)系使得我們必選先安裝父工程才可以安裝子工程,否則會(huì)報(bào)錯(cuò)。而且必須一個(gè)一個(gè)的install。那么能不能有一種更好的方式完成一鍵安裝呢?聚合工程就可以完成。
如何實(shí)現(xiàn)聚合?
我們首先要定義一個(gè)打包方式為pom的工程當(dāng)做聚合工程,并且在其中用<models><model></model></models>標(biāo)簽的形式將一個(gè)一個(gè)Maven工程聚合進(jìn)來(lái),不必在意在model中的順序,它會(huì)自動(dòng)識(shí)別父工程來(lái)先完成安裝。然后只有將這個(gè)聚合工程install那么其中聚合進(jìn)來(lái)的工程就都可以順利的install了。
需要注意的是:在實(shí)際項(xiàng)目開發(fā)工程中,我們可以使用同一個(gè)pom打包方式的工程來(lái)充當(dāng)父工程和聚合工程。即效果是其中的pom.xml文件包含test范圍的依賴和models標(biāo)簽將各個(gè)Maven工程聚合進(jìn)來(lái)。