Maven 虐我千百遍,我待 Maven 如初戀

前言

在現在的互聯網項目開發當中,特別是Java領域,能夠說Maven隨處可見。Maven的倉庫管理、依賴管理、繼承和聚合等特性爲項目的構建提供了一整套完善的解決方案,能夠說若是你搞不懂Maven,那麼一個多模塊的項目足以讓你頭疼,依賴衝突就會讓你不知所措,甚至搞不清楚項目是如何運行起來的.....OK,博主就曾經被Maven「傷害」過,那麼該專題的目的就是:完全搞定Maven!mysql

Thinking in Maven

回想一下,當你新到一家公司,安裝完JDK後就會安裝配置Maven(MAVEN_HOME、path),很大可能性你須要修改settings.xml文件,好比你會修改本地倉庫地址路徑,好比你極可能會copy一段配置到你的settings.xml中(極可能就是私服的一些配置)。面試

接下來,你會到IDEA或者Eclipse中進行Maven插件配置,而後你就能夠在工程中的pom.xml裏面開始添加<dependency>標籤來管理jar包,在Maven規範的目錄結構下進行編寫代碼,最後你會經過插件的方式來進行測試、打包(jar or war)、部署、運行。sql

上面描述了咱們對Maven的一些使用方式,下面咱們進行一些思考:

本地倉庫?Maven到底有哪些倉庫?它們什麼關係?

你要jar包,不可能每次都要聯網去下載吧,多費勁,因此本地倉庫就是至關於加了一層jar包緩存,先到這裏來查。若是這裏查不到,那麼就去私服上找,若是私服也找不到,那麼去中央倉庫去找,找到jar後,會把jar的信息同步到私服和本地倉庫中。api

私服,就是公司內部局域網的一臺服務器而已,你想一下,當你的工程Project-A依賴別人的Project-B的接口,怎麼作呢?沒有Maven的時候,固然是copy Project-B jar到你的本地lib中引入,那麼Maven的方式,很顯然須要其餘人把Project-B deploy到私服倉庫中供你使用。所以私服中存儲了本公司的內部專用的jar!不只如此,私服還充當了中央倉庫的鏡像,說白了就是一個代理!緩存

中央倉庫:該倉庫存儲了互聯網上的jar,由Maven團隊來維護,地址是:http://repo1.maven.org/maven2/。tomcat

關於<dependency>的使用

其實這個標籤揭示了jar的查找座標:groupId、artifactId、version。服務器

通常而言,咱們能夠到私服上輸入artifactId進行搜索,或者到http://search.maven.org/、http://mvnrepository.com/上進行查找肯定座標。mybatis

version分爲開發版本(Snapshot)和發佈版本(Release),那麼爲何要分呢? 在實際開發中,咱們常常遇到這樣的場景,好比A服務依賴於B服務,A和B同時開發,B在開發中發現了BUG,修改後,將版本由1.0升級爲2.0,那麼A必須也跟着在POM.XML中進行版本升級。過了幾天後,B又發現了問題,進行修改後升級版本發佈,而後通知A進行升級...能夠說這是開發過程當中的版本不穩定致使了這樣的問題。架構

Maven,已經替咱們想好了解決方案,就是使用Snapshot版本,在開發過程當中B發佈的版本標誌爲Snapshot版本,A進行依賴的時候選擇Snapshot版本,那麼每次B發佈的話,會在私服倉庫中,造成帶有時間戳的Snapshot版本,而A構建的時候會自動下載B最新時間戳的Snapshot版本!maven

既然Maven進行了依賴管理,爲何還會出現依賴衝突?處理依賴衝突的手段是?

首先來講,對於Maven而言,同一個groupId同一個artifactId下,只能使用一個version!

根據上圖的依賴順序,將使用1.2版本的jar。

如今,咱們能夠思考下了,好比工程中須要引入A、B,而A依賴1.0版本的C,B依賴2.0版本的C,那麼問題來了,C使用的版本將由引入A、B的順序而定?這顯然不靠譜!若是A的依賴寫在B的依賴後面,將意味着最後引入的是1.0版本的C,極可能在運行階段出現類(ClassNotFoundException)、方法(NoSuchMethodError)找不到的錯誤(由於B使用的是高版本的C)!

