溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

每個(gè)程序員都該學(xué)會(huì)的Maven知識(shí),你都能運(yùn)用這些嗎?

發(fā)布時(shí)間:2020-07-13 14:10:51 來(lái)源:網(wǎng)絡(luò) 閱讀:261 作者:wx5d9ed7c8443c3 欄目:編程語(yǔ)言

以前的日子

以前我們寫(xiě)代碼時(shí),jar包都默認(rèn)放在一個(gè)叫 /lib 的目錄下,然后把該目錄設(shè)置為classpath可以讀取到的目錄,如下圖所示:

每個(gè)程序員都該學(xué)會(huì)的Maven知識(shí),你都能運(yùn)用這些嗎?

某一天我們新加了一個(gè)功能,需要用到一個(gè)比較古老的 z.jar 包,這時(shí)我們到網(wǎng)上去各種搜索,由于比較罕見(jiàn),最終在某個(gè) xxx軟件園 中找到了他。然后我們把 z.jar 包拷貝到 /lib 目錄下:

每個(gè)程序員都該學(xué)會(huì)的Maven知識(shí),你都能運(yùn)用這些嗎?

這時(shí)運(yùn)行后報(bào)了一堆的錯(cuò),原因是 z.jar 包有很多的依賴項(xiàng),分別是 z1.jar ,z2.jar , z3.jar。這時(shí)的你是否有種想要罵人的沖動(dòng)?但是沖動(dòng)歸沖動(dòng),代碼還是要寫(xiě),jar包還是要找,自己挖的坑,哭著也要填完啊。

歷經(jīng)千難萬(wàn)險(xiǎn),終于在1個(gè)博客中找到了 z1.jar , z2.jar , z3.jar 的下載鏈接,但是需要支付積分才能下載。。為了得到這些jar包,我們又注冊(cè)了一個(gè)賬號(hào),發(fā)了幾個(gè)評(píng)論,賺到足夠的積分后,終于把那三個(gè)jar包下載下來(lái)了。然后趕緊把他們拷貝到 /lib 下去:

每個(gè)程序員都該學(xué)會(huì)的Maven知識(shí),你都能運(yùn)用這些嗎?

湊夠了jar包之后,再次運(yùn)行項(xiàng)目,終于能成功運(yùn)行了,真是謝天謝地!

過(guò)了半個(gè)月,老板說(shuō)我們 項(xiàng)目A 非常不錯(cuò),現(xiàn)在我們準(zhǔn)備再啟動(dòng)一個(gè) 項(xiàng)目B 作為他的兄弟項(xiàng)目。這時(shí)你開(kāi)始搭建 項(xiàng)目B 的框架了,把所有需要用到的jar包從 項(xiàng)目A 拷貝到 項(xiàng)目B 中:

每個(gè)程序員都該學(xué)會(huì)的Maven知識(shí),你都能運(yùn)用這些嗎?

從此你又開(kāi)始了打怪升級(jí)的日子了。

現(xiàn)在的日子

隨著科技的發(fā)展,改革開(kāi)放的浪潮席卷了大地,也席卷了IT界,一大堆生產(chǎn)力工具被創(chuàng)造出來(lái)了,俗話說(shuō)的好,工欲善其事必先利其器。有了好的生產(chǎn)力工具,要做的事必定是事半功倍!而 Maven 就是一種為了解放我們程序猿的生產(chǎn)力工具。

程序猿在日常工作中需要用到大量的jar包,有的是框架包如:netty,sentinel等,有的是工具包如:hutool,有的是公司內(nèi)部的私有包如:xx-framework等等。

一個(gè)項(xiàng)目中可能充斥著各種各樣的jar包,如果我們用手工的方式去一個(gè)一個(gè)的管理的話,那樣就會(huì)迷失在jar包的海洋里,這時(shí)我們通過(guò) Maven 這種管理jar包的工具來(lái)幫助我們解決這個(gè)繁瑣又棘手的問(wèn)題,可以讓我們專(zhuān)心于自己的功能與業(yè)務(wù)。

其實(shí) Maven 是一套軟件工程管理和整合工具。他有很多的功能包括但不限于以下幾點(diǎn):

  • 工程的創(chuàng)建、構(gòu)建、測(cè)試
  • 依賴的管理
  • 倉(cāng)庫(kù)的管理
  • 自動(dòng)化部署
  • 。。。

