阿里,騰訊,美團三廠一面居然都問到的Maven!

竟然問我Maven?

maven package和maven install 有什麼區別?

你常用的maven命令有哪些?

是幹什麼的?

還有用過其它構建工具嗎? 和maven有啥區別?


這幾個問題都可以脫口而出,你應該是有點 maven 能耐,寫代碼去吧,不用看了

Maven是一個項目管理工具,它包含了一個項目對象模型 (Project Object Model),一組標準集合,一個項目生命週期(Project Lifecycle),一個依賴管理系統(Dependency Management System),和用來運行定義在生命週期階段(phase)中插件(plugin)目標(goal)的邏輯。當你使用Maven的時候,你用一個明確定義的項目對象模型來描述你的項目,然後Maven可以應用橫切的邏輯,這些邏輯來自一組共享的(或者自定義的)插件。

Maven 有一個生命週期,當你運行 mvn install 的時候被調用。

Maven是Apache軟件基金會唯一維護的一款自動化構建工具,專注於服務Java平臺的項目構建

依賴管理

Maven是基於項目對象模型(POM),可以通過一小段描述信息來管理項目的構建、報告和文檔的軟件項目管理工具。

2.Maven可以幹啥:

添加第三方jar包jar包之間的依賴關係: Maven 可以替我們自動的將當前 jar 包所依賴的其他所有 jar 包全部導入進來獲取第三方jar包: Maven 提供了一個完全統一規範的 jar 包管理體系,只需要在項目中以座標的方式依賴一個 jar 包,Maven 就會自動從中央倉庫進行下載到本地倉庫將項目拆分成多個工程模塊構建項目(打包,編譯等)

3.構建項目的幾個主要環節:

清理(clean):刪除以前的編譯結果,為重新編譯做好準備編譯(compile):將Java 源程序編譯為字節碼文件測試(test):針對項目中的關鍵點進行測試,確保項目在迭代開發過程中關鍵點的正確性報告:在每一次測試後以標準的格式記錄和展示測試結果打包(package):將一個包含諸多文件的工程封裝為一個壓縮文件用於安裝或部署。Java 工程對應 jar 包,Web工程對應 war 包。安裝(install):在 Maven 環境下特指將打包的結果——jar 包或 war 包安裝到本地倉庫中。部署(deploy):將打包的結果部署到遠程倉庫或將 war 包部署到服務器上運行。

4.Maven常用命令

mvn -version/-v —— 顯示版本信息mvn clean —— 清空生成的文件mvn compile —— 編譯mvn test —— 編譯並測試mvn package —— 生成target目錄,編譯、測試代碼,生成測試報告,生成jar/war文件mvn site —— 生成項目相關信息的網站mvn clean compile —— 表示先運行清理之後運行編譯,會將代碼編譯到target文件夾中mvn clean package —— 運行清理和打包mvn clean install —— 運行清理和安裝,會將打好的包安裝到本地倉庫中,以便其他的項目可以調用mvn clean deploy —— 運行清理和發佈

5.Maven核心概念

Maven 能夠實現自動化構建是和它的內部原理分不開的,這裡我們從 Maven 的九個核心概念入手, 看看 Maven 是如何實現自動化構建的

POM約定的目錄結構座標依賴管理倉庫管理生命週期插件和目標繼承聚合

Maven 的核心程序中僅僅定義了抽象的生命週期,而具體的操作則是由 Maven 的插件來完成的。可是 Maven 的插件並不包含在 Maven 的核心程序中,在首次使用時需要聯網下載。 下載得到的插件會被保存到本地倉庫中。本地倉庫默認的位置是:~.m2\repository。

5.1. Maven約定的工程目錄:



Java開發領域普遍認同的一個觀點:約定>配置>編碼(能用配置解決的問題就不編碼,能基於約定的就不配置)

5.2. POM

Project Object Model:項目對象模型。將 Java 工程的相關信息封裝為對象作為便於操作和管理的模型。

Maven 工程的核心配置。

5.3. 座標

Maven 的座標 使用如下三個向量在 Maven 的倉庫中唯一的確定一個 Maven 工程。groupid:公司或組織的域名倒序+當前項目名稱artifactId:當前項目的模塊名稱version:當前模塊的版本

<code>

<

groupId

>

net.lazyegg.maven

groupId

>

<

artifactId

>

Hello

artifactId

>

<

version

>

0.0.1-SNAPSHOT

version

>

/<code>如何通過座標到倉庫中查找 jar 包?將 gav 三個向量連起來net.lazyegg.maven+Hello+0.0.1-SNAPSHOT 複製代碼以連起來的字符串作為目錄結構到倉庫中查找net/lazyegg/maven/Hello/0.0.1-SNAPSHOT/Hello-0.0.1-SNAPSHOT.jar

