五分鐘快速掌握Maven的核心概念
前兩天在一個(gè)技術(shù)群,有人還在問(wèn)maven中g(shù)roupId、artifactId、version這些關(guān)鍵字的含義是什么,于是,我覺(jué)得還是很有必要來(lái)聊聊Maven中的這些核心概念。
成功不是將來(lái)才有的,而是從決定去做的那一刻起,持續(xù)累積而成。
今天我們來(lái)學(xué)習(xí)Maven中的核心概念。了解了這些核心概念后,我們后面就可以更深層次的學(xué)習(xí)和使用Maven。
坐標(biāo)
坐標(biāo)的概念
來(lái)自百度百科
能夠確定一個(gè)點(diǎn)在空間的位置的一個(gè)或一組數(shù),叫做這個(gè)點(diǎn)的坐標(biāo)。通常由這個(gè)點(diǎn)到垂直相交的若干條固定的直線的距離來(lái)表示 。這些直線叫做坐標(biāo)軸。坐標(biāo)軸的數(shù)目在平面上為2(x,y),在空間里為3(x,y,z)。
其實(shí)就是可以標(biāo)識(shí)平面中或空間里唯一的一個(gè)點(diǎn)。
Maven中的坐標(biāo)
Maven其中一個(gè)核心的作用就是管理項(xiàng)目的依賴,引入我們所需的各種jar包等。為了能自動(dòng)化的解析任何一個(gè)Java構(gòu)件,Maven必須將這些Jar包或者其他資源進(jìn)行唯一標(biāo)識(shí),這是管理項(xiàng)目的依賴的基礎(chǔ),也就是我們要說(shuō)的坐標(biāo)。包括我們自己開(kāi)發(fā)的項(xiàng)目,也是要通過(guò)坐標(biāo)進(jìn)行唯一標(biāo)識(shí)的,這樣才能才其它項(xiàng)目中進(jìn)行依賴引用。
案例
依賴時(shí)候:比如下面我們依賴junit的jar包。
- <!-- pom.xml中 -->
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <version>3.8.1</version>
- <scope>test</scope>
- </dependency>
項(xiàng)目中定義我們的項(xiàng)目將打成jar或者war包。
- <?xml version="1.0" encoding="UTF-8"?>
- <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>com.tian</groupId>
- <artifactId>maven-demo</artifactId>
- <version>1.0-SNAPSHOT</version>
- <!-- 默認(rèn)是jar -->
- <packaging>jar</packaging>
- </project>
最后打出來(lái)的jar或war的形式的形式:
- artifactid-version.jar
- artifactid-version.war
packaging 標(biāo)簽?zāi)J(rèn)是jar,所以通常我們?cè)跊](méi)有指定打成jar包還是war的時(shí)候,最終打成的就是jar包。
Maven坐標(biāo)的組成
「groupId」組織標(biāo)識(shí)(包名)。定義當(dāng)前Maven項(xiàng)目隸屬的實(shí)際項(xiàng)目。首先,Maven項(xiàng)目和實(shí)際項(xiàng)目不一定是一對(duì)一的關(guān)系。比如SpringFrameWork這一實(shí)際項(xiàng)目,其對(duì)應(yīng)的Maven項(xiàng)目會(huì)有很多,如spring-core,spring-context等。這是由于Maven中模塊的概念,因此,一個(gè)實(shí)際項(xiàng)目往往會(huì)被劃分成很多模塊。其次,groupId不應(yīng)該對(duì)應(yīng)項(xiàng)目隸屬的組織或公司。原因很簡(jiǎn)單,一個(gè)組織下會(huì)有很多實(shí)際項(xiàng)目,如果groupId只定義到組織級(jí)別,而后面我們會(huì)看到,artifactId只能定義Maven項(xiàng)目(模塊),那么實(shí)際項(xiàng)目這個(gè)層次將難以定義。最后,groupId的表示方式與Java包名的表達(dá)方式類(lèi)似,通常與域名反向一一對(duì)應(yīng)。上例中,groupId為junit,是不是感覺(jué)很特殊,這樣也是可以的,因?yàn)槿澜缇瓦@么個(gè)junit,它也沒(méi)有很多分支。
「artifactId」項(xiàng)目名稱(chēng)。該元素定義當(dāng)前實(shí)際項(xiàng)目中的一個(gè)Maven項(xiàng)目(模塊),推薦的做法是使用實(shí)際項(xiàng)目名稱(chēng)作為artifactId的前綴。比如上例中的junit,junit就是實(shí)際的項(xiàng)目名稱(chēng),方便而且直觀。在默認(rèn)情況下,maven生成的構(gòu)件,會(huì)以artifactId作為文件頭,如junit-3.8.1.jar,使用實(shí)際項(xiàng)目名稱(chēng)作為前綴,就能方便的從本地倉(cāng)庫(kù)找到某個(gè)項(xiàng)目的構(gòu)件。
「version」項(xiàng)目的當(dāng)前版本或者我們要依賴jar的版本。該元素定義了使用構(gòu)件的版本,如上例中junit的版本是3.8.1,你也可以改為4.0表示使用4.0版本的junit。
「packaging」項(xiàng)目的打包方式,最為常見(jiàn)的jar和war兩種,默認(rèn)是jar。定義Maven項(xiàng)目打包的方式,使用構(gòu)件的什么包。首先,打包方式通常與所生成構(gòu)件的文件擴(kuò)展名對(duì)應(yīng),如上例中沒(méi)有packaging,則默認(rèn)為jar包,最終的文件名為junit-3.8.1.jar。也可以打包成war等。
「classifier」 該元素用來(lái)幫助定義構(gòu)建輸出的一些附件。附屬構(gòu)件與主構(gòu)件對(duì)應(yīng),如上例中的主構(gòu)件為junit-3.8.1.jar,該項(xiàng)目可能還會(huì)通過(guò)一些插件生成如junit-3.8.1-javadoc.jar,junit-3.8.1-sources.jar, 這樣附屬構(gòu)件也就擁有了自己唯一的坐標(biāo)。
上述5個(gè)元素中,groupId、artifactId、version是必須定義的,packaging是可選的(默認(rèn)為jar),而classfier是不能直接定義的,需要結(jié)合插件使用。
Maven為什么使用坐標(biāo)呢?
Maven世界里擁有大量構(gòu)建,我們需要找一個(gè)用來(lái)唯一標(biāo)識(shí)一個(gè)構(gòu)建的統(tǒng)一規(guī)范。
擁有了統(tǒng)一規(guī)范,就可以把查找工作交給機(jī)器。
maven依賴管理
依賴
依賴通常表現(xiàn)為:我需要你的東西,就像情侶之間相互依賴,夫妻之間相互依賴,人依賴于水,人依賴于糧食等。
在Maven中則表現(xiàn)為:項(xiàng)目中用到b.jar包的每個(gè)類(lèi),此時(shí)的項(xiàng)目就依賴b.jar。
復(fù)雜點(diǎn)關(guān)系就是多層依賴:a.jar包依賴b.jar包,還有可能b.jar包依賴c.jar。這種現(xiàn)象也可以稱(chēng)之為依賴傳遞性。
我們的項(xiàng)目間接性的依賴了b.jar。
依賴配置
Maven中依賴配置案例如下:
- <!--添加依賴配置-->
- <dependencies>
- <!--項(xiàng)目要使用到j(luò)unit的jar包,所以在這里添加junit的jar包的依賴-->
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <version>4.9</version>
- <scope>test</scope>
- </dependency>
- <!--項(xiàng)目要使用到Hello的jar包,所以在這里添加Hello的jar包的依賴-->
- <dependency>
- <groupId>com.tian.maven</groupId>
- <artifactId>user-service</artifactId>
- <version>0.0.1-SNAPSHOT</version>
- <scope>compile</scope><!-- 依賴范圍-->
- </dependency>
- </dependencies>
依賴范圍
所謂的依賴范圍就是指我們?cè)谑裁葱枰蕾嚨膉ar。有的是在編譯的時(shí)候就需要,有的是測(cè)試的時(shí)候需要等。
依賴范圍scope有以下6種:
「compile」 默認(rèn)編譯依賴范圍。對(duì)于編譯,測(cè)試,運(yùn)行三種classpath都有效。即在編譯、測(cè)試和運(yùn)行的時(shí)候都要使用該依賴jar包;
「test」測(cè)試依賴范圍。只對(duì)于測(cè)試classpath有效。而在編譯和運(yùn)行項(xiàng)目時(shí)無(wú)法使用此類(lèi)依賴,典型的是JUnit,它只用于編譯測(cè)試代碼和運(yùn)行測(cè)試代碼的時(shí)候才需要;
「provided」已提供依賴范圍。對(duì)于編譯,測(cè)試的classpath都有效,但對(duì)于運(yùn)行無(wú)效。因?yàn)橛扇萜饕呀?jīng)提供,例如servlet-api.jar,這個(gè)在編譯和測(cè)試的時(shí)候需要用到,但是在運(yùn)行的時(shí)候,web容器已經(jīng)提供了,就不需要maven幫忙引入了。
「runtime」運(yùn)行時(shí)依賴范圍,使用此依賴范圍的maven依賴,對(duì)于編譯測(cè)試、運(yùn)行測(cè)試和運(yùn)行項(xiàng)目的classpath有效,但在編譯主代碼時(shí)無(wú)效,比如jdbc驅(qū)動(dòng)實(shí)現(xiàn),運(yùn)行的時(shí)候才需要具體的jdbc驅(qū)動(dòng)實(shí)現(xiàn)。
「system」系統(tǒng)依賴范圍,使用system范圍的依賴時(shí)必須通過(guò)systemPath元素顯示地指定依賴文件的路徑,不依賴Maven倉(cāng)庫(kù)解析,所以可能會(huì)造成建構(gòu)的不可移植(即就是在你的電腦上可能沒(méi)問(wèn)題,但是到別人電腦上那就說(shuō)不清楚了),有點(diǎn)類(lèi)似provided ,注意這個(gè)system謹(jǐn)慎使用。
- <systemPath>${java.home}/lib/rt.jar</systemPath>
「import」僅pom在本節(jié)中的類(lèi)型依賴項(xiàng)上支持此作用域。它指示依賴關(guān)系將被指定的pom部分中的有效依賴關(guān)系列表替換。由于已替換它們,因此范圍為的依賴項(xiàng)import實(shí)際上不會(huì)參與限制依賴項(xiàng)的可傳遞性,在springboot和springcloud中用到的比較多。
以上六種范圍中,常用的有compile、test、runtime、provided 。
依賴范圍不僅可以控制與三種classpath的關(guān)系,還對(duì)傳遞性依賴產(chǎn)生影響,依賴關(guān)系圖如下:
「注意」預(yù)期這應(yīng)該是運(yùn)行時(shí)范圍,因此必須明確列出所有編譯依賴項(xiàng)。但是,如果您依賴的庫(kù)從另一個(gè)庫(kù)擴(kuò)展了一個(gè)類(lèi),則兩者都必須在編譯時(shí)可用。因此,即使編譯時(shí)間相關(guān)性是可傳遞的,它們?nèi)员A魹榫幾g范圍。
Maven倉(cāng)庫(kù)管理
Maven倉(cāng)庫(kù)
用來(lái)統(tǒng)一存儲(chǔ)所有Maven共享構(gòu)建的位置,說(shuō)白了就是用來(lái)存放jar包的,我們本地每次編譯的時(shí)候沒(méi)有對(duì)應(yīng)jar包是編譯通不過(guò)的,我們一個(gè)項(xiàng)目中是需要很多jar的依賴的,這時(shí)候就知道倉(cāng)庫(kù)的重要性了。
Maven倉(cāng)庫(kù)布局
根據(jù)Maven坐標(biāo)定義每個(gè)構(gòu)建在倉(cāng)庫(kù)中唯一存儲(chǔ)路徑,大致為:
groupId/artifactId/version/artifactId-version.packaging
本地倉(cāng)庫(kù)
在上一篇文章中,每個(gè)用戶只有一個(gè)本地倉(cāng)庫(kù),默認(rèn)是在~/.m2/repository/,~代表的是用戶目錄 。為了便于管理,一般都會(huì)自己搞一目錄,專(zhuān)門(mén)用來(lái)存儲(chǔ)本地倉(cāng)庫(kù)內(nèi)容。這樣我們開(kāi)發(fā)的時(shí)候,依賴那個(gè)jar就直接去我們的本地倉(cāng)庫(kù)repository中去查找,如果沒(méi)有,我們會(huì)從中央倉(cāng)庫(kù)中拉取。
中央倉(cāng)庫(kù)
基本上保存了對(duì)外開(kāi)發(fā)的所有jar包,Maven默認(rèn)的遠(yuǎn)程倉(cāng)庫(kù),(外國(guó)網(wǎng)站)URL地址:http://search.maven.org/ 。還有比如阿里的倉(cāng)庫(kù),我們?cè)陂_(kāi)發(fā)的時(shí)候,由于網(wǎng)絡(luò)原因,很多人都喜歡使用阿里的這個(gè)倉(cāng)庫(kù):http://maven.aliyun.com 。
這時(shí)候我們本地倉(cāng)庫(kù)和中央倉(cāng)庫(kù)的關(guān)系:
私服
大部分公司都會(huì)搭建私服,私服就是一種特殊的遠(yuǎn)程倉(cāng)庫(kù),它是架設(shè)在局域網(wǎng)內(nèi)的倉(cāng)庫(kù) 。比如公司搭建局域網(wǎng),公司也搞個(gè)倉(cāng)庫(kù),然后開(kāi)發(fā)人員就直接使用公司搭建的私服就行了,這樣大大減少了網(wǎng)絡(luò)開(kāi)銷(xiāo)以及開(kāi)發(fā)成本(有時(shí)候外網(wǎng)訪問(wèn)很慢,會(huì)浪費(fèi)大家開(kāi)發(fā)時(shí)間的)。
這樣開(kāi)發(fā)人員每次需要每個(gè)jar包就直接從公司的私服里拉取,不需要使用外網(wǎng)去中央倉(cāng)庫(kù)里拉取了??傊?jié)約時(shí)間和節(jié)約網(wǎng)絡(luò)開(kāi)始。并且有些企業(yè)還是不給外網(wǎng)的,這時(shí)候你就知道這個(gè)私服的重要性了。
增加了私服后,本地倉(cāng)庫(kù)+私服+中央倉(cāng)庫(kù)的關(guān)系圖:
面試中也頻繁被問(wèn):本地倉(cāng)庫(kù)、私服以及中央倉(cāng)庫(kù)是什么關(guān)系?
Maven生命周期
Maven的 生命周期:從我們的項(xiàng)目構(gòu)建,一直到項(xiàng)目發(fā)布的這個(gè)過(guò)程。
每個(gè)階段的說(shuō)明:
為了完成 default 生命周期,這些階段(包括其他未在上面羅列的生命周期階段)將被按順序地執(zhí)行。
Maven 有以下三個(gè)標(biāo)準(zhǔn)的生命周期:
- Clean Lifecycle 在進(jìn)行真正的構(gòu)建之前進(jìn)行一些清理工作。
- Default Lifecycle 構(gòu)建的核心部分,編譯,測(cè)試,打包,部署等等。
- Site Lifecycle 生成項(xiàng)目報(bào)告,站點(diǎn),發(fā)布站點(diǎn)。
這三個(gè)標(biāo)準(zhǔn)它們是相互獨(dú)立的,你可以僅僅調(diào)用clean來(lái)清理工作目錄,僅僅調(diào)用site來(lái)生成站點(diǎn)。當(dāng)然你也可以直接運(yùn)行 mvn clean install site運(yùn)行所有這三套生命周期。
運(yùn)行任何一個(gè)階段的時(shí)候,它前面的所有階段都會(huì)被運(yùn)行,這也就是為什么我們運(yùn)行mvn install 的時(shí)候,代碼會(huì)被編譯,測(cè)試,打包。此外,Maven的插件機(jī)制是完全依賴Maven的生命周期的,因此理解生命周期至關(guān)重要。
Maven插件
Maven是不做具體事情的,只是規(guī)定了生命周期的各個(gè)階段和步驟,由集成到 Maven 中的插件完成。
Maven的核心僅僅定義了抽象的生命周期,具體的任務(wù)都是交由插件完成的。
每個(gè)插件都能實(shí)現(xiàn)多個(gè)功能,每個(gè)功能就是一個(gè)插件目標(biāo)。
Maven的生命周期與插件目標(biāo)相互綁定,以完成某個(gè)具體的構(gòu)建任務(wù), 例如compile就是插件maven-compiler-plugin的一個(gè)插件目標(biāo)。
關(guān)于插件,這里就說(shuō)個(gè)大概,后續(xù)會(huì)出一篇文章專(zhuān)門(mén)來(lái)說(shuō)Maven插件。
排除不需要依賴
- <dependency>
- <groupId>com.tian.maven</groupId>
- <artifactId>my-maven</artifactId>
- <version>1.0.0</version>
- <exclusions>
- <exclusion>
- <groupId>com.tian.maven</groupId>
- <artifactId>your-maven</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
上面使用使用exclusions元素排除了my-maven->your-maven依賴的傳遞,也就是my-maven->your-maven不會(huì)被傳遞到當(dāng)前項(xiàng)目中。
exclusions中可以有多個(gè)exclusion元素,可以排除一個(gè)或者多個(gè)依賴的傳遞,聲明exclusion時(shí)只需要寫(xiě)上groupId、artifactId就可以了,version可以省略。
總結(jié)
本文講述Maven坐標(biāo),Maven依賴管理、Maven倉(cāng)庫(kù)管理、Maven生命周期以及簡(jiǎn)單介紹了Maven插件。有了這些概念作為鋪墊,我們就可以更深層次去體會(huì),為什么我們?cè)诠ぷ魇疫@么用的。
本文轉(zhuǎn)載自微信公眾號(hào)「Java后端技術(shù)全棧」,可以通過(guò)以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系Java后端技術(shù)全棧公眾號(hào)。