我們?nèi)粘V杏玫淖疃嗟目赡芫褪枪こ膛c依賴的管理了,其他的用到的頻率不多。

有了 Maven 之后,我們:

  • 不需要為每個(gè)項(xiàng)目都創(chuàng)建一個(gè) /lib 目錄用來(lái)存放各種jar包了
  • 不需要為到哪去尋找我需要的jar包而發(fā)愁了
  • 不需要為引用的jar包去尋找他所依賴的jar包了
  • 。。。

結(jié)構(gòu)

下面是一個(gè)典型的maven項(xiàng)目的結(jié)構(gòu)圖:

每個(gè)程序員都該學(xué)會(huì)的Maven知識(shí),你都能運(yùn)用這些嗎?

倉(cāng)庫(kù)

在 Maven 的術(shù)語(yǔ)中,倉(cāng)庫(kù)是一個(gè)位置(place),例如目錄,可以存儲(chǔ)所有的工程 jar 文件、library jar 文 件、插件或任何其他的工程指定的文件。

嚴(yán)格意義上說(shuō),Maven 只有兩種類(lèi)型的倉(cāng)庫(kù):

  • 本地(local)
  • 遠(yuǎn)程(remote)

本地倉(cāng)庫(kù)

Maven 的本地倉(cāng)庫(kù)是機(jī)器上的一個(gè)文件夾。它在你第一次運(yùn)行任何 maven 命令的時(shí)候創(chuàng)建。

Maven 的本地倉(cāng)庫(kù)保存你的工程的所有依賴(library jar、plugin jar 等)。當(dāng)你運(yùn)行一次 Maven 構(gòu)建時(shí),Maven 會(huì)自動(dòng)下載所有依賴的 jar 文件到本地倉(cāng)庫(kù)中。它避免了每次構(gòu)建時(shí)都引用存放在遠(yuǎn)程倉(cāng)庫(kù)上的依賴文件。

Maven 的本地倉(cāng)庫(kù)默認(rèn)被創(chuàng)建在 ${user.home}/.m2/repository 目錄下。要修改默認(rèn)位置,只要在 settings.xml 文件中定義另一個(gè)路徑即可,例如:

<localRepository>/anotherDirectory/.m2/respository</localRepository>

遠(yuǎn)程倉(cāng)庫(kù)

Maven 的遠(yuǎn)程倉(cāng)庫(kù)可以是任何其他類(lèi)型的存儲(chǔ)庫(kù),可通過(guò)各種協(xié)議,例如 file://http:// 來(lái)訪問(wèn)。

這些存儲(chǔ)庫(kù)可以是由第三方提供的可供下載的遠(yuǎn)程倉(cāng)庫(kù),例如Maven 的中央倉(cāng)庫(kù)(central repository):

repo.maven.apache.org/maven2

uk.maven.org/maven2

也可以是在公司內(nèi)的FTP服務(wù)器或HTTP服務(wù)器上設(shè)置的內(nèi)部存儲(chǔ)庫(kù),用于在開(kāi)發(fā)團(tuán)隊(duì)和發(fā)布之間共享私有的 artifacts。

中央倉(cāng)庫(kù)

Maven 的中央倉(cāng)庫(kù)是 Maven 社區(qū)維護(hù)的,里面包含了大量常用的庫(kù),我們可以直接引用,但是前提是我們的項(xiàng)目能夠訪問(wèn)外網(wǎng)。

私有倉(cāng)庫(kù)

除了 Maven 的中央倉(cāng)庫(kù)外,還有一種就是私有倉(cāng)庫(kù),這種倉(cāng)庫(kù)通常都是企業(yè)內(nèi)部創(chuàng)建的一個(gè)私有庫(kù),用于一些內(nèi)部jar包的維護(hù)與共享。由于網(wǎng)絡(luò)原因和鑒于安全性的考慮,很多公司的內(nèi)外網(wǎng)是隔離的,要想直接訪問(wèn)中央倉(cāng)庫(kù)是不可能的,并且直接把內(nèi)部資源暴露在互聯(lián)網(wǎng)上也是非常危險(xiǎn)的,所以這時(shí)就需要?jiǎng)?chuàng)建一個(gè)私有庫(kù)。