※ 注意:我們自己的 Maven 工程必須執行安裝操作才會進入倉庫。安裝的命令是:mvn install

5.4. 依賴

Maven 中最關鍵的部分,我們使用 Maven 最主要的就是使用它的依賴管理功能。要理解和掌握 Maven 的依賴管理,我們只需要解決以下幾個問題:

① 依賴的目的是什麼

當 A jar 包用到了 B jar 包中的某些類時,A 就對 B 產生了依賴,這是概念上的描述。那麼如何在項目中以依賴的方式引入一個我們需要的 jar 包呢? 答案非常簡單,就是使用 dependency 標籤指定被依賴 jar 包的座標就可以了。

<code>

<

dependency

>

<

groupId

>

net.lazyegg.maven

groupId

>

<

artifactId

>

Hello

artifactId

>

<

version

>

0.0.1-SNAPSHOT

version

>

<

scope

>

compile

scope

>

dependency

>

/<code>

② 依賴的範圍

有時依賴信息中除了目標 jar 包的座標還有一個 scope 設置,這就是依賴的範圍。依賴的範圍有幾個可選值,常用的有:compile、test、provided 三個,當然還有不常用的 runtime、system..

compile默認範圍,編譯測試運行都有效provided:在編譯和測試時有效runtime:在測試和運行時有效test:只在測試時有效system:在編譯和測試時有效,與本機系統關聯,可移植性差常用依賴範圍有效性總結

compiletestprovided主程序√×√測試程序√√√參與部署√××

③ 依賴的傳遞性

A 依賴 B,B 依賴 C,A 能否使用 C 呢?那要看 B 依賴 C 的範圍是不是 compile,如果是則可用,否則不可用。

④ 依賴的排除

如果我們在當前工程中引入了一個依賴是 A,而 A 又依賴了 B,那麼 Maven 會自動將 A 依賴的 B 引入當 前工程,但是個別情況下 B 有可能是一個不穩定版,或對當前工程有不良影響。這時我們可以在引入 A 的時候將 B 排除。

<code>

<

dependency

>

<

groupId

>

net.lazyegg.maven

groupId

>

<

artifactId

>

Hello

artifactId

>

<

version

>

0.0.1-SNAPSHOT

version

>

<

scope

>

compile

scope

>

<

exclusions

>

<

exclusion

>

<

groupId

>

commons-logging

groupId

>

<

artifactId

>

commons-logging

artifactId

>

exclusion

>

exclusions

>

dependency

>

/<code>

⑤ 統一管理所依賴 jar 包的版本,對同一個框架的一組 jar 包最好使用相同的版本。為了方便升級框架,可以將 jar 包的版本信息統一提取出來

統一聲明版本號

<code>

<

properties

>

<

starfish.spring.version

>

4.1.1.RELEASE

starfish.spring.version

>

<

project.build.sourceEncoding

>

UTF-8

project.build.sourceEncoding

>

properties

>

/<code>引用前面聲明的版本號

<code>

<

dependency

>

<

groupId

>

org.springframework

groupId

>

<

artifactId

>

spring-core

artifactId

>

<

version

>

${starfish.spring.version}

version

>

<

scope

>

compile

scope

>

dependency

>

/<code>

⑥ 依賴的原則:解決 jar 包衝突

路徑最短者優先路徑相同時先聲明者優先

項目版本衝突時候的那種蛋疼的感覺,只有疼過的才知道,所以,我們來看看疼過的人是怎麼解決的,推薦一個IDEA插件,Maven Helper,比自帶的好用,一目瞭然

5.5. 倉庫

分類本地倉庫:為當前本機電腦上的所有 Maven 工程服務遠程倉庫私服:架設在當前局域網環境下,為當前局域網範圍內的所有 Maven 工程服務中央倉庫:架設在 Internet 上,為全世界所有 Maven 工程服務中央倉庫的鏡像:架設在各個大洲,為中央倉庫分擔流量。減輕中央倉庫的壓力,同時更快的響應用戶請求,比如阿里的鏡像倉庫中的文件Maven 的插件我們自己開發的項目的模塊第三方框架或工具的 jar 包 ※ 不管是什麼樣的 jar 包,在倉庫中都是按照座標生成目錄結構,所以可以通過統一的方式查詢或依賴,查詢地址:mvnrepository.com/

5.6. 生命週期

5.6.1. 什麼是 Maven 的生命週期?

Maven 生命週期定義了各個構建環節的執行順序,有了這個清單,Maven 就可以自動化的執行構建命令了。

Maven 有三套相互獨立的生命週期,分別是:

Clean Lifecycle 在進行真正的構建之前進行一些清理工作Default Lifecycle 構建的核心部分,編譯,測試,打包,安裝,部署等等Site Lifecycle 生成項目報告,站點,發佈站點

