最近在看 Gradle 相關(guān)的知識,看到介紹 Gradle 的開篇總是會拿來和 Maven 做對比殃姓,于是就好奇了解下 Maven 的使用,本篇文章只是對 Maven 很淺顯的一個介紹鼻百,知道 Maven 是怎么回事扬虚,并不會對 Maven 做深入的探討能曾,因為我們的重點還是 Gradle涨颜。
Ant沐兵,Maven曙旭,Gradle 都是優(yōu)秀的項目管理工具盗舰,做 Android 開發(fā)時應(yīng)該都知道整個項目是用 gradle 進行管理的,平時用的最多的也就是往 gradle 配置文件中添加第三方庫的依賴桂躏,僅此而已钻趋。接著我們直接點擊了 run 的按鈕等最終結(jié)果的輸出了,而這些項目管理工具到底管理的是什么呢剂习?我們不去看官方的話語蛮位,這里用大白話解釋下,所謂項目管理鳞绕,可以想象一個很大的項目失仁,模塊無窮多,這時就遇到問題了
- 項目中不可避免的要引入一些第三方的庫
- 每個模塊之間也會產(chǎn)生一定的依賴關(guān)系
- 每個模塊都要進行獨立的單元測試
- 代碼在其他人機器上不能跑们何,而在自己的機器上明明可以跑...
- 項目需要在構(gòu)建的過程而不是編碼過程中加入一些自定義的功能
- ......
這么多的問題萄焦,想想就頭大,然而平時我們做的 hello world 根本不會遇到這些問題冤竹。如何解決拂封?單靠程序員手動解決,簡直累死鹦蠕,于是項目管理工具產(chǎn)生了冒签,我們只需要在配置文件中進行簡單的配置,即可完成以上復(fù)雜的流程片部。
Maven 下載安裝
略镣衡。。档悠。
注意配置環(huán)境變量
配置好之后,用 mvn -v
查看是否安裝成功并成功配置了環(huán)境變量望浩。
Maven 項目的默認目錄結(jié)構(gòu)
我們平時寫的代碼可能有自己的組織結(jié)構(gòu)辖所,要想將該項目改造為由 Maven 管理,那么就要按照 Maven 的管理規(guī)則來磨德,否則 Maven 無法識別正確的路徑缘回,影響最終結(jié)果的生成吆视。Maven 默認的文件結(jié)構(gòu)如下
項目文件夾
src
main
java
test
java
我們的平時寫的代碼放在 src/main/java/下,測試代碼放在 test/java 下酥宴,當(dāng)然這個結(jié)構(gòu)也是可以通過 maven 來配置的啦吧,不過一般使用默認結(jié)構(gòu),而且現(xiàn)在大多數(shù)的 IDE 創(chuàng)建的工程也是按照這個結(jié)構(gòu)組織的拙寡。
pom.xml 簡介
上面只是對代碼的文件夾結(jié)構(gòu)進行了約束授滓,若要使用 maven 提供的 mvn 命令來管理該項目,還需要在項目文件夾下添加一個 pom.xml 的配置文件肆糕,其基本的結(jié)構(gòu)如下:
<?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.zx.mymavenproj</groupId>
<artifactId>mymavenproj-model</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
</dependency>
</dependencies>
</project>
這里有幾個節(jié)點簡單介紹下:
首先般堆,project 的屬性是默認的,所有的 pom 文件都一樣诚啃,接下來是 modelVersion 節(jié)點淮摔,其值是 4.0.0,也是固定的始赎。
- groupId:項目包名(一般為公司域名反寫+項目名)
- artifactId:項目名-模塊名(maven 是的一個優(yōu)勢就是對項目多模塊之間的管理和橙,不同的模塊都可以用 maven 進行管理)
- version:項目的版本號
以上三個節(jié)點稱之為一個組件的 坐標,因為我們可以通過這三個屬性唯一的確定一個模塊或者依賴造垛。
- dependencies:用來進行依賴管理胃碾,具體見下一節(jié)
依賴管理
現(xiàn)在的項目中難免會用到一些第三方的依賴庫,早期的做法是直接將 jar 包拷貝下來筋搏,放到工程目錄的 libs 文件夾下仆百,但是 jar 包一多,尤其是對于團隊開發(fā)奔脐,將 jar 包們來回拷貝也是麻煩俄周,而且還可能因為不同的小組使用了不同版本的 jar,導(dǎo)致項目最終無法通過編譯髓迎,好在 maven 為我們解決了這個問題峦朗,不用將 jar 包拷貝來拷貝去,同時只要指定 jar 包的版本號排龄,即可是團隊中的 jar 包保持一致波势,不同的小組只需要同步這個 pom.xml 文件即可。
在 pom.xml 文件中橄维,有一個 dependencies 的節(jié)點尺铣,在這個節(jié)點中可以將我們所有的依賴配置進去。每個依賴都由一個單獨的 dependency 節(jié)點組成争舞,該節(jié)點下有三個重要的屬性:
- groupId:項目包名
- artifactId:項目名-模塊名
- version:項目的版本號
這和上一節(jié)介紹配置我們自己項目時需要的三個屬性是相同的凛忿,通過在三個屬性,就能唯一確定一個依賴項竞川。
eg:maven 的項目管理中包括測試這一項店溢,這就需要我們在依賴中添加關(guān)于測試相關(guān)的框架
我們可以在 maven center 中尋找我們所需的項目叁熔,這是 Maven 為我們提供的倉庫,有了它床牧,我們就不必到 Junit 的官網(wǎng)下載 jar 包了荣回,而是通過 maven 統(tǒng)一管理,在這個網(wǎng)頁中搜索 junit戈咳,找到 http://search.maven.org/#artifactdetails%7Cjunit%7Cjunit%7C4.12%7Cjar 這里提供了如何向我們自己的項目中添加 junit 的依賴心软。
添加完 junit 的依賴后,我們的 pom.xml 的內(nèi)容就是上一節(jié)中的樣子除秀。
常用命令
經(jīng)過前面的配置糯累,一個簡單的 Maven 項目已經(jīng)配置好了,那么接下來考慮我們要做什么册踩?
- 代碼要被編譯為 class——compile
- 代碼要進行測試——test
- 代碼可能需要進行打包生成 jar 或者其它形式——package
- 代碼上傳到本地倉庫——install
- 清除上次構(gòu)建產(chǎn)生的結(jié)果——clean
- ......
為滿足上述的要求泳姐,可以使用后面提供的命令 mvn xxx
即可。
構(gòu)建所產(chǎn)生的結(jié)果放在工程目錄下的 target 子文件夾中
這里特別要說明一下 install 的命令暂吉,假設(shè)我們開發(fā)的這個模塊是日志記錄模塊 log胖秒,那么執(zhí)行 package 命令之后就會生成對應(yīng) 的 jar 包,接下來就要考慮如何將該 jar 包提供給其它模塊來使用了慕的,假設(shè)這里有一個網(wǎng)絡(luò)模塊 net阎肝,需要依賴 log 模塊,那么需要在 net 的 pom.xml 的 dependency 中加入 log 對應(yīng)的坐標即可肮街。僅僅是加入這個依賴风题,那么 net 模塊是如何找到 log 的代碼呢?
這里分為兩個步驟嫉父,首先 Maven 會從本機的本地 Maven 倉庫去找沛硅,如果找到就使用,如果沒找到绕辖,那么從 maven center 去找摇肌,如果還找不到,就直接報錯了仪际。這個 Maven 本地倉庫的地址是:C:\Users\UserName\.m2\repository
這是 windows 下的默認路徑围小,當(dāng)然也可以修改為其它路徑。
那么這個 install 命令的作用就是將 package 生成的 jar 包拷貝到該路徑下树碱,這樣在接下來 net 進行編譯的時候就可以找到對應(yīng)的 log 的依賴了肯适。
Maven 生命周期
了解了上面常用的命令,我們還需要知道 Maven 工程構(gòu)建的生命周期赴恨,上述的命令執(zhí)行其實是有先后順序的疹娶,eg:mvn install
如果執(zhí)行該命令,其實會先依次執(zhí)行: compile -> test -> package -> install
一般情況下伦连,直接點擊 install雨饺,等待打包即可,中間任何環(huán)節(jié)出現(xiàn)問題惑淳,都會停止構(gòu)建额港,并輸出對應(yīng)的信息。
同時要注意的是歧焦,如果運行 package 命令移斩,中間勢必要經(jīng)過 test 的流程,這就要求我們在當(dāng)前項目中必須要依賴 junit绢馍,即使我們并沒有寫任何測試代碼向瓷,否則 test 流程會失敗。
在 idea 中使用 Maven
前面講解的流程都是手動構(gòu)建了項目目錄舰涌,然后使用 maven 命令行對項目進行構(gòu)建的猖任,通過這種方式可以更加清楚的了解 maven 構(gòu)建過程中的一些細節(jié),但是這種方式已經(jīng)很少手動去都建了瓷耙,所有的大型項目都是通過 IDE 來寫的朱躺,而且目前幾乎所有的 IDE 創(chuàng)建的工程目錄結(jié)構(gòu)默認都是符合 Maven 規(guī)范的,所以我們也不用手動去創(chuàng)建符合 Mavne 標準的目錄結(jié)構(gòu)了搁痛。
接下來以常用的 IDE——idea 來講解如何在項目中使用 Maven长搀。
新建一個工程,類型為 empty鸡典,然后在該工程下創(chuàng)建三個 module源请,依次為 log,net彻况,ui谁尸。module 的類型可以選擇 java,也可以選為 maven。這兩個類型的區(qū)別在于 maven module 會比 java module 多一個 pom.xml 文件疗垛,僅此而已症汹,其它的都一樣。我們既可以直接創(chuàng)建一個 maven 的 module贷腕,也可以選擇創(chuàng)建好 java module 之后背镇,在該 module 的根目錄下手動添加一個 pom.xml,然后執(zhí)行同步泽裳,其最終結(jié)果都是一樣的瞒斩。
但是如果你真的想讓 maven 來管理項目,那么還是建議直接創(chuàng)建 Maven 項目涮总,因為先創(chuàng)建 java module胸囱,然后在添加 pom.xml 文件時,識別比較慢瀑梗,而且 java module 創(chuàng)建的目錄下只到了項目目錄/src烹笔,而 src 下面沒有 main 和 test 的文件夾裳扯,還是需要我們手動創(chuàng)建
目前我們已經(jīng)創(chuàng)建了 log,net谤职,ui 三個 module饰豺,三個模塊的 pom 文件如下:
log 模塊:
<?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.zx.testmaven</groupId>
<artifactId>testmaven-log</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
</dependency>
</dependencies>
</project>
net 模塊
<?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.zx.testmaven</groupId>
<artifactId>testmaven-net</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
</dependency>
</dependencies>
</project>
ui 模塊
<?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.zx.testmaven</groupId>
<artifactId>testmaven-ui</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
</dependency>
</dependencies>
</project>
依賴傳遞
目前我們已經(jīng)創(chuàng)建了 log,net允蜈,ui 三個 module冤吨,然后讓 net 依賴 log,ui 依賴 net饶套。三個模塊的 pom 文件如下:
log 模塊:
<?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.zx.testmaven</groupId>
<artifactId>testmaven-log</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
</dependency>
</dependencies>
</project>
net 模塊
<?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.zx.testmaven</groupId>
<artifactId>testmaven-net</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
</dependency>
<dependency>
<groupId>com.zx.testmaven</groupId>
<artifactId>testmaven-log</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
ui 模塊
<?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.zx.testmaven</groupId>
<artifactId>testmaven-ui</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
</dependency>
<dependency>
<groupId>com.zx.testmaven</groupId>
<artifactId>testmaven-net</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
目前的依賴關(guān)系是:ui->net->log漩蟆;
我們依次對 log,net妓蛮,進行 install怠李,對 ui 進行 package。此時可以發(fā)現(xiàn) ui 的 dependencise 中依賴了 net仔引,同時又依賴了 log扔仓。這就是所謂的依賴傳遞,雖然 ui 并沒有直接依賴 log咖耘,但是其依賴的 net 中卻依賴了 log翘簇,因此 ui 也就依賴了 log。這樣的話儿倒,在 ui 中編寫代碼時也可以直接使用 log 中的類了版保。
依賴的排除
上一節(jié)介紹了依賴的傳遞,但是現(xiàn)在有個問題夫否,ui 中并不想依賴 log彻犁,因為在某些情況下,我們在 ui 中自己寫了一個類和 log 中的類是同名的凰慈,恰好方法也是同名的汞幢,如果沒有注意,可能會引入 log 中的類微谓,而我們的本意是依賴 ui 中自己寫的類森篷,這就在 import 時,帶來不必要的麻煩豺型,解決的方法是在 ui 的 pom 文件仲智,對net 的依賴中,使用 exclusions 標簽姻氨,將不想依賴的 log 移除钓辆,移除后的 pom 如下:
<dependency>
<groupId>com.zx.testmaven</groupId>
<artifactId>testmaven-net</artifactId>
<version>1.0-SNAPSHOT</version>
<exclusions>
<exclusion>
<groupId>com.zx.testmaven</groupId>
<artifactId>testmaven-log</artifactId>
</exclusion>
</exclusions>
</dependency>
依賴沖突
產(chǎn)生依賴沖突的場景:
(1)A->B->C, A->C
A 依賴 B,B 依賴 C,而 A 有直接依賴 C前联,同時這兩個 C 的版本還不一致功戚,這就導(dǎo)致了依賴沖突。
解決該沖突的默認方法:
短路優(yōu)先:默認解析路徑短的蛀恩,上例中疫铜,會采用 A 直接依賴的 C 的版本茂浮,而不是采用 B 中依賴的双谆。
(2)A->B-X,A->C->X
A 依賴 B,B 依賴 X席揽,同時 A 依賴 C顽馋,C 依賴 X,也會導(dǎo)致沖突
解決該沖突的默認方法:
路徑相同幌羞,誰的 dependency 靠前就依賴誰寸谜,eg 在 A 的 dependency 中,先依賴的 B属桦,那么間接引用的 X 采用的是 B 的熊痴。
依賴的范圍
引入依賴后,該依賴起作用的范圍也是不同的聂宾,其大體可以分為以下三種范圍:
- 編譯
- 測試
- 運行
這里所說的范圍指的是
有的依賴僅僅是在測試的時候引入的果善,有的依賴僅在編譯的時候使用,有的依賴僅在運行時使用系谐,或者有的依賴是在其中的三種或者兩種是有效的巾陕,為了避免依賴在其無用的范圍內(nèi)被錯誤的使用,maven 為我們提供了以下 6 種 dependency 節(jié)點中可選的 scope 屬性纪他,其對應(yīng)的值分別是:
- compile:默認級別鄙煤,對于編譯,測試茶袒,運行三種 classpath 均有效梯刚;
- provided:僅在編譯和測試時有效,在運行是不會被打入包中
- runtime:在測試和運行時有效薪寓,典型的應(yīng)用就是 jdbc 驅(qū)動的實現(xiàn)亡资;
- test:僅在測試范圍有效;
- system:類型 provided预愤,在編譯和測試時有效沟于,但是該特性與本地系統(tǒng)相關(guān)聯(lián),系統(tǒng)移植性差
- import:導(dǎo)入范圍植康,僅適用于 dependencyManagement 標簽下旷太,表示從其它的 pom 中導(dǎo)入 dependency 配置
而我們也看到,在上述的三個模塊中,均使用了 junit 的框架供璧,而該框架只是在測試的范圍內(nèi)使用的存崖,因此我們可以在 dependency 節(jié)點中引入 scope 節(jié)點,將其值設(shè)置為 test
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
聚合
我們要得到 ui 的 jar 包睡毒,要分別對 log 和 net 進行 install来惧,這就要求我們必須了解各個模塊是如何相互依賴的,然后在手動的去 install演顾。有沒有一種方法供搀,通過一個命令,直接得到最后 ui 的 jar钠至,答案就是聚合葛虐。
依舊在該工程中創(chuàng)建一個 maven 的 module,這里主要要將 packaging 標簽改為 pom棉钧,默認值為 jar屿脐,同時在 modules 標簽中,將上面的 log宪卿,net的诵,ui 的 module 都包含進來。示例如下:
<?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.zx.testmaven</groupId>
<artifactId>testmaven-aggreation</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>../Log</module>
<module>../Net</module>
<module>../UI</module>
</modules>
</project>
此時在依次執(zhí)行該 module 的 celan->package 就會直接得到 ui 的 jar佑钾。
注意:使用 package西疤,并沒有將 log 和 net 的 jar 上傳到本地 maven 倉庫中,但卻可以打出 ui 的包次绘。
繼承
在上面的三個模塊中都是用了 junit瘪阁,我們可以將其抽象出來,定義到一個父 pom 中邮偎,然后讓這三個模塊都繼承這個父 pom管跺,而自身的 pom 中不需要引入 junit 的依賴了。
定義一個 parent 的 maven 模塊
<?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.zx.testmaven</groupId>
<artifactId>testmaven-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
<junit.version>4.1.0</junit.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
注意點:
- 其 packaging 的值為 pom
- 是用 dependencyManagement 標簽管理 dependencies禾进,然后在其下的 dependency 中將 junit 填入
- 可以定義為 junit 的版本定義一個屬性豁跑,然后在其 version 中進行引用(可選)
接下來,要讓三個模塊來繼承這個 parent泻云,以 log 為例艇拍,進行如下修改:
<?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.zx.testmaven</groupId>
<artifactId>testmaven-log</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>com.zx.testmaven</groupId>
<artifactId>testmaven-parent</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<!-- <dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
</dependency>
</dependencies>-->
</project>
引入了 parent 標簽,同時將依賴中的 junit 刪除掉宠纯。net卸夕,ui 模塊也是類似的處理。
接下來婆瓜,先對 parent 模塊進行 install快集,然后再用 aggreation 模塊贡羔,進行 install,此時 ui 的 jar 成功的安裝在本地 mave 倉庫中个初。