那這些倉(cāng)庫(kù)之間的關(guān)系是怎樣的呢?或者說(shuō)一個(gè) Maven 項(xiàng)目想要獲取一個(gè)jar包的話,他該從哪個(gè)倉(cāng)庫(kù)中去獲取呢?下圖就是一個(gè)簡(jiǎn)單的描述:

每個(gè)程序員都該學(xué)會(huì)的Maven知識(shí),你都能運(yùn)用這些嗎?

首先 Maven 會(huì)到本地倉(cāng)庫(kù)中去尋找所需要的jar吧,如果找不到就會(huì)到配置的私有倉(cāng)庫(kù)中去找,如果私有倉(cāng)庫(kù)中也找不到的話,就會(huì)到配置的中央倉(cāng)庫(kù)中去找,如果還是找不到就會(huì)報(bào)錯(cuò)。但是這中間只要在某一個(gè)倉(cāng)庫(kù)中找到了就會(huì)返回了,除非倉(cāng)庫(kù)中有更新的版本,或者是snapshot版本。

那么 Maven 的遠(yuǎn)程倉(cāng)庫(kù)是怎么配置的呢?假設(shè)我們要配置一個(gè)中央倉(cāng)庫(kù),可以像下面這樣配置:

<project>
...
    <profiles>
        <profile>
              <id>central</id>  
            <repositories>
                <repository>
                    <id>Central</id>
                    <name>Central</name>
                    <url>http://repo.maven.apache.org/maven2/</url>
                </repository>
            </repositories>
        </profile>
    </profiles>
    <activeProfiles>
        <activeProfile>central</activeProfile>
    </activeProfiles>
...
</project>

最佳實(shí)踐

但是官方并不推薦直接配置遠(yuǎn)程倉(cāng)庫(kù),例如直接配置一個(gè)中央倉(cāng)庫(kù),而是通過(guò) 倉(cāng)庫(kù)管理器 來(lái)下載我們所需要的jar包。試想一下如果你所在的公司有幾千甚至上萬(wàn)的開(kāi)發(fā)者,每個(gè)人都單獨(dú)配置一個(gè)中央倉(cāng)庫(kù),那每個(gè)人都到中央倉(cāng)庫(kù)中去下載所需的jar,這就退化成最原始的模式,并且是一個(gè)巨大的資源浪費(fèi)。

那什么是 倉(cāng)庫(kù)管理器 呢?倉(cāng)庫(kù)管理器是一種專(zhuān)用服務(wù)器應(yīng)用程序,目的是用來(lái)管理二進(jìn)制組件的存儲(chǔ)庫(kù)。對(duì)于任何使用 Maven 的項(xiàng)目,倉(cāng)庫(kù)管理器的使用被認(rèn)為是必不可少的最佳實(shí)踐。

倉(cāng)庫(kù)管理器提供了以下基本用途:

  • 充當(dāng)中央Maven存儲(chǔ)庫(kù)的專(zhuān)用代理服務(wù)器
  • 提供存儲(chǔ)庫(kù)作為Maven項(xiàng)目輸出的部署目標(biāo)

使用倉(cāng)庫(kù)管理器可以獲得以下優(yōu)點(diǎn)和功能:

  • 顯著減少了遠(yuǎn)程存儲(chǔ)庫(kù)的下載次數(shù),節(jié)省了時(shí)間和帶寬,從而提高了構(gòu)建性能
  • 由于減少了對(duì)外部存儲(chǔ)庫(kù)的依賴,提高了構(gòu)建穩(wěn)定性
  • 與遠(yuǎn)程SNAPSHOT存儲(chǔ)庫(kù)交互的性能提高
  • 提供了一個(gè)有效的平臺(tái),用于在組織內(nèi)外交換二進(jìn)制工件,而無(wú)需從源代碼中構(gòu)建工件
  • 。。。

已知的開(kāi)源和商業(yè)存儲(chǔ)庫(kù)管理器有以下這些:

  • Apache Archiva(開(kāi)源)
  • CloudRepo(商業(yè))
  • Cloudsmith套餐(商業(yè))
  • JFrog Artifactory開(kāi)源(開(kāi)源)
  • JFrog Artifactory Pro(商業(yè))
  • Sonatype Nexus OSS(開(kāi)源)
  • Sonatype Nexus Pro(商業(yè))
  • packagecloud.io(商業(yè))

鏡像