這裏其實涉及到了2個概念:依賴傳遞(transitive)、Maven的最近依賴策略。

依賴傳遞:若是A依賴B,B依賴C,那麼引入A,意味着B和C都會被引入。

Maven的最近依賴策略:若是一個項目依賴相同的groupId、artifactId的多個版本,那麼在依賴樹(mvn dependency:tree)中離項目最近的那個版本將會被使用。(從這裏能夠看出Maven是否是有點小問題呢?能不能選擇高版本的進行依賴麼?據瞭解,Gradle就是version+策略)

如今,咱們能夠想一想如何處理依賴衝突呢?

想法1:要使用哪一個版本,咱們是清楚的,那麼能不能無論如何依賴傳遞,均可以進行版本鎖定呢?

使用<dependencyManagement> [這種主要用於子模塊的版本一致性中]

想法2:在依賴傳遞中,能不能去掉咱們不想依賴的?

使用<exclusions> [在實際中咱們能夠在IDEA中直接利用插件幫助咱們生成]

想法3:既然是最近依賴策略,那麼咱們就直接使用顯式依賴指定版本,那不就是最靠近項目的麼?

使用<dependency>

引入依賴的最佳實踐,提早發現問題!

在工程中,咱們避免不了須要加一些依賴,也許加了依賴後運行時才發現存在依賴衝突在去解決,彷佛有點晚!那麼能不能提早發現問題呢?

若是咱們新加入一個依賴的話,那麼先經過mvn dependency:tree命令造成依賴樹,看看咱們新加入的依賴,是否存在傳遞依賴,傳遞依賴中是否和依賴樹中的版本存在衝突,若是存在多個版本衝突,利用上文的方式進行解決!

Maven規範化目錄結構

這裏須要注意2點: 第一:src/main下內容最終會打包到Jar/War中,而src/test下是測試內容,並不會打包進去。

第二:src/main/resources中的資源文件會COPY至目標目錄,這是Maven的默認生命週期中的一個規定動做。(想想,hibernate/mybatis的映射XML須要放入resources下,而不能在放在其餘地方了)

Maven的生命週期

咱們只須要注意一點:執行後面的命令時,前面的命令自動獲得執行。

實際上,咱們最經常使用的就是這麼幾個:

  • clean:有問題,多清理!
  • package:打成Jar or War包,會自動進行clean+compile
  • install:將本地工程Jar上傳到本地倉庫
  • deploy:上傳到私服
關於scope依賴範圍

既然,Maven的生命週期存在編譯、測試、運行這些過程,那麼顯然有些依賴只用於測試,好比junit;有些依賴編譯用不到,只有運行的時候才能用到,好比mysql的驅動包在編譯期就用不到(編譯期用的是JDBC接口),而是在運行時用到的;還有些依賴,編譯期要用到,而運行期不須要提供,由於有些容器已經提供了,好比servlet-api在tomcat中已經提供了,咱們只須要的是編譯期提供而已。

總結來講:
  • compile:默認的scope,運行期有效,須要打入包中。
  • provided:編譯期有效,運行期不須要提供,不會打入包中。
  • runtime:編譯不須要,在運行期有效,須要導入包中。(接口與實現分離)
  • test:測試須要,不會打入包中。
  • system:非本地倉庫引入、存在系統的某個路徑下的jar。(通常不使用)

若是你如今在JAVA這條路上掙扎,也想在IT行業拿高薪,能夠參加咱們的訓練營課程,選擇最適合本身的課程學習,技術大牛親授,7個月後,進入名企拿高薪。咱們的課程內容有:Java工程化、高性能及分佈式、高架構。性能調優、Spring,MyBatis,Netty源碼分析和大數據等多個知識點。若是你想拿高薪的,想學習的,想就業前景好的,想跟別人競爭能取得優點的,想進阿里面試但擔憂面試不過的,你均可以來,q羣號爲:956011797

相關文章
相關標籤/搜索