Maven實(shí)戰(zhàn)-許曉斌-機(jī)械工業(yè)出版社

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

1ZQxT1.png

聚合(多模塊)與繼承

聚合(多模塊):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:略

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容