Mirror 則相當(dāng)于一個(gè)代理,它會(huì)攔截去指定的遠(yuǎn)程 Repository 下載構(gòu)件的請(qǐng)求,然后從自己這里找出構(gòu)件回送給客戶端。配置 Mirror 的目的一般是出于網(wǎng)速考慮。

RepositoryMirror 是兩個(gè)不同的概念:前者本身是一個(gè)倉(cāng)庫(kù),可以堆外提供服務(wù),而后者本身并不是一個(gè)倉(cāng)庫(kù),它只是遠(yuǎn)程倉(cāng)庫(kù)的網(wǎng)絡(luò)加速器。

需要注意的是很多本地倉(cāng)庫(kù)搭建工具往往也提供 Mirror 服務(wù),比如Nexus就可以讓同一個(gè)URL,既用作 internalrepository,又使它成為所有 repositoryMirror。

如果 倉(cāng)庫(kù)X 可以提供 倉(cāng)庫(kù)Y 存儲(chǔ)的所有內(nèi)容,那么就可以認(rèn)為 X是Y的一個(gè)鏡像。這也意味著,任何一個(gè)可以從某個(gè)倉(cāng)庫(kù)中獲得的構(gòu)件,都可以從它的鏡像中獲取。

舉個(gè)例子:http://maven.net.cn/content/groups/public/ 是中央倉(cāng)庫(kù) http://repo1.maven.org/maven2/ 在中國(guó)的鏡像,由于地理位置的因素,該鏡像往往能夠提供比中央倉(cāng)庫(kù)更快的服務(wù)。

因此,可以在Maven中配置該鏡像來(lái)替代中央倉(cāng)庫(kù)。在settings.xml中配置如下代碼:

<settings>
  ...
  <mirrors>
    <mirror>
      <id>maven.net.cn</id>
      <mirrorOf>central</mirrorOf>
      <name>one of the central mirrors in china</name>
      <url>http://maven.net.cn/content/groups/public/</url>
      </mirror>
  </mirrors>
  ...
</settings>

\的值為central,表示該鏡像是中央倉(cāng)庫(kù)的鏡像,任何對(duì)于中央倉(cāng)庫(kù)的請(qǐng)求都會(huì)轉(zhuǎn)至該鏡像,如下圖所示:

每個(gè)程序員都該學(xué)會(huì)的Maven知識(shí),你都能運(yùn)用這些嗎?

對(duì)于鏡像的最佳實(shí)踐是結(jié)合私服。由于私服可以代理任何外部的公共倉(cāng)庫(kù)(包括中央倉(cāng)庫(kù)),因此,對(duì)于組織內(nèi)部的Maven用戶來(lái)說(shuō),使用一個(gè)私服地址就等于使用了所有需要的外部倉(cāng)庫(kù),這可以將配置集中到私服,從而簡(jiǎn)化Maven本身的配置。在這種情況下,任何需要的構(gòu)件都可以從私服獲得,私服就是所有倉(cāng)庫(kù)的鏡像。

例如可以這樣來(lái)配置一個(gè)代理所有倉(cāng)庫(kù)的鏡像:

<settings>
  ...
  <mirrors>
    <mirror>
      <id>internal-repository</id>
      <name>Internal Repository Manager</name>
      <url>internal-repository-url</url>
      <mirrorOf>*</mirrorOf>
    </mirror>
  </mirrors>
  ...
</settings>

\的值為星號(hào),表示該鏡像是所有Maven倉(cāng)庫(kù)的鏡像,任何對(duì)于遠(yuǎn)程倉(cāng)庫(kù)的請(qǐng)求都會(huì)被轉(zhuǎn)至: internal-repository-url 這個(gè)地址。

下面給出一張 Maven 官方的架構(gòu)圖:

每個(gè)程序員都該學(xué)會(huì)的Maven知識(shí),你都能運(yùn)用這些嗎?

生命周期

生命周期是由 一組順序階段 構(gòu)成的一個(gè)整體,這么說(shuō)可能有點(diǎn)繞,那讓我們來(lái)關(guān)注他里面的幾個(gè)重要的點(diǎn):

  • 一組:指的是可能有多個(gè)
  • 順序:指的是按照順序執(zhí)行,執(zhí)行某一個(gè)階段的指令時(shí)會(huì)依次先執(zhí)行該階段之前的指令
  • 階段:指的是具體要執(zhí)行的內(nèi)容

