Maven簡(jiǎn)介
maven這個(gè)詞可以翻譯為“知識(shí)的積累”或者“專家”。
Maven能幫我們做的事:
- 項(xiàng)目構(gòu)建:Maven抽象了一個(gè)完整的“構(gòu)建生命周期”模型,這個(gè)模型吸取了大量其他的構(gòu)建腳本和構(gòu)建工具的優(yōu)點(diǎn)。
- 依賴管理
- 項(xiàng)目信息管理
構(gòu)建工具對(duì)比:
- make:由目標(biāo)、依賴、命令構(gòu)成,Makefile驅(qū)動(dòng)。命令依賴于系統(tǒng),無(wú)法跨平臺(tái)。
- Ant:使用Java編寫(xiě),可以跨平臺(tái),和make類似由目標(biāo)、依賴、任務(wù)構(gòu)成,build.xml驅(qū)動(dòng)。如目標(biāo)為jar打包一個(gè)項(xiàng)目,依賴于compile編譯一個(gè)項(xiàng)目,任務(wù)就是編譯為后的打包操作。
- Maven:以上兩種最大的問(wèn)題就是每個(gè)項(xiàng)目都要重新編寫(xiě)Makefile、build.xml驅(qū)動(dòng)文件,不能管理jar包依賴(Ant可以集成Ivy進(jìn)行依賴管理)。Maven很好的解決了這兩個(gè)問(wèn)題。
Maven核心概念:
- 坐標(biāo)和依賴
- 倉(cāng)庫(kù)
- 生命周期
- 插件
Maven的安裝與配置
mvn是一個(gè)shell腳本,執(zhí)行mvn命令時(shí)最終調(diào)用的是Java來(lái)解析。
用戶目錄下的.m2目錄一般存放:repository參考,和用戶設(shè)置文件settings.xml(可以從maven安裝目錄下的conf/settings.xml復(fù)制并修改)
不要使用IDE內(nèi)嵌的Maven版本:
- IDE內(nèi)嵌的Maven一般比較新,可能不穩(wěn)定
- 如果使用IDE內(nèi)嵌的Maven,其跟命令行下的mvn命令使用的就不是同一個(gè)maven,會(huì)為構(gòu)建帶來(lái)麻煩。
Maven使用入門(mén)
就像Make的Makefile一樣、Ant的build.xml一樣,Maven項(xiàng)目的核心是pom.xml文件。創(chuàng)建Mavne工程的步驟為:
- 創(chuàng)建pom.xml文件
- 按maven規(guī)范創(chuàng)建src/main/java工程結(jié)構(gòu)
Maven的pom.xml工程定義的坐標(biāo),最佳實(shí)踐:
- groupId最好填寫(xiě)公司項(xiàng)目名稱,artifactId填寫(xiě)模塊名稱中間加短劃線。這樣同一個(gè)公司的項(xiàng)目mvn install會(huì)安裝到同一個(gè)目錄下
- 而主包名最好加模塊名不要短劃線,如這里的主包名為io.zebinh.hellodemo.hellomaven
- maven package如果需要生成可運(yùn)行的jar包,是要加入maven-shade-plugin插件
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>io.zebinh.hellodemo</groupId>
<artifactId>hello-maven</artifactId>
<version>1.0-SNAPSHOT</version>
<name>hello maven demo</name>
</project>
背景案例
設(shè)計(jì)一個(gè)注冊(cè)登錄系統(tǒng)
坐標(biāo)和依賴
maven的坐標(biāo)包括:groupId, artifactId, version, packaging, classifier
- groupId:groupId不應(yīng)該只定義到公司級(jí)別,而應(yīng)該定義到項(xiàng)目級(jí)別。
- artifactId:模塊名
- version:nexus定義了一套版本管理,遵循該版本管理即可
- packaging:打包方式,如jar、war
- classifier:定義附屬構(gòu)建,如javadoc和source等,一般用于指定該jar包是基于哪個(gè)jdk版本構(gòu)建的,classifier最終會(huì)加到j(luò)ar包名字中,如<classifier>jdk15</classifier>,則最終jar包名會(huì)為artifact-version-jdk15.jar
引入依賴時(shí),除了一般的groupId, artifactId, version外,可以有以下一些額外的配置。
- scope:依賴的范圍
- optional:標(biāo)記依賴是否可選,可選依賴被引入時(shí)無(wú)法傳遞
- exclusions:用來(lái)排除傳遞性依賴
依賴范圍:引入依賴時(shí),可以使用<scope></scope>指定依賴范圍。Maven在編譯、測(cè)試、運(yùn)行時(shí)都會(huì)指定不同的classpath。如下:
- compile:編譯依賴范圍,默認(rèn)的范圍。此范圍對(duì)編譯、測(cè)試、運(yùn)行時(shí)都有效。
- test:測(cè)試依賴范圍,使用此依賴范圍的maven依賴,只對(duì)測(cè)試classpath有效。在編譯主代碼(非src/test/java)和運(yùn)行項(xiàng)目時(shí)不會(huì)引入此依賴。如Junit。如果在src/main/java中使用了junit的類,則報(bào)無(wú)法找到該類。
- provided:已提供依賴范圍,對(duì)于編譯和測(cè)試classpath有效,對(duì)運(yùn)行時(shí)classpath無(wú)效。如servlet-api這些tomcat等容器已提供,不需要maven再引入了。
- runtime:運(yùn)行時(shí)依賴范圍,對(duì)于測(cè)試和運(yùn)行時(shí)有效。如Jdbc驅(qū)動(dòng),項(xiàng)目編譯時(shí)使用的是接口,不需具體實(shí)現(xiàn)。
- system:與provided類似,但system不是引用的遠(yuǎn)程倉(cāng)庫(kù),而是引用本地路徑,使用system時(shí)需要配置一個(gè)本地要導(dǎo)入的jar包路徑。
依賴傳遞:maven引入依賴時(shí),會(huì)自動(dòng)引入該依賴對(duì)應(yīng)的依賴。如果b,c依賴都引入了d依賴的不同版本,則優(yōu)先引入依賴鏈較短的d依賴版本。若依賴鏈等長(zhǎng),則優(yōu)先引入pom.xml中排在前面的b依賴對(duì)應(yīng)的d依賴版本
依賴歸類:正如代碼中相同的字面量要提取出一個(gè)常量名一般,maven中使用屬性值來(lái)統(tǒng)一管理版本號(hào)
倉(cāng)庫(kù)
私服:私服是一種特殊的遠(yuǎn)程倉(cāng)庫(kù),當(dāng)maven需要下載構(gòu)件時(shí),它請(qǐng)求私服,如果私服上不存在該構(gòu)件,則從外部的遠(yuǎn)程倉(cāng)庫(kù)下載,緩存在私服之上,再為maven的下載請(qǐng)求提供服務(wù)。此外,一些無(wú)法從外部倉(cāng)庫(kù)下載到的構(gòu)件,也能上傳到私服供局域網(wǎng)內(nèi)的用戶使用。
遠(yuǎn)程倉(cāng)庫(kù)的配置和認(rèn)證:可以在pom文件中配置<repositories>結(jié)點(diǎn)配置遠(yuǎn)程倉(cāng)庫(kù),但如果遠(yuǎn)程倉(cāng)庫(kù)需要用戶名和密碼認(rèn)證,則同時(shí)需要在settings.xml文件中配置<server>結(jié)點(diǎn)配置用戶名和密碼。settings.xml中<server>下的id必須與<repositories>下的id一致。
部署到私服:可以使用mvn命令部署構(gòu)件到私服,需在pom文件中配置<distributionManagement>結(jié)點(diǎn),該結(jié)點(diǎn)配置了發(fā)布版和快照版兩個(gè)倉(cāng)庫(kù)。快照版本的構(gòu)件會(huì)自動(dòng)部署到快照倉(cāng)庫(kù)。
快照版本:為什么要有快照版本的構(gòu)件?協(xié)同開(kāi)發(fā)時(shí),如果使用快照版本,maven會(huì)自動(dòng)為快照版本的構(gòu)件添加時(shí)間戳,如artifactId-version-20200101-221414-13.jar表示2020年1月1日22點(diǎn)14分14秒的第13次快照。根據(jù)maven的更新策略<updatePolicy>默認(rèn)每天自動(dòng)更新快照版本到本地倉(cāng)庫(kù),如果使用穩(wěn)定版,則如果本地倉(cāng)庫(kù)中存在該版本的構(gòu)件,是不會(huì)自動(dòng)更新構(gòu)件的。
maven從倉(cāng)庫(kù)解析依賴:maven所有的倉(cāng)庫(kù)下的構(gòu)件目錄,都包含了表示當(dāng)前構(gòu)件信息的maven-metadata.xml文件。當(dāng)使用快照版本時(shí),maven會(huì)對(duì)比所有倉(cāng)庫(kù)下的maven-metadata.xml信息,選出最新的快照下載到本地使用。
鏡像:在settings.xml配置<mirror>結(jié)點(diǎn),其中<mirrorOf>指向國(guó)外網(wǎng)速較慢的倉(cāng)庫(kù)id,如中央倉(cāng)庫(kù)central,所有對(duì)于central的訪問(wèn)都重定向到了這個(gè)mirror。
生命周期與插件
maven的生命周期是抽象的,其實(shí)際行為都用插件來(lái)完成。
生命周期:在maven出現(xiàn)之前,項(xiàng)目構(gòu)件的生命周期就已經(jīng)存在,軟件開(kāi)發(fā)人員每天對(duì)項(xiàng)目進(jìn)行清理、編譯、測(cè)試和部署。maven的生命周期抽象了構(gòu)建的各個(gè)步驟,定義了它們的次序,但沒(méi)有提供具體實(shí)現(xiàn),而是由插件來(lái)實(shí)現(xiàn)。
三套生命周期:
- clean:清理項(xiàng)目
- default:構(gòu)建項(xiàng)目
- site:建立項(xiàng)目站點(diǎn)
插件目標(biāo):一個(gè)插件能執(zhí)行很多同類的功能,如maven-dependency-plugin,它能夠分析項(xiàng)目依賴、列出項(xiàng)目依賴樹(shù)等等。所有的這些功能,表示為一個(gè)插件目標(biāo),maven-dependency-plugin有10幾個(gè)目標(biāo),如dependency:analyze等。
插件綁定:maven的生命周期的階段是和插件目標(biāo)相互綁定的。default生命周期的很多階段是默認(rèn)和插件綁定的,有些階段沒(méi)有綁定任何插件,因此沒(méi)有任何實(shí)際行動(dòng)。
自定義綁定:用戶可以在<build>結(jié)點(diǎn)下的<plugin>下執(zhí)行指定某插件綁定到具體的生命周期。
插件配置:有兩種方式
- 命令行:執(zhí)行maven命令時(shí)還可以帶參數(shù),-D加參數(shù)即可。如執(zhí)行mvn package時(shí)maven會(huì)執(zhí)行單元測(cè)試,maven-surefire-plugin提供了maven.test.skip參數(shù)可以跳過(guò)單元測(cè)試,如
mvn package -Dmaven.test.skip=true。-D是java命令自帶的。 - pom配置:在<build><plugins><plugin><configuration>結(jié)點(diǎn)下進(jìn)行配置。
從命令行調(diào)用插件:插件是生命周期的具體實(shí)現(xiàn),當(dāng)然可以直接使用插件來(lái)進(jìn)行項(xiàng)目構(gòu)建了。如mvn dependency:tree調(diào)用maven-dependency-plugin的tree目標(biāo),其實(shí)該命令的全寫(xiě)法為:mvn org.apache.maven.plugins:maven-dependency-plugin:2.1:tree,為了使得命令更簡(jiǎn)介,maven引入了目標(biāo)前綴的概念,dependency是maven-dependency-plugin的前綴。
插件解析機(jī)制
插件和依賴一樣,也有自己的遠(yuǎn)程倉(cāng)庫(kù),默認(rèn)是中央倉(cāng)庫(kù)。當(dāng)插件時(shí)官方插件時(shí),即groupId為org.apache.maven.plugins時(shí),則可以省略groupId。當(dāng)使用插件前綴時(shí),maven會(huì)搜索倉(cāng)庫(kù)元數(shù)據(jù)下的配置groupId/maven-metadata.xml文件,解析得到插件前綴對(duì)于的全名。如下圖所示:clean前綴對(duì)應(yīng)的插件就是maven-clean-plugin