它們是相互獨立的,你可以僅僅調用 clean 來清理工作目錄,僅僅調用 site 來生成站點。當然你也可以直接運行 mvn clean install site 運行所有這三套生命週期。 每套生命週期都由一組階段(Phase)組成,我們平時在命令行輸入的命令總會對應於一個特定的階段。比 如,運行 mvn clean,這個 clean 是 Clean 生命週期的一個階段。有 Clean 生命週期,也有 clean 階段。

5.6.2. Clean 生命週期

Clean 生命週期一共包含了三個階段:

pre-clean 執行一些需要在 clean 之前完成的工作clean 移除所有上一次構建生成的文件post-clean 執行一些需要在 clean 之後立刻完成的工作

5.6.3. Site 生命週期

pre-site 執行一些需要在生成站點文檔之前完成的工作site 生成項目的站點文檔post-site 執行一些需要在生成站點文檔之後完成的工作,並且為部署做準備site-deploy 將生成的站點文檔部署到特定的服務器上 這裡經常用到的是 site 階段和 site-deploy 階段,用以生成和發佈 Maven 站點,這可是 Maven 相當強大 的功能,Manager 比較喜歡,文檔及統計數據自動生成,很好看。

5.6.4. Default 生命週期

Default 生命週期是 Maven 生命週期中最重要的一個,絕大部分工作都發生在這個生命週期中(列出一些重要階段)

validate:驗證工程是否正確,所有需要的資源是否可用。compile:編譯項目的源代碼。test:使用合適的單元測試框架來測試已編譯的源代碼。這些測試不需要已打包和佈署。package:把已編譯的代碼打包成可發佈的格式,比如 jar、war 等。integration-test:如有需要,將包處理和發佈到一個能夠進行集成測試的環境。verify:運行所有檢查,驗證包是否有效且達到質量標準。install:把包安裝到maven本地倉庫,可以被其他工程作為依賴來使用。deploy:在集成或者發佈環境下執行,將最終版本的包拷貝到遠程的repository,使得其他的開發者或者工程可以共享

5.6.5. 生命週期與自動化構建

運行任何一個階段的時候,它前面的所有階段都會被運行

,例如我們運行 mvn install 的時候,代碼會被編譯,測試,打包。這就是 Maven 為什麼能夠自動執行構建過程的各個環節的原因。此外,Maven 的插件機制是完全依賴 Maven 的生命週期的,因此理解生命週期至關重要。

5.7. 插件和目標

Maven 的核心僅僅定義了抽象的生命週期,具體的任務都是交由插件完成的每個插件都能實現多個功能,每個功能就是一個插件目標Maven 的生命週期與插件目標相互綁定,以完成某個具體的構建任務 例如:compile 就是插件 maven-compiler-plugin 的一個目標;pre-clean 是插件 maven-clean-plugin 的一個目標

5.8. 繼承

為什麼需要繼承機制? 由於非 compile 範圍的依賴信息是不能在“依賴鏈”中傳遞的,所以有需要的工程只能單獨配置創建父工程 創建父工程和創建一般的 Java 工程操作一致,唯一需要注意的是:打包方式主要設置為 pom在子工程中引用父工程 ,從當前目錄到父項目的 pom.xml 文件的相對路徑

<code>

<

parent

>

<

groupId

>

com.starfish.maven

groupId

>

<

artifactId

>

Parent

artifactId

>

<

version

>

0.0.1-SNAPSHOT

version

>

<

relativePath

>

../Parent/pom.xml

relativePath

>

parent

>

/<code>

此時如果子工程的 groupId 和 version 如果和父工程重複則可以刪除。

在父工程中管理依賴 將 Parent 項目中的 dependencies 標籤,用 dependencyManagement 標籤括起來

<code>

<

dependencyManagement

>

<

dependencies

>

<

dependency

>

<

groupId

>

junit

groupId

>

<

artifactId

>

junit

artifactId

>

<

version

>

4.9

version

>

<

scope

>

test

scope

>

dependency

>

dependencies

>

dependencyManagement

>

/<code>

在子項目中重新指定需要的依賴,刪除範圍和版本號

<code>

<

dependency

>

<

groupId

>

junit

groupId

>

<

artifactId

>

junit

artifactId

>

dependency

>

/<code>

5.9. 聚合

為什麼要使用聚合?

將多個工程拆分為模塊後,需要手動逐個安裝到倉庫後依賴才能夠生效。修改源碼後也需要逐個手動進 行 clean 操作。而使用了聚合之後就可以批量進行 Maven 工程的安裝、清理工作。

如何配置聚合? 在總的聚合工程中使用 modules/module 標籤組合,指定模塊工程的相對路徑即可

<code>

<

modules

>

<

module

>

starfish-learn-grpc

module

>

<

module

>

starfish-learn-kafka

module

>

<

module

>

starfish-web-demo

module

>

modules

>

/<code>