例如 Maven 有三個(gè)內(nèi)置的構(gòu)建生命周期: defaultcleansite。每個(gè)生命周期都由一系列的階段所構(gòu)成,比如 default 生命周期的一個(gè)簡(jiǎn)易階段如下,完整的生命周期請(qǐng)參考官方文檔:

每個(gè)程序員都該學(xué)會(huì)的Maven知識(shí),你都能運(yùn)用這些嗎?

上圖中的每一個(gè)節(jié)點(diǎn)都是一個(gè) 階段 ,階段的執(zhí)行是按順序的,一個(gè)階段執(zhí)行完成之后才會(huì)執(zhí)行下一個(gè)階段。比如我們執(zhí)行了一個(gè)如下的指令:

mvn install

他實(shí)際會(huì)執(zhí)行 install 階段之前的所有階段,然后才會(huì)執(zhí)行 install 階段本身。

PS:當(dāng)我們的項(xiàng)目是多模塊的,我們?cè)谧铐攲訄?zhí)行該指令時(shí),Maven 會(huì)遍歷每一個(gè)子模塊,依次執(zhí)行所有的階段。

坐標(biāo)

說(shuō)到 Maven 的坐標(biāo),我們首先就需要想到 GAV ,即 groupId artifactId version。由這三個(gè)屬性就可以唯一確定一個(gè)jar包了。其中每個(gè)屬性的意義如下:

  • groupId:表示一個(gè)團(tuán)體,可以是公司、組織等
  • artifactId:表示團(tuán)體下的某個(gè)項(xiàng)目
  • version:表示某個(gè)項(xiàng)目的版本號(hào)

他們之間的關(guān)系是一對(duì)多的,即每個(gè)團(tuán)體下可以有多個(gè)項(xiàng)目,每個(gè)項(xiàng)目可以有多個(gè)版本號(hào),可以用下面這張圖來(lái)表示:

每個(gè)程序員都該學(xué)會(huì)的Maven知識(shí),你都能運(yùn)用這些嗎?

依賴

Maven 核心特點(diǎn)之一是依賴管理。一旦我們開(kāi)始處理多模塊工程(包含數(shù)百個(gè)子模塊或者子工程)的時(shí)候,模塊 間的依賴關(guān)系就變得非常復(fù)雜,管理也變得很困難。針對(duì)此種情形,Maven 提供了一種高度控制的方法。

依賴傳遞

依賴傳遞很好理解,假設(shè) B 依賴于 C,當(dāng) A 需要依賴 B 時(shí),則 A 自動(dòng)獲得了對(duì) C 的依賴。依賴傳遞有時(shí)非常好,當(dāng)我們需要依賴很多jar包時(shí),我們可以聲明一個(gè)包來(lái)依賴所有的jar,然后只要依賴這個(gè)包就可以了。但是有時(shí)又很麻煩,因?yàn)楹芸赡軙?huì)造成依賴的沖突。

依賴沖突

當(dāng)同一個(gè)項(xiàng)目中由于不同的jar包依賴了相同的jar包,此時(shí)就會(huì)發(fā)生依賴沖突的情況,如下圖所示:

每個(gè)程序員都該學(xué)會(huì)的Maven知識(shí),你都能運(yùn)用這些嗎?

當(dāng)項(xiàng)目中依賴了a和c,而a和c都依賴了b,這時(shí)就造成了沖突。為了避免沖突的產(chǎn)生,Maven 使用了兩種策略來(lái)解決沖突,分別是 短路優(yōu)先聲明優(yōu)先 。

短路優(yōu)先

短路優(yōu)先的意識(shí)是,從項(xiàng)目一直到最終依賴的jar的距離,哪個(gè)距離短就依賴哪個(gè),距離長(zhǎng)的將被忽略掉。例如下圖所示:

每個(gè)程序員都該學(xué)會(huì)的Maven知識(shí),你都能運(yùn)用這些嗎?

聲明優(yōu)先

聲明優(yōu)先的意思是,通過(guò)jar包聲明的順序來(lái)決定使用哪個(gè),最先聲明的jar包總是被選中,后聲明的jar包則會(huì)被忽略,如下圖所示:

每個(gè)程序員都該學(xué)會(huì)的Maven知識(shí),你都能運(yùn)用這些嗎?