聚合(多模塊)與繼承
聚合(多模塊):maven可以允許構(gòu)建一個(gè)pom工程,管理其下的多個(gè)模塊,對(duì)主工程的命令會(huì)跑到其他的各個(gè)子模塊中運(yùn)行。
繼承:類似聚合模塊需要構(gòu)建一個(gè)pom工程,繼承也需要一個(gè)pom工程來(lái)使其他子模塊繼承。
父工程依賴管理:根據(jù)父工程元素可以繼承到子項(xiàng)目的特點(diǎn),我們可以將子項(xiàng)目中相同的依賴提取到父工程中,但會(huì)帶來(lái)一個(gè)問(wèn)題,即以后新加入的工程都會(huì)引入這些依賴。因此maven提供了<dependencyManagement>依賴管理來(lái)解決這個(gè)問(wèn)題。
父工程插件管理: 同上,父工程依賴管理。
聚合和繼承的區(qū)別:都是pom工程,除了pom.xml沒(méi)有其他內(nèi)容。聚合工程知道其下的所有子項(xiàng)目,子項(xiàng)目不知道聚合工程的存在。而繼承工程不知道其被哪個(gè)子項(xiàng)目繼承了,子項(xiàng)目卻知道其繼承自哪個(gè)父項(xiàng)目。
一般將父工程設(shè)置為聚合和繼承項(xiàng)目,融合聚合和繼承。
約定優(yōu)于配置:maven約定了目錄的結(jié)構(gòu),如:
- 源碼目錄:src/main/java
- 編譯輸出目錄:target/classes
這樣的好處是減少了配置,不用去通過(guò)配置告訴maven我的源碼目錄在什么地方。那么,在maven中約定是怎么體現(xiàn)的呢?他就是超級(jí)pom,所有的maven項(xiàng)目都繼承自超級(jí)pom,故超級(jí)pom就可以認(rèn)為是maven的約定。
反應(yīng)堆(Reactor):在多模塊的項(xiàng)目中,反應(yīng)堆指的是所有模塊組成的構(gòu)建結(jié)構(gòu)。因?yàn)槎嗄K間存在著繼承和聚合關(guān)系。maven提供了命令行指令進(jìn)行反應(yīng)堆裁剪,即只構(gòu)建某個(gè)模塊,mvn compile -pl <模塊名>
使用Nexus創(chuàng)建私服
無(wú)
使用Maven進(jìn)行測(cè)試
maven使用maven-surefire-plugin插件運(yùn)行測(cè)試用例,src/test/java下的以Test、TestCase結(jié)尾的類,maven會(huì)自動(dòng)運(yùn)行它們。
可以使用maven package -D skipTests命令跳過(guò)單元測(cè)試。使用maven package -D maven.test.skip=true連測(cè)試代碼的編譯也跳過(guò)了。
測(cè)試報(bào)告:surefire插件默認(rèn)在target/surefire-report目錄中輸出文本型和xml型的測(cè)試報(bào)告。前者是給人看,后者給工具解析,如eclipse、jenkins可以解析xml格式的文件。
測(cè)試覆蓋率報(bào)告:cobertura
使用hudson進(jìn)行集成測(cè)試
建議閱讀jenkins相關(guān)的書(shū)
使用Maven構(gòu)建web應(yīng)用
本章節(jié)主要講web應(yīng)用的war包部署,目前流行的springboot編寫(xiě)web應(yīng)用都是jar包格式了,本章較落后。本章可以跳過(guò)不看。
版本管理
無(wú)
靈活的構(gòu)建
Maven屬性:
- 自定義屬性,如<properties>標(biāo)簽
- 內(nèi)置屬性,如${basedir}代表pom.xml所在根目錄
- pom屬性:如${project.artifacted}代表pom.xml下的<project><artifacted>標(biāo)簽的值
- settings屬性,應(yīng)用settings.xml文件下的值,如${settings.localRepository}
- Java系統(tǒng)屬性,如${user.home},可以使用mvn help:system查看該類屬性
- 環(huán)境變量屬性:如${env.JAVA_HOME}
maven profile:跟springboot的多環(huán)境配置profile類似,一般開(kāi)發(fā)都是使用springboot的profile功能,maven這點(diǎn)功能可以不看。
生成項(xiàng)目站點(diǎn)
無(wú)
m2eclipse
本章是eclipse插件的使用,現(xiàn)在基本都是用idea了,可以跳過(guò)不看。
編寫(xiě)Maven插件
插件項(xiàng)目也是maven工程,打包類型<packaging>不是jar而是maven-plugin。需要繼承AbstractMojo類并實(shí)現(xiàn)其execute方法。
Archetype
Maven Archetype可以快速生成項(xiàng)目骨架,可以將其理解為Maven項(xiàng)目的模板。例如maven-archetype-quickstart就是最簡(jiǎn)單的Maven項(xiàng)目模板,其只提供基本的元素(如groupId, artifactId, version等)。很多著名的項(xiàng)目都提供了Archetype方便用戶創(chuàng)建項(xiàng)目。
maven-archetype-plugin:主要的ide都集成了該插件,mvn命令為mvn archetype:generate
常用的archetype:
- maven-archetype-quickstart:默認(rèn)的,構(gòu)建了一個(gè)基本的maven目錄骨架
- maven-archetype-webapp:包含src/main/webapp目錄
編寫(xiě)archetype:略