依賴排除

如果我們只想引用我們直接依賴的jar包,而不想把間接依賴的jar包也引入的話,那可以使用依賴排除的方式,將間接引用的jar包排除掉,如下面的配置所示:

<exclusions>
    <exclusion>
        <groupId>excluded.groupId</groupId>
        <artifactId>excluded-artifactId</artifactId>
    </exclusion>
</exclusions>

解決沖突

項(xiàng)目中出現(xiàn)沖突,大體都是因?yàn)樯厦嫠枋龅脑?,然?Maven 在選擇jar包時(shí),選擇了一個(gè)錯(cuò)的包,導(dǎo)致出現(xiàn)問(wèn)題,這時(shí)我們就需要人為來(lái)干預(yù)他,告訴 Maven 使用哪個(gè)正取的包。下面讓我舉個(gè)例子來(lái)說(shuō)明沖突產(chǎn)生后該如何解決。

我們?cè)具\(yùn)行得好好的一個(gè)項(xiàng)目,突然有一次啟動(dòng)的時(shí)候,報(bào)錯(cuò)了,如下圖所示:

每個(gè)程序員都該學(xué)會(huì)的Maven知識(shí),你都能運(yùn)用這些嗎?

可以看到有報(bào)了一個(gè) NoSuchMethodError ,看到這個(gè)錯(cuò),多半都是因?yàn)闆_突導(dǎo)致的。

錯(cuò)誤說(shuō)的是找不到 javax.servlet.ServletContext 類(lèi)中的 getVirtualServerName 方法了,那我們?cè)?idea 中搜索一下 javax.servlet.ServletContext 類(lèi),看看是否存在多個(gè)的情況,如下圖所示:

每個(gè)程序員都該學(xué)會(huì)的Maven知識(shí),你都能運(yùn)用這些嗎?

可以發(fā)現(xiàn)確實(shí)在兩個(gè)jar包中都找到了 javax.servlet.ServletContext 這個(gè)類(lèi),那我們打開(kāi)他們看看哪個(gè)類(lèi)沒(méi)有我們需要方法:

每個(gè)程序員都該學(xué)會(huì)的Maven知識(shí),你都能運(yùn)用這些嗎?

可以很清楚的看到,在 servlet-api-3.0.jar 包中沒(méi)有找到我們需要的方法,而 Maven 肯定是選擇了這個(gè)包。那就讓我們來(lái)看下依賴樹(shù)吧,看看 Maven 是怎樣選擇了錯(cuò)誤的包的。

在項(xiàng)目目錄中輸入如下指令:

mvn dependency:tree -Dverbose -Dincludes=javax.servlet:servlet-api

Maven 將打印出 servlet-api-3.0.jar 的包的依賴樹(shù),如下圖所示:

每個(gè)程序員都該學(xué)會(huì)的Maven知識(shí),你都能運(yùn)用這些嗎?

然后在輸入如下指令:

mvn dependency:tree -Dverbose  -Dincludes=org.apache.tomcat.embed:tomcat-embed-core`

Maven 將打印出 tomcat-embed-core-8.5.31.jar 的包的依賴樹(shù),如下圖所示:

每個(gè)程序員都該學(xué)會(huì)的Maven知識(shí),你都能運(yùn)用這些嗎?

我們分析下原因,從 Maven 中打印出的依賴樹(shù)來(lái)看,發(fā)現(xiàn)很奇怪的事:

servlet-api-3.0.jar 包是在 xx-service 模塊中引入的,從 xx-web 到他的深度為6,

tomcat-embed-core-8.5.31.jar 包是在 xx-web 模塊中引入的,從 xx-web 到他的深度為3。

那按照短路優(yōu)先的規(guī)則,Maven 應(yīng)該會(huì)選擇 tomcat-embed-core-8.5.31.jar 包才對(duì),現(xiàn)在沒(méi)有選擇他,那原因肯定只有一個(gè)了:聲明優(yōu)先!

說(shuō)明 servlet-api-3.0.jar 包比 tomcat-embed-core-8.5.31.jar 包先聲明。

我們發(fā)現(xiàn) servlet-api-3.0.jar 包是在 xx-service 中被引入的,

tomcat-embed-core-8.5.31.jar 是在 spring-boot-starter-web 中被引入的。

那只要能證明在 xx-webxx-service 先于 spring-boot-starter-web 聲明就可以了,讓我們?nèi)?xx-web 中看看:

每個(gè)程序員都該學(xué)會(huì)的Maven知識(shí),你都能運(yùn)用這些嗎?

事實(shí)證明了我們的猜想是正確的, xx-service 確實(shí)比 spring-boot-starter-web 先聲明!

可以用圖形表示成如下:

每個(gè)程序員都該學(xué)會(huì)的Maven知識(shí),你都能運(yùn)用這些嗎?

那知道原因了,要解決這個(gè)沖突,就很好辦了,有兩種方法:

  • 在?xx-web?中將?xx-service?放到?spring-boot-starter-web?后面聲明

  • 在?xx-service?中找到引入?servlet-api-3.0.jar?包將他排除掉

依賴管理

聚合

將多個(gè)項(xiàng)目同時(shí)運(yùn)行就稱(chēng)為聚合,如下就是一個(gè)多模塊的項(xiàng)目:

<packaging>pom</packaging>
 <modules>
     <module>module-1</module>
     <module>module-2</module>
     <module>module-3</module>
 </modules>

聚合的優(yōu)勢(shì)在于可以在一個(gè)地方編譯多個(gè) pom 文件。

PS:聚合時(shí) packaging 必須要是 pom

繼承

跟java類(lèi)的繼承類(lèi)似,Maven 的繼承特性也會(huì)繼承父pom中的依賴,假設(shè)我們定義了一個(gè)父pom:

<groupId>com.houyi</groupId>
<artifactId>maven-parent</artifactId>
 <version>0.0.1-SNAPSHOT</version>
<dependencyManagement>
   <dependencies>
        <dependency>
            <groupId>junit</groupId> 
            <artifactId>junit</artifactId> 
            <version>${junit.version}</version> 
            <scope>test</scope>
      </dependency>
      <dependency>
            <groupId>mysql</groupId> 
              <artifactId>mysql-connector-java</artifactId> 
              <version>5.1.30</version>
      </dependency>
   </dependencies>
</dependencyManagement>

然后在子pom中引入這個(gè)父pom:

<!-- 指定parent,說(shuō)明是從哪個(gè)pom繼承 -->
<parent>
    <groupId>com.houyi</groupId> 
    <artifactId>maven-parent</artifactId> 
    <version>0.0.1-SNAPSHOT</version>
    <!-- 指定相對(duì)路徑 --> 
    <relativePath>../maven-parent</relativePath></parent>

<!-- 只需要指明groupId + artifactId,就可以到父pom找到了,無(wú)需指明版本 -->
<dependencies>
  <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId> 
  </dependency>
  <dependency>
        <groupId>mysql</groupId> 
          <artifactId>mysql-connector-java</artifactId>
   </dependency>
</dependencies>

使用dependencyManagement,可對(duì)依賴進(jìn)行管理。子類(lèi)只要不引用這個(gè)里面寫(xiě)的groupId + artifactId,則不會(huì)添加依賴,這樣防止造成重復(fù)加了包:如果不使用dependencyManagement,那么只要寫(xiě)了dependency,子pom中會(huì)全部添加到依賴中,而其中很多包可能都用不上。

插件

插件是 Maven 的核心,所有執(zhí)行的操作都是基于插件來(lái)完成的。

為了讓一個(gè)插件中可以實(shí)現(xiàn)眾多的相類(lèi)似的功能,Maven 為插件設(shè)定了目標(biāo),一個(gè)插件中有可能有多個(gè)目標(biāo)。其實(shí)生命周期中的每個(gè)階段都是由插件的一個(gè)具體目標(biāo)來(lái)執(zhí)行的

例如可以用下面的方式配置一個(gè)插件:

<build>
   <plugins>
        <plugin>
             <groupId>org.apache.maven.plugins</groupId>
             <artifactId>maven-source-plugin</artifactId> 
             <version>2.2.1</version>
            <!-- 配置執(zhí)行 -->
            <executions>
                 <execution>
                     <phase>package</phase>
                     <goals>
                        <goal>jar-no-fork</goal>
                     </goals>
                 </execution>
              </executions>
          </plugin>
       </plugins>
</build>

配置目標(biāo) goal 的目的是:這樣在執(zhí)行 mvnpackage 的時(shí)候,就會(huì)自動(dòng)執(zhí)行 mvn source:jar-no-fork了,jar-no-fork這個(gè)目標(biāo)是用來(lái)進(jìn)行源碼打包的。

除了可以在build元素中配置插件,當(dāng)然也可以在parent項(xiàng)目中,用pluginManagement來(lái)配置,然后在子項(xiàng)目繼承即可使用。

PS:通過(guò)插件我們可以做很多事,比如通過(guò)mybatis-generator 我們可以生成很多DAO層的代碼,再配合通用Mapper+lombok使用的話就可以使你的代碼非常簡(jiǎn)潔,絕對(duì)的生產(chǎn)力工具!

指令

下面列舉一些常用的 maven 指令:

每個(gè)程序員都該學(xué)會(huì)的Maven知識(shí),你都能運(yùn)用這些嗎?

指令參數(shù)

上面列舉的只是比較通用的命令,其實(shí)很多命令都可以攜帶參數(shù)以執(zhí)行更精準(zhǔn)的任務(wù)。 Maven命令可攜帶的參數(shù)類(lèi)型如下:

-D 傳入屬性參數(shù)

比如命令: mvnpackage-Dmaven.test.skip=true-D開(kāi)頭,將 maven.test.skip 的值設(shè)為 true ,就是告訴maven打包的時(shí)候跳過(guò)單元測(cè)試。

同理, mvn deploy-Dmaven.test.skip=true 代表部署項(xiàng)目并跳過(guò)單元測(cè)試。

-P 使用指定的Profile配置

比如項(xiàng)目開(kāi)發(fā)需要有多個(gè)環(huán)境,一般為開(kāi)發(fā),測(cè)試,預(yù)發(fā),正式4個(gè)環(huán)境,在pom.xml中的配置如下:

<profiles>
  <profile>
     <id>dev</id>
     <properties>
        <env>dev</env>
     </properties>
     <activation>
        <activeByDefault>true</activeByDefault>
     </activation>
  </profile>
  <profile>
     <id>qa</id>
     <properties>
         <env>qa</env>
     </properties>
  </profile>
  <profile>
     <id>pre</id>
     <properties>
        <env>pre</env>
     </properties>
  </profile>
  <profile>
     <id>prod</id>
     <properties>
        <env>prod</env>
     </properties>
  </profile></profiles>
...
<build>
  <filters>
        <filter>config/${env}.properties</filter>
  </filters>
  <resources>
     <resource>
        <directory>src/main/resources</directory>
        <filtering>true</filtering>
     </resource>
  </resources>
</build>

profiles定義了各個(gè)環(huán)境的變量 id, filters中定義了變量配置文件的地址,其中地址中的環(huán)境變量就是上面 profile中定義的值, resources中是定義哪些目錄下的文件會(huì)被配置文件中定義的變量替換。

通過(guò)maven可以實(shí)現(xiàn)按不同環(huán)境進(jìn)行打包部署,命令為:

mvn package -P dev

其中 dev 為環(huán)境的變量id,代表使用Id為 devprofile。**

-e 顯示maven運(yùn)行出錯(cuò)的信息
-o 離線執(zhí)行命令,即不去遠(yuǎn)程倉(cāng)庫(kù)更新包
-X 顯示maven允許的debug信息
-U 強(qiáng)制去遠(yuǎn)程更新snapshot的插件或依賴,默認(rèn)每天只更新一次

舉個(gè)例子

將自己的jar包部署到遠(yuǎn)程倉(cāng)庫(kù)去,可以使用 deploy 指令:

mvn deploy:deploy-file -DgroupId=<group-id> \
  -DartifactId=<artifact-id> \
  -Dversion=<version> \
  -Dpackaging=<type-of-packaging> \
  -Dfile=<path-to-file> \
  -DrepositoryId=<id-to-map-on-server-section-of-settings.xml> \
  -Durl=<url-of-the-repository-to-deploy>

最后說(shuō)下我們?yōu)槭裁匆獙W(xué)習(xí)maven,大概可以收獲這些好處吧:

  • 提高自己的生產(chǎn)力
  • 更好的管理項(xiàng)目中的jar包
  • 自己開(kāi)發(fā)的jar包可以共享給別人
  • 遇到j(luò)ar包沖突問(wèn)題可以不求人
  • 。。。
向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI