Maven可翻譯爲"知識的積累" or"專家",是一款成功的開源跨平臺的項目管理工具,不管小型的開源類庫項目,仍是大型的企業級應用;不管傳統的瀑布式開發,仍是流行的敏捷模式,Maven都能大顯身手.
1.1.1 構建工具java
咱們一直在不停的尋找避免重複的方法,設計的重複,編碼的重複,文檔的重複,固然還有構建的重複.Maven最大化的消除了構建的重複,抽象了構建生命週期,而且爲絕大部分的構建任務提供了已實現的插件,咱們不須要再定義過程,甚至不須要去實現這些過程當中的一些任務,只須要遵循Maven中的約定.mysql
同時Maven幫助咱們標準化構建過程,之前十個項目可能有十種構建方式,有了Maven後,全部項目的構建命令都是一直且簡單的.所以Maven做爲一個構建工具:算法
- 能夠幫咱們自動化構建,
- 能夠幫咱們抽象構建過程
- 提供構建任務是實現
- 跨平臺
- 對外提供一直的操做接口
1.1.2 不只僅是構建工具spring
Maven不只是構建工具,仍是一個依賴管理工具和項目信息管理工具,提供中央倉庫來幫忙咱們自動下載構建,經過引入一套經緯機制來系統準確的定位每個構建(artifact).sql
1.1.3 Maven數據庫
在Maven以前,有過程式的Make和Ant,開發者須要顯示的指定每個目標,以及完成該目標所須要執行的任務.針對每個項目,開發者都須要從新編寫這一過程,而其中就隱含着大量重複.apache
而Maven是聲明式的,項目構建過程和過程各個階段所需的工做都由插件實現,而且大部分插件都是現成的,開發者只須要聲明項目的基本元素,Maven就執行內置的,完整的構建過程.設計模式
Maven項目的核心是pom.xml,POM(Project Object Model,項目對象模型)定義了項目的基本信息,用於描述項目如何構建,聲明項目依賴等.api
<modelVersion>4.0.0</modelVersion>:modelVersion指定了當前Pom模型的版本,固定爲4.0.0<groupId>com.lsy</groupId>:groupId定義了項目屬於哪一個組,這個組每每和項目所在的組織和公司相關緩存
<artifactId>hello-world</artifactId>:artifactId定義了當前Maven項目在組中惟一的ID
<version>1.0-SNAPSHOT</version>:version指定了Hello World項目當前的版本,其中SNAPSHOT意爲快照,說明該項目還處於開發中
<name>Maven Hello World Project</name>:name元素聲明瞭一個對於用戶更爲友好的項目名稱.非必須
當運行mvn clean compile命令:clean告訴Maven清理輸出目錄target/,compile告訴Maven編譯項目主代碼,從輸出中看到Maven首先執行clean:clean任務,刪除target/目錄(默認狀況下,Maven構建的全部輸出都在target目錄中,接着執行resources:resources任務(未定義項目資源),最後執行compiler:compile任務,將項目主代碼編譯至target/classes目錄
其中clean:clean,resources:resources和compiler:compile對應了Maven插件及插件目標,好比clean:clean是clean插件的clean目標,compiler:compile是compiler插件的compile目標
首先添加Junit依賴
<dependencies> <dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.7</version>
<scope>test</scope>
</dependency>
<dependencies>
其中scope元素爲依賴範圍,若依賴範圍爲test則表示該依賴只對測試有效,若是不聲明依賴範圍,則默認是compile,表示該依賴對主代碼和測試代碼均有效
當運行mvn clean test命令,Maven實際執行的不止test任務,而是clean:clean,resources:resources,compiler:compile,resources:testResources以及compiler:testCompile,即在執行測試以前,會先自動執行項目資源處理,主代碼編譯,測試資源處理,測試代碼編譯等工做,這是Maven生命週期的一個特性.
因爲Maven核心插件之一compiler插件默認支持Java1.3,所以須要配置插件支持Java1.8
<build> <plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8<source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
將項目進行編譯,測試以後,下一個重要的步驟就是打包(package),當沒有指定打包類型時,默認打包類型是jar,當執行mvn clean package命令,Maven會在打包以前進行編譯,測試等操做,以後經過jar:jar任務負責打包,實際上就是jar插件的jar目標將項目主代碼打包成一個hello-world-1.0-SNAPSHOT.jar的文件
當其餘項目須要直接引用這個jar時,接下來須要一個安裝的步驟,執行mvn clean install,此命令執行了安裝任務install:install,將項目輸出的jar安裝到了Maven本地倉庫中,而構件只有被下載到本地倉庫後,才能由Maven項目使用.
mvn archetype:generate,當運行插件時,格式爲:groupId:artifactId:version:goal,默認使用最新的穩定版
- 在實際生活中,咱們能夠將地址看做是一種座標,省,市,區,街道等一系列信息能夠惟一標識城市中的任意居住地址,對應在Maven的世界中擁有很是巨大的構件,也就是日常使用的一些jar,war等文件
Maven定義了這樣一規則:世界上任意一個構建均可以使用Maven座標惟一標識,Maven座標的元素包括
- groupId:定義當前Maven項目隸屬公司的實際項目
- artifactId:該元素定義實際項目中的一個Maven模塊,推薦作法使用實際項目名稱爲artifact的前綴,便於尋找實際構建
- version:該元素定義Maven項目當前所處的版本
- package:該元素定義Maven項目的打包方式,默認爲jar包
- classifier:該元素用來幫助定義構建輸出一些附屬構建,附屬構件與主構件對應
<groupId\>org.sonatype.nexus</groupId\> <artifactId\>nexus-indexer</artifactId\> <version\>2.0.0</version\> <packaging\>jar</packaging\> <!--> 1\. 以上5個元素,groupId,artifactId,version是必須定義的,packaging是可選的,而classifier是不能直接定義的 2\. 項目構件的文件名格式:artifactId-version\[-classifier\].packaging
項目要引用Maven中的構建,就須要在pom文件中,經過座標來使用依賴,在根元素project下的dependencies能夠包含一個或多個dependency元素,用以聲明一個或多個項目依賴
- groupId,artifactId和version:依賴的基本座標,對於任何一個依賴來講,基本座標是最重要的
- type:依賴的類型,對應項目座標定義的packaging,通常沒必要聲明,默認爲jar
- scope:依賴的範圍
- optional:標記依賴是否可選
- exclusions:用來排除傳遞性依賴
- Maven項目在編譯項目主代碼時須要使用一套classpath,如編譯項目主代碼時須要使用spring-core,該文件以依賴的方式被引入到classpath中.其次,Maven在編譯和執行測試的時候會使用另一套classpath,依賴一樣會引入到相應的classpath中,最後在運行Maven項目時,又會使用一套classpath
依賴範圍就是用來控制依賴與三種classpath(編譯classpath,測試classpath,運行classpath)的關係
- compile:編譯依賴範圍,沒有指定時,爲默認依賴範圍.使用此依賴範圍的Maven依賴,對於編譯,測試運行三種classpath都有效,典型的例子爲:spring-core,在編譯,測試,運行階段都須要使用該依賴
- test:測試依賴範圍,使用此依賴範圍的Maven依賴,只對於測試的classpath有效,在編譯主代碼或者運行項目時將沒法使用此類依賴,典型的例子就是JUnit,它只有在編譯器測試代碼及運行測試的時候才須要
- provided:已提供依賴範圍,使用此依賴範圍的Maven依賴,對於編譯和測試classpath有效,但在運行時無效,典型的例子就是servlet-api,編譯和測試項目的時候須要該依賴,但在運行項目的時候,因爲容器已經提供,就不須要Maven重複引入了
- runtime:運行時依賴範圍,使用此依賴範圍的Maven依賴,對於測試和運行classpath有效,但在編譯主代碼時無效,典型的例子是JDBC驅動實現,項目主代碼的編譯只須要JDK提供的JDBC接口,只有在執行測試或運行項目的時候才須要實現上述接口的具體JDBC驅動
- system:系統依賴範圍,該依賴與三種classpath的關係,和provided依賴範圍徹底一致,可是,使用system範圍的依賴時必須經過systemPath元素顯示地指定依賴文件的路徑,因爲此類依賴不是經過Maven倉庫解析的,並且每每與本機系統綁定,可能形成構建的不可移植性
<dependency>
<groupId>javax.sql</groupId>
<artifactId>jdbc-stdext</artifactId>
<version>2.0</version>
<scope>system</scope>
<systemPath>${java.home}/lib/rt.jar</systemPath>
</dependency>- import:導入依賴範圍,該依賴範圍不會對三種classpath產生實際的影響,主要用於導入其餘pom文件中的dependencyManagement元素對於依賴版本約束的內容
在使用Maven依賴時,如Spring Framework,此依賴又會依賴其餘的開源庫,所以實際中每每會下載一個很大的如spring-framework-2.5.6-with-dependencies.zip包,這樣每每就引入了不少沒必要要的依賴,而Maven的傳遞性依賴機制就能夠很好的解決這一問題
- 當A項目引入一個compile範圍的B依賴,而B依賴中有一個compile範圍的C依賴,那麼C依賴一樣會成爲A的compile範圍依賴
傳遞性依賴和依賴範圍
- 假設A依賴於B,B依賴於C,那麼A對於B是第一直接依賴,B對於C是第二直接依賴,A對於C是傳遞性依賴
- 當第二直接依賴是compile的時候,傳遞性依賴與第一直接依賴範圍一致
- 當第二直接依賴是test的時候,依賴不會得以傳遞
- 當第二直接依賴是provided的時候,只傳遞第一直接依賴範圍爲provided的依賴,且傳遞性依賴範圍爲provided
- 當第二直接依賴是runtime的時候,傳遞性地依賴的範圍與第一直接依賴的範圍一致,但compile例外,此時傳遞性依賴的範圍爲runtime
假若有這樣一個依賴關係,A依賴於B,B依賴於X和Y,B對於X和Y的依賴都是可選依賴,根據傳遞性依賴的定義,若是這三個依賴的範圍都是compile,那麼X,Y就是A的compile範圍傳遞性依賴,可是因爲X,Y都是可選依賴,因此依賴不會得以傳遞,所以X,Y不會對A有任何影響
爲何會有可選依賴這一特性呢?
- 當B實現了兩個特性,特性一依賴於X,特性二依賴於Y,而且這兩個特性是互斥的,用戶不可能同時使用兩個特性,好比B是一個持久層隔離工具包,支持多種數據庫,在使用這個工具包的時候,只會依賴一種數據庫
<dependencies\> <dependency\> <groupId\>mysql</groupId\> <artifact\>mysql-connector-java</artifact\> <version\>5.6.0</version\> <optional\>true</optional\> </dependency\> <dependency\> <groupId\>postgresql</groupId\> <artifactpostgresql</artifact> <version\>8.4-701.jdbc3</version\> <optional\>true</optional\> </dependency\> </dependencies\> * 以上使用<optional\>元素表示兩個依賴爲可選依賴,他們只會對當前項目B產生影響,當其餘項目依賴於B時,這兩個依賴不會被傳遞,因此當A依賴於B項目時,若是要使用mysql數據庫,那麼須要顯式的聲明mysql-connector-java這一依賴 * 關於可選依賴,在理想的狀況下,是不該該使用可選依賴的,使用可選依賴的緣由是某一個項目中實現了多個特性,而根據單一職責原則,應該針對不一樣的特性分別建立一個Maven項目,用戶根據須要選擇使用其中某一個依賴
傳遞性依賴會給項目隱式的引入不少依賴,這極大的簡化了項目依賴的管理,可是有時候這種特性䧥帶來問題
- 例如,當前項目有一個第三方依賴,而這個依賴因爲某些緣由依賴了另外一個類庫的SNAPSHOT,那麼整個SNAPSHOT就會成爲當前項目的傳遞性依賴,而SNAPSHOT的不穩定性會直接影響到當前的項目,這時候就須要排除掉該SNAPSHOT,而且在當前項目中聲明該類庫的某個正式發佈版
<dependencies>
<dependency>
<groupId>com.lsy.myproject</groupId>
<artifactId>myproject-a</artifactId>
<version>1.0.0</version>
<exclusions>
<exclusion>
<groupId>com.lsy.myproject</groupId>
<artifactId>myproject-b</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.lsy.myproject</groupId>
<artifactId>myproject-b</artifactId>
<version>1.1.0</version>
</dependency>
</dependencies>
- 當一些依賴來自同一個項目的不一樣模塊,這些依賴的版本都應該是相同的,未來升級也是一塊兒升級,如Spring Framework,這時可使用properties元素定義Maven屬性
<properties>
<springframework.version>4.2.1</springframework.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<verison>${springframework.version}</verison>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<verison>${springframework.version}</verison>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<verison>${springframework.version}</verison>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<verison>${springframework.version}</verison>
</dependency>
</dependencies>
在軟件開發過程當中,一般會經過重構等方式不斷優化本身代碼,一樣,對於Maven項目的依賴也須要對其進行優化
- 去除多餘的依賴
- 顯示聲明某些必要的依賴
Maven會自動解析全部項目的直接依賴和間接依賴,而且根據規則判斷每一個依賴範圍,對於一些依賴衝突,也能進行調整,以確保任何一個構建只有惟一的版本在依賴中存在,此稱爲解析依賴
- mvn dependency:list 查看當前項目的已解析依賴
- mvn dependency:tree 以樹結構查看已解析依賴
mvn dependency:analyze 解析依賴
- Used undeclared dependencies:指項目中使用到的,可是沒有顯示聲明的依賴,這種依賴意味着潛在的風險,當前項目直接在使用它們,因此須要顯示聲明任何項目中直接用到的依賴
- Unused declared dependencies:指項目中未使使用的.但顯示聲明的依賴
在Maven世界中,任何一個以依賴,插件或項目構建的輸出,均可以成爲構件
任何一個構建都有其惟一的座標,根據這個座標能夠定義其在倉庫中的惟一存儲路徑,這就是Maven的倉庫佈局方式,如log4j:log4j:1.2.15這一依賴,其對應的倉庫路徑爲log4j/log4j/1.2.15/log4j-1.2.15.jar
- 路徑與座標的關係爲:groupId/artifactId/version/artifactId-verision.packaging
對於Maven來講,倉庫分爲兩類
- 本地倉庫
遠程倉庫
- 中央倉庫:Maven核心自帶的倉庫服務器,它包含了絕大部分開源的構件
- 私服:另外一種特殊的遠程倉庫,爲了節省寬帶和時間,應該在局域網內部構建一個私有倉庫服務器,用其代理全部外部的遠程倉庫,內部項目部署到私服上供其它項目使用
- 其餘公共庫
- 當Maven根據座標尋找構件時,它首先會查看本地倉庫,若是本地倉庫存在此構件,則直接使用,若是本地倉庫不存在此構件,或者須要查看是否有更新的構件版本,Maven就會去遠程倉庫查找,發現須要的構件後下載到本地倉庫再使用,若本地和遠程都沒有,則報錯
通常來講,在Maven項目目錄下,沒有注入lib/這樣用來存放依賴文件的目錄,當Maven執行編譯或測試時,若是須要使用依賴文件,它老是基於座標使用本地倉庫的依賴文件
- 默認狀況,在用戶目錄下路徑名爲.m2/repository/的倉庫目錄,當想自定義本地倉庫目錄地址時,能夠編輯~/.m2/setting.xml,設置localRepository元素來指定倉庫地址
<settings>
<localRepository>D:/java/repository/</localRepository>
</settings>- 默認狀況下,~/.m2/settings.xml文件是不存在的,用戶須要從Maven安裝目錄複製$M2_HOME/conf/settings.xml文件再編輯,建議不要直接修改全局目錄的settings.xml,而是在用戶目錄下進行修改
一個構建只有在本地倉庫中,才能由其它Maven項目使用
- 依賴Maven從遠程倉庫下載到本地倉庫中
- 將本地項目的構件安裝到Maven本地倉庫中 mvn clean install
安裝好Maven後,若是不執行任何Maven命令,本地倉庫目錄是不存在的,只有輸入第一條Maven命令後,Maven纔會建立本地倉庫,並根據配置和須要,從遠程倉庫下載至本地倉庫
- 這就比如藏書,本地倉庫比如書房,遠程倉庫比如書店,我須要讀書時先去書房找,當書房沒有時,就去書店買回放到書房,而且通常對每一個人來講,書房只有一個,而外面的書店能夠有多個
最原始的本地倉庫是空的,因此Maven必須知道至少一個可用的遠程倉庫,才能在執行Maven命令時下載到須要的構建,中央倉庫就是這樣一個默認的遠程倉庫,能夠經過解壓工具打開$M2_HOME/lib/maven-model-builder-3.0.jar中的org/apache/maven/model/pom-4.0.0.xml文件
<repositories>
<repository>
<id>central</id>
<name>Maven Repository Switchboard</name>
<url>http://repo1.maven/org/maven2</url>
<layout>default</layout>
<snapshots>
<enable>false</enable>
</snapshots>
</repository>
</repositories>
- 這段配置是全部Maven項目都會繼承的超級Pom文件,這段配置使用id central對中央倉庫進行惟一標識,其名稱爲Maven Repository Switchboard,它使用default倉庫佈局,也就是在以前介紹的倉庫佈局,最後snapshots元素表示不從該倉庫下載快照版
私服是一種特殊的遠程倉庫,它是架設在局域網內的倉庫服務,私服代理廣域網上的遠程倉庫,供局域網內的Maven用戶使用,當Maven須要下載構建的時候,它從私服請求,若是私服上不存在該構件,則從外部的遠程倉庫下載,緩存在私服上以後,再爲Maven的下載提供服務
- 此外,一些沒法從外部下載的構件也能夠從本地上傳到私服上供你們使用
私服的好處
- 節省外網帶寬:創建私服一樣能夠減小組織本身的開支,大量的對於外部倉庫的重複請求會消耗很大的帶寬,利用私服代理外部倉庫以後,對外的重複構件下載即可以消除,即下降外網帶寬的壓力
- 加速Maven構件:不停的鏈接請求外部倉庫是十分耗時的,但Maven的一些內部機制(如快照更新檢查井)要求Maven在執行構建時不停的檢查遠程倉庫數據,所以,當項目配置不少外部倉庫時,構建速度就會下降
- 部署第三方構件:當某個構件沒法從任何一個外部遠程倉庫獲取,創建私服以後,即可以將這些構件部署到這個內部的倉庫中,供內部的Maven項目使用
- 提升穩定性,加強控制:Maven構建高度依賴遠程倉庫,所以,大哥Internet不穩定的時候,Maven構建也會變得不穩定,甚至沒法構建,使用私服後,便是短暫時沒有Internet鏈接,因爲私服中有大量緩存,Maven依然能夠正常運行,而且私服中有不少額外的權限功能控制
- 下降中央倉庫的負荷:天天中央倉庫都須要面對大量的下載請求,使用私庫能夠下降對於中央倉庫的負荷
遠程倉庫的配置
不少狀況下,默認的中央倉庫沒法知足項目的需求,可能項目須要的構件存在於另外一個遠程倉庫中,能夠經過Pom/settings文件中來配置該倉庫
<!--> POM文件 </!-->
<project>
.....
<repositories>
<repository>
<id>jboss</id>
<name>JBoss Repository</name>
<url>http://repository.jboss.com/m...;/url>
<release>
<enabled>true</enabled>
</release>
<snapshots>
<enabled>false</enabled>
</snapshots>
<layout>default</layout>
</repository>
</repositories>
</project>
<!--> settings文件 </!-->
<profiles>
<profile>
<id>dev</id>
<activation>
<activatedByDefault>true</activatedByDefault>
</activation>
<repositories>
<repository>
<id>jboss</id>
<name>JBoss Repository</name>
<url>http://repository.jboss.com/m...;/url>
<release>
<enabled>true</enabled>
</release>
<snapshots>
<enabled>false</enabled>
</snapshots>
<layout>default</layout>
</repository>
</repositories>
</profile>
</profiles>
- 在repositories元素下,可使用repository子元素聲明一個或多個遠程倉庫,而且聲明一個id,任何一個倉庫聲明的id必須是惟一id
- Maven自帶的中央倉庫使用的id爲central,若是其餘倉庫使用該id,則會覆蓋中央倉庫的配置,該配置中的url指向了倉庫的地址
該配置中的release和snapshots元素用來控制Maven對於發佈版構件和快照版構件的下載,對於release和snapshots元素來講除了enabled子元素,還有updatePolicy和checksumPolicy元素
updatePolicy:用來配置Maven從遠程倉庫檢查更新的頻率,默認爲daily
- daily:天天
- never:從不
- always:每次
- interval :X :每隔X分鐘一次
checksumPolicy:配置Maven檢查文件失敗時的策略,默認爲warn
- fail:Maven遇到驗證和錯誤就讓構建失敗
- warn:Maven遇到驗證和錯誤就發出警告信息
- ignore:Maven遇到驗證和錯誤時徹底忽略
<snapshots\> <enabled\>true</enabled\> <updatePolicy\>daily</updatePolicy\> <checksumPolicy\>ignore</checksumPolicy\> </snapshots\> * 遠程倉庫的驗證 * 大部分遠程倉庫無需認證就能夠訪問,但出於安全考慮,咱們須要提供一些認證信息才能訪問一些遠程倉庫 * 配置認證信息和配置倉庫信息不一樣,倉庫信息能夠直接配置在POM文件中,可是認證信息必須配置在settings.xml文件中 * 其中server元素的id必須與POM文件中須要認證的repository元素的id徹底一致 <settings\> ...... <servers\> <server\> <id\>my-project</id\> <username\>user</username\> <password\>password</password\> </server\> </servers\> ....... </settings\> * 部署至遠程倉庫 * 私服一大做用就是部署第三方構件,包括組織內部生成的構件以及沒法從外部倉庫直接獲取的構件,不管是平常開發中生成的構件,仍是正式版本發佈的構件,都須要部署到倉庫中 * distributionManagement包含repository和snapshotRepository子元素,前者表示發佈版構件的倉庫,後者表示快照版的倉庫 * 往遠程倉庫部署構件時,每每須要認證,認證配置需在settings.xml中建立一個server元素,其id與倉庫的id匹配,不管從遠程倉庫下載構件,仍是部署構件至遠程倉庫,當須要認證時,配置方式都是同樣的 <!--> 在POM文件中配置distributionManagement </!--> <project\> ..... <distributionManagement\> <repository\> <id\>jboss-release</id\> <name\>JBoss Release Repository</name\> <url\>http://192.168.1.99/content/repository/jboss-release</url\> </repository\> <snapshotRepository\> <id\>jboss-snapshots</id\> <name\>JBoss Snapshots Repository</name\> <url\>http://192.168.1.99/content/repository/jboss-snapshots</url\> </snapshotRepository\> </distributionManagement\> ..... </project\>
- 在Maven的世界中,任何一個項目或者構件都必須有本身的版本,版本的值多是1.0.0,1.3-alpha-4,2.0,2.1-SNAPSHOT或者2.1-20191214.221414-13,其中1.0.0,1.3-alpha-4和2.0是穩定的發佈版本,而2.1-SNAPSHOT和2.1-20091214.221414-13是不穩定的快照版本
- 對於一個穩定的版本,若是倉庫中已經包含,那麼Maven就不會再去對照遠程倉庫進行更新,除非每次執行Maven命令前,清除本地倉庫中等待穩定版本,而對於一個正在迭代的項目,若是要實時更新版本的內容就須要頻繁的修改新的版本名稱,這樣是對版本號的濫用
- 針對這種狀況,使用快照版時,Maven會自動爲構件打上時間戳,所以,Maven就能隨時找到倉庫中該構建最新版本的文件,一旦有新的更新,就會去同步到本地倉庫.當項目通過完善的測試後須要發佈的時候,再將快照版本更改成發佈版本
- 快照版本只應該在組織內部的項目或模塊之間依賴使用,由於這時,組織對這些快照版本的依賴具備徹底的理解和控制權,項目不該該依賴任何組織外部的快照版本依賴,因爲快照版本的不穩定性,隨時可能發生變化,這樣的依賴會有潛在的危險
當本地倉庫沒有依賴構件的時候,Maven會自動從遠程倉庫下載,當依賴版本爲快照版本時,Maven會自動找到最新的快照,這背後的依賴機制能夠歸納以下
- 當依賴的範圍時system的時候,Maven直接從本地文件系統解析構件
- 根據依賴座標計算倉庫路徑後,嘗試從本地倉庫尋找構件,若是發現相應的構件,則解析成功
- 在本地倉庫不存在相應構件的狀況下,若是依賴的版本時顯式的發佈版本構件,如1.2,2.1-beta-1等,則遍歷全部的遠程倉庫,發現後,下載並解析使用
- 若是依賴的版本時RELEASE或LATEST,則基於更新策略讀取全部遠程倉庫的元數據groupId/artifact/maven-metadata.xml,將其與本地倉庫的對應元數據合併後,計算出RELEASE或LATEST真實的值,而後基於這個真實的值檢查本地倉庫和遠程倉庫
- 若是依賴的版本是SNAPSHOT,則基於更新策略讀取全部遠程倉庫的元數據groupId/artifactId/maven-metadata.xml,將其與本地倉庫的對應數據合併後,獲得最新的快照版本的值,而後基於該值檢查本地倉庫,或者從遠程倉庫下載
- 若是最後解析獲得的構件版本是時間戳格式,如1.4.1-20191104.121455-8,則複製其時間戳格式的文件至非時間戳格式,如SNAPSHOT,並使用該非時間戳格式的構件
<!--> 基於groupId和artifactId的maven-metadata.xml </!-->
<?xml version="1.0" encoding="UTF-8"?>
<metadata>
<groupId>org.sonatype.nexus</groupId>
<artifactId>nexus</artifactId>
<versioning>
<latest>1.4.2-SNAPSHOT</latest>
<release>1.3.7</release>
<versions>
<version>1.3.5</version>
<version>1.3.6</version>
<version>1.3.7</version>
<version>1.4.0-SNAPSHOT</version>
<version>1.4.1-SNAPSHOT</version>
<version>1.4.2-SNAPSHOT</version>
</versions>
<lastUpdated>20191214221133</lastUpdated>
</versioning>
</metadata>該XML文件列出了倉庫中存在的構件全部可用的版本,同時latest元素指向了這些版本中最新的那個版本1.4.2-SNAPSHOT,而release元素指向了這些版本中最新的發佈版本1.3.7,Maven經過合併多個遠程倉庫及本地倉庫的元數據,就能計算出基於全部倉庫的latest和release
- 須要注意的是,在依賴聲明使用LATEST和RELEASE是不推薦的作法,由於Maven隨時可能解析到不一樣的構件,且Maven不會明確告訴用戶這樣的變化
若是倉庫X能夠提供倉庫Y存儲的全部內容,那麼就能夠認爲X是Y的一個鏡像,因爲地理位置的因素,中央倉庫的下載速度會比較慢,這時咱們能夠配置Maven使用鏡像來代替中央倉庫,編輯settings.xml
<settings>
......
<mirrors>
<mirror>
<id>maven.net.cn</id>
<name>one of the central mirror in china</name>
<url>http://maven.net.cn/content/g...;/url>
<mirrorOf>central</mirrorOf>
</mirror>
</mirrors>
......
</settings>
<mirrorOf>的值爲central,表示該配置爲中央倉庫的鏡像,任何對於中央倉庫的請求都會轉至該鏡像,用戶也可使用一樣的方法配置其餘倉庫的鏡像,另外三個元素id,name,url與通常倉庫配置無異,表示該鏡像倉庫的惟一標識,名稱以及地址
- 若該鏡像要進行驗證,即基於該id配置倉庫認證
<settings\> ..... <mirrors\> <mirror\> <id\>internal-repository</id\> <name\>Internal Repository Mananger</name\> <url\>http://192.168.1.100/maven2/</url\> <mirrorOf\>\*</mirrorOf\> </mirror\> </mirrors\> ..... </settings\>
以上mirrorOf元素的值爲*,表示該配置是全部Maven倉庫的鏡像,對於任何遠程倉庫的請求都會被轉至該指定的倉庫,若是鏡像倉庫須要驗證,則配置一個id爲internal-repository的<server>便可
- <mirrorOf>*<mirrorOf>:匹配全部遠程倉庫
- <mirrorOf>external:*<mirrorOf>:匹配全部遠程倉庫,使用localhost的除外,使用file://協議的除外,也就是說,匹配全部不在本機上的遠程倉庫
- <mirrorOf>repo1,repo2<mirrorOf>:匹配倉庫repo1和repo2,用逗號分隔多個倉庫
- <mirrorOf>*,! repo1<mirrorOf>:匹配全部的遠程倉庫,除repo1外,使用感嘆號將倉庫從匹配中排除
- 須要注意的是,因爲鏡像倉庫徹底屏蔽了被鏡像倉庫,當鏡像倉庫不穩定或中止服務時,Maven仍將沒法訪問被鏡像倉庫,所以沒法下載構件
在Maven出現以前,項目構建的生命週期就已經存在,可是不一樣的公司和開發人員雖然一樣在作構件工做,其對於不一樣的項目卻不可以重用,只能從新定製開發,而Maven的生命週期就是爲了對全部的構件過程進行抽象和統一
- 這個生命週期包含了項目的清理,初始化,編譯,測試,打包,集成測試,驗證,部署和站點生成等幾乎全部的構件步驟,也就是說幾乎全部項目的構件,都能映射到這樣一個生命週期上
Maven的生命週期是抽象的,這意味着生命週期自己不作任何實際的工做,在Maven的設計中,實際的任務(如編譯主代碼)都交由插件完成,這種思想和設計模式的模方法相似,在父類中定義算法的總體結構,子類能夠經過實現或重寫父類的方法來控制實際的行爲
public abstract class Template{
public void build(){
initialize();
compile();
test();
packagee();
integrationTest();
deploy();
}protect abstract void initialize();
protect abstract void compile();
protect abstract void test();
protect abstract void packagee();
protect abstract void integrationTest();
protect abstract void deploy();
}
- 在Maven的生命週期中抽象了各個步驟,定義了他們的次序,可是沒有提供具體的實現,而經過插件機制爲每一個構件步驟綁定一個或多個插件行爲,並且Maven爲大多數構件步驟都綁定了默認的插件,例如,針對編譯的maven-compiler-plguin,針對測試的maven-surefire-plugin等
- Maven定義的生命週期和插件機制一方面保證了全部Maven項目有一致的構件標準,另外一方面又經過默認的插件簡化和穩定實際項目的構件,此外,該機制還提供了足夠的擴展,用戶能夠經過配置現有的插件或自定義插件來自定義構件行爲
三套聲明週期
Maven擁有三套相互獨立的生命週期,他們分別爲clean,default和site,每一個生命週期包含一些階段,這些階段是有序的,而且後面的階段依賴於前面的階段,可是三套聲明週期自己是互相獨立的
clean生命週期:清理項目
- pre-clean:執行一些清理前須要完成的工做
- clean:清理上次構件生成的文件
- post-clean:執行一些清理後須要完成的工做
default聲明週期:定義了真正的構件所需執行的全部步驟
- validate
- initialize
- generate-sources
- process-sources:處理項目主資源文件,通常來講針對/src/main/resources目錄的內容進行變量替換等工做後,複製到項目輸出的主classpath目錄中
- compile:編譯項目的主源碼,通常來講針對/src/main/java目錄下的Java文件至目錄輸出的主classpath目錄中
- process-classes
- generate-test-sources
- process-test-sources:處理項目測試資源文件,通常來講針對/src/test/resources目錄的內容進行變量替換工做後,複製到項目輸出的測試classpath目錄
- test-compile:編譯項目的測試代碼,通常來講針對/src/test/java目錄下的java文件至輸出的測試classpath目錄中
- test:使用單元測試框架運行測試,測試代碼不會被打包或部署
- prepare-package
- package:接收編譯好的代碼,打包成可發佈的格式,如jar
- pre-integration-test
- integration-test
- post-integration-test
- verify
- install:將包安裝到Maven本地倉庫,供本地其餘Maven項目使用
- deploy:將最終的包複製到遠程倉庫,供其餘開發人員和Maven項目使用
site聲明週期:創建和發佈項目站點,Maven可以基於POM所包含的信息,自動生成一個友好的站點供交流和發佈項目信息
- pre-site:執行一些在生成項目站點以前須要完成的工做
- site:生成項目站點文檔
- post-site:執行一些在生成項目站點後須要完成的工做
- site-deploy:將生成的項目站點發布到服務器上
命令行與生命週期
從命令行執行Maven任務最主要方式就是調用Maven的生命週期,各個生命週期是相互獨立的,而生命週期的階段是有先後依賴關係的
- mvn clean:該命令調用clean生命週期的clean階段,實際執行階段爲pre-clean和clean
- mvn test:該命令調用default生命週期的test階段,實際執行的階段爲default生命週期的validate,initialize直到test的全部階段,這也解釋爲何在執行測試的時候,項目代碼可以自動編譯
- mvn clean install:該命令調用clean生命週期的clean階段和default生命週期的install階段
- mvn clean deploy site-deploy:該命令調用clean生命週期的clean階段,default生命週期的deploy階段和site生命週期的site-deploy階段
Maven的核心僅僅定義了抽象的生命週期,具體的任務是交由插件完成的,插件以獨立的構件形式存在
- 對於插件自己而言,爲了可以複用代碼,它每每可以完成多個任務,爲每一個功能編寫一個插件顯然不合理,由於這些任務背後有大量可複用的代碼,所以,這些功能彙集到一個插件裏面,每一個功能就是一個插件目標
Maven的生命週期和插件相互綁定,用以完成實際的構件任務,具體而言,是生命週期的階段與插件的目標相互綁定,以完成某個具體的構件任務
- 例如:編譯這一任務對應了default生命週期的compile這一階段,而maven-compiler-plugin這一插件的compile目標能完成此任務
自定義綁定
除了內置綁定之外,用戶還可以本身選擇將某個插件目標綁定到生命週期的某個階段上,這種自定義綁定方式能讓Maven項目在構件過程當中執行更多更豐富的任務
- 當須要建立項目的源碼jar包,maven-source-plugin能夠幫咱們完成任務,可是內置的插件綁定關係中並無涉及這一任務,所以須要自行配置,它的jar-no-fork目標能將項目主代碼打包成jar文件,能夠將其綁定到default生命週期的verify階段,在執行完集成測試和安裝構件以前建立源碼jar包
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.1.1</version>
<executions>
<execution>
<id>attach-sources</id>
<phase>verify</phase>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>在POM的build元素下的plugins子元素中聲明插件的使用,上例用到了是maven-source-plugin,其groupId爲org.apache.maven.plugins,用戶老是應該聲明一個非快照版本,這樣能夠避免因爲插件版本變化形成的構件不穩定性
- 除了基本的插件座標聲明外,還有插件執行配置,executions下每一個execution子元素能夠用來配置執行一個任務,該例配置了一個id爲attach-sources的任務,經過phase配置將其綁定到verify生命週期階段上,再經過goals配置指定要執行的插件目標,在運行mvn verify時就會執行該任務
有時候.便是不經過phase元素配置生命週期階段,插件目標也能綁定到生命週期中去,緣由是:有不少插件的目標在編寫時就已經定義了默認綁定階段,可使用maven-help-plugin查看詳細信息
mvn help:describe -Dplugin=org.apache.maven.plugins:maven-source-plugin:2.1 -Ddetail
- Bound to phase : package 默認綁定到package生命週期
- 當插件目標被綁定到不一樣生命週期階段的時候,其執行順序會由生命週期階段的前後順序決定,若是多個目標被綁定到同一個階段,他們的順序就由插件聲明的前後順序決定
用戶能夠經過配置插件目標的參數,進一步調整插件目標所執行的任務,幾乎全部Maven插件的目標均可以配置參數,用戶能夠經過命令行和POM配置等方式來配置這些參數
命令行插件配置
在平常的Maven使用中,咱們一般從命令行輸入並執行Maven命令,不少插件目標的參數都支持從命令行配置,用戶能夠在Maven命令中使用-D參數,並伴隨一個參數鍵=參數值的形式,來配置插件目標的參數
mvn install -Dmaven.test.skip = ture:給maven.surefire-plugin提供一個maventest.skip參數,當參數爲true時,就會跳過執行測試
- 參數-D是Java自帶的,其功能是經過命令行設置一個Java系統屬性,Maven簡單地重用了該參數,在準備插件的時候檢查系統屬性來實現插件參數的配置
POM中插件全局配置
並非全部的插件參數都適合用命令行配置,有些參數的值從項目建立到項目發佈都不會改變,或者說不多改變,這種狀況下在POM文件中一次性配置比重複在命令行輸入更合理
- 用戶能夠在聲明插件的時候,對插件進行一個全局的配置,全部基於該插件的目標任務,都會使用這些配置,如:maven-compiler-plugin來編譯1.8版本的源文件,生成與JVM1.8兼容的字節碼文件
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifact>maven-compiler-plugin</artifact>
<version>2.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
當遇到一個構建任務時,不只須要知道去哪裏找到合適的插件,還須要詳細的瞭解插件的配置點,因爲Maven的插件很是多,其中大部分沒有完善的文檔,所以,經過幫助命令來了解插件顯得很是重要
使用maven-help-plugin描述插件
除了訪問在線的插件文檔外,能夠藉助maven-help-plugin來獲取插件的詳細信息
mvn help:decribe -Dplugin = org.apache.maven.plugins:maven-compiler-plugin:2.1
- 這裏執行的是maven-help-plugin的decribe目標,在參數plugin中輸入須要描述插件的groupId,artifactId和version,Maven在命令行的輸出maven-compiler-plugin的簡要信息,包括插件的座標,目標前綴和目標等信息
在描述插件時,能夠省去版本信息,Maven會自動獲取最新版原本進行表述,並能夠用目標前綴來替換座標
- mvn help:describe -Dplugin=compiler
若是僅僅描述某個插件目標的信息,則加上goal參數,更詳細的信息,則加上detail參數
- mvn help:describe -Dplugin=compiler -Dgoal=compile -Ddetail
爲了方便用戶使用和配置插件,Maven不須要用戶提供完整的插件座標信息,就能夠解析獲得正確的插件
插件倉庫
- 與依賴構件同樣,插件構件一樣基於座標存儲在Maven倉庫中,在須要的時候Maven先從本地倉庫尋找,若是不存在,則從遠程倉庫查找,找到插件以後,再下載到本地倉庫使用
- 不一樣於repositories以及repository子元素,插件的遠程倉庫使用pluginRepositories和pluginRepository配置
<pluginRepositories>
<pluginRepository>
<id>central</id>
<name>Maven Plugin Resository</name>
<url>http://repo1.maven.org/maven2</url>
<layout>default</layout>
<snapshots>
<enabled>false</enabled>
</snapshots>
<release>
<enabled>true</enabled>
<updatePolicy>never</updatePolicy>
</release>
</pluginRepository>
</pluginRepositories>插件默認的groupId
- 在POM中配置插件時,若是該插件時Maven的官方插件(即groupId爲org.apache.maven.plugins),就能夠省略groupId,Maven在解析該插件的時候,會自動使用默認groupId不起
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>解析插件版本
一樣爲了簡化插件的配置和使用,在用戶沒有提供插件版本的狀況下,Maven會自動解析插件版本
- 首先Maven在超級POM中爲全部核心插件設定了版本,超級POM是全部Maven項目的父POM,全部項目都繼承這個超級POM配置,所以即便用戶不加任何配置,Maven在使用核心插件時,它的版本就已經肯定了
- 若是用戶使用的某個插件沒有設定版本,而且這個插件也不屬於核心插件,Maven機會去檢查全部倉庫中可用的版本,經過倉庫元數據groupId/artifactId/maven-metadata.xml文件,如maven-compiler-plugin插件爲例,它在org/apache/maven/plugins/maven-compiler-plugin/maven-metadata.xml
<?xml version="1.0" encoding="UTF-8"?>
<metadata>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<versioning>
<latest>2.1</latest>
<release>2.1</release>
<versions>
<version>2.0-beta-1</version>
<version>2.1</version>
<version>2.2</version>
<version>2.3</version>
</versions>
<lastUpdated>20190102092331</lastUpdated>
</versioning>
</metadata>- Maven遍歷本地倉庫和全部遠程倉庫,將倉庫元數據合併後,就能計算出latest和release的值,maven3將版本解析到最新的非快照版,如2.1
- Maven命令支持使用插件前綴來簡化插件的使用,插件前綴和groupId:artifact是一一對應的,這種匹配關係存儲在倉庫元數據中,與以前提到的groupId/artifactId/maven-metadata.xml不一樣,這裏倉庫元數據爲groupId/maven-metadata.xml,通常插件都位於/org/apache/maven/plugins/和org/code-haus/mojo/,能夠經過settings.xml配置讓Maven檢查其餘groupId上的插件倉庫元數據
<settings>
<pluginGroups>
<pluginGroup>com.my.plugins</pluginGroup>
</pluginGroups>
</settings>- 在倉庫的元數據文件中能夠看到插件的前綴定義
<metadata>
<plugins>
<plugin>
<name>Maven Clean Plugin</name>
<prefix>clean</prefix>
<artifact>maven-clean-plugin</artifact>
</plugin>
<plugin>
<name>Maven Compiler Plugin</name>
<prefix>compile</prefix>
<artifact>maven-compile-plugin</artifact>
</plugin>
<plugin>
<name>Maven Dependency Plugin</name>
<prefix>dependency</prefix>
<artifact>maven-dependency-plugin</artifact>
</plugin>
</plugins>
</metadata>
aggregator
<modelVersion>4.0.0</modelVersion>
<groupId>com.lsy.project</groupId>
<artifact>project-aggregator</artifact>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Aggregator</name>
<modules>
<module>project-email</module>
<module>project-register</module>
</modules>
- 對於聚合模塊,其打包方式packaging的值必須爲pom
經過modules元素來實現聚合,用戶能夠經過在一個打包方式爲pom的Maven項目中聲明任意數量的module元素來實現模塊的聚合,這裏每一個module的值都是一個當前POM的相對目錄
- 如aggregator的POM路徑爲.../project-aggregator/pom.xml,那麼project-email對應 的目錄爲.../project-aggregator/project-email/,而且目錄中包含了pom.xml,src/main/java,src/test/java等內容,離開了project-aggregator也能獨立建立=
- 聚合模塊和其餘模塊的目錄結構並不是必定要父子關係,也能夠是平行關係
<modules>
<module>../prject-email</module>
<module>../project-register</module>
</modules>- 當在聚合模塊中執行mvn clean install時,Maven首先解析聚合模塊的POM,分析要構件的模塊,並計算出一個反應堆構件順序(Reactor Build Order),而後根據這個順序構件各個模塊
聚合模塊的構件順序
- Maven按序讀取POM文件,若是POM沒有依賴模塊,那麼就構件模塊,不然就先構件其依賴模塊
從以上的聚合模塊和其餘模塊中能夠看到,多個被管理模塊的POM文件中會有大量重複的相同配置,他們有相同的groupId和version,相同的依賴,相同的插件配置,而經過POM文案的繼承能夠消除重複
project-parent父模塊
<modelVersion>4.0.0</modelVersion>
<groupId>com.lsy.project</groupId>
<artifactId>project-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Parent</name>
- 父模塊的POM文件使用與其餘模塊一直的groupId和version,它的packaging方式爲pom,與聚合模塊一致
- 父模塊主要是爲了消除配置的重複,所以它自己不包含除POM文件以外的項目文件,也就不須要src/main/java之類的文件夾了
project-email子模塊
<parent>
<groupId>com.lsy.project</groupId>
<artifactId>project-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../project-parent/pom.xml</relativePath>
</parent>
<artifactId>project-email</artifactId>
<name>Email</name>
<dependencies>
.....
</dependencies>
使用parent元素聲明父模塊,parent下子元素groupId,artifactId,version指定父模塊的座標,元素relativePath表示父模塊POM的相對路徑,表示Email模塊和其父模塊是在平行的目錄下
- 當構件項目時,Maven會首先根據relativePath檢查父POM文件,若是找不到再從本地倉庫找,relativePath的默認值爲../pom.xml,也就是說,Maven默認父POM在上一層目錄
- 子模塊沒有groupId和version,是由於子模塊隱式的從父模塊繼承了這兩個元素,從而消除了沒必要要的配置
可繼承的POM元素
- groupId:項目組ID,項目座標的核心元素
- version:項目版本,項目座標的核心元素
- description:項目的描述信息
- organization:項目的組織信息
- inceptionYear:項目的創始年份
- url:項目的URL地址
- develops:項目的開發者信息
- contributors:項目貢獻者信息
- distributionManagement:項目的部署配置
- issueManagement:項目的缺陷跟蹤系統信息
- ciManagement:項目的持續集成系統信息
- scm:項目的版本控制系統信息
- mailingList:項目的郵件列表信息
- properties:自定義屬性
- dependencies:項目的依賴配置
- dependencyManagement:項目的依賴管理配置
- repositories:項目的倉庫配置
- pluginRepositories:項目的插件倉庫配置
- build:包括項目的源碼目錄配置,輸出目錄配置,插件配置,插件管理配置等
- reporting:項目的輸出目錄配置,報告插件配置等
依賴管理
- dependencies元素是能夠被繼承的,說明依賴是會被繼承的,因此咱們能夠將子模塊共有的依賴配置到父模塊中,子模塊就能夠移除這些依賴,簡化配置
上述方法是可行的,可是可能未來會有新的模塊並不須要父模塊中的一些依賴,這就會產生不合理的現象,從而Maven提供了dependencyManagement元素,既能讓子模塊繼承到父模塊的依賴配置,又能保證子模塊依賴使用的靈活性
- 在dependencyManagement元素下的依賴聲明不會引入實際的依賴,不過它可以約束dependencies下的依賴使用
<modelVersion\>4.0.0</modelVersion\> <groupId\>com.lsy.project</groupId\> <artifactId\>project-parent</artifactId\> <version\>1.0.0-SNAPSHOT</version\> <packaging\>pom</packaging\> <name\>Parent</name\> <properties\> <springframework.version\>4.3.1</springframework.version\> </properties\> <dependencyManagement\> <dependencies\> <dependency\> <groupId\>org.springframework</groupId\> <artifactId\>spring-core</artifactId\> <version\>${springframwork.version}</version\> </dependency\> <dependency\> <groupId\>org.springframework</groupId\> <artifactId\>spring-beans</artifactId\> <version\>${springframwork.version}</version\> </dependency\> <dependency\> <groupId\>org.springframework</groupId\> <artifactId\>spring-context</artifactId\> <version\>${springframwork.version}</version\> </dependency\> </dependencies\> </dependencyManagement\> * 這裏使用dependencyManagement聲明的依賴既不會給parent模塊引入依賴,也不會給子模塊引入依賴,不過這段配置是會被繼承的 * 子模塊POM <parent\> <groupId\>com.lsy.project</groupId\> <artifactId\>project-parent</artifactId\> <version\>1.0.0-SNAPSHOT</version\> <relativePath\>../project-parent/pom.xml</relativePath\> </parent\> <artifactId\>project-email</artifactId\> <name\>Email</name\> <dependencies\> <dependency\> <groupId\>org.springframework</groupId\> <artifactId\>spring-core</artifactId\> </dependency\> <dependency\> <groupId\>org.springframework</groupId\> <artifactId\>spring-beans</artifactId\> </dependency\> <dependency\> <groupId\>org.springframework</groupId\> <artifactId\>spring-context</artifactId\> </dependency\> </dependencies\> * 使用這種依賴管理機制,能夠在父POM中使用dependencyManagement聲明依賴可以統一項目規範中的依賴版本,當版本在父POM中聲明以後,子模塊使用依賴時就不須要聲明瞭.也不會發生多個子模塊使用依賴版本不一致的狀況 * scoper元素的import依賴範圍只在dependencyManagement元素下才有效果,使用該範圍的依賴一般指向一個POM文件,做用是將POM中的dependencyManagement配置導入併合併到當前POM的dependencyManagement元素中 * 因此除了複製配置和繼承父模塊這兩種方式外,還能夠經過import範圍依賴導入這一配置 <dependencyManagement\> <dependencies\> <dependency\>com.lsy.project</dependency\> <artifactId\>project-parent</artifactId\> <version\>1.0-SNAPSHOT</version\> <type\>pom</type\> <scope\>import</scope\> </dependencies\> </dependencyManagement\>
插件管理
- Maven提供了dependencyManagement元素幫助管理依賴,相似的,Maven也提供了pluginManagement元素管理插件,該元素中配置的依賴一樣不會形成實際的插件調用行爲,而POM文件中配置了真正的plugin元素,而且groupId和artifact一致時,纔會產生實際的插件行爲
- 父模塊
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.1.1</version>
<executions>
<execution>
<id>attach-source</id>
<phase>verify</phase>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</pluginManagement>
</build>子模塊
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifact>maven-source-plugin</artifact>
</plugin>
</plugins>
</build>
- 子模塊使用了maven-source-plugin插件,同時又繼承了父模塊的pluginManagement配置
集合與繼承的關係
多模塊的聚合與繼承實際上是兩個概念,其目的是徹底不一樣的,前者爲了方便快速的構件項目,後者主要爲了消除重複配置
- 對於聚合模塊來講:它知道有哪些模塊被聚合,可是那些被聚合的模塊並不知道這個聚合模塊的存在
- 對於繼承關係的父POM來講:它不知道有哪些子模塊繼承於它,可是那些子模塊都必須知道本身的父模塊是什麼
- 在實際項目中,一個POM每每便是聚合模塊,又是父模塊
約定優於配置
Maven提倡"約定優於配置",Maven只須要一個簡單的POM文件,就能夠完成清除,構件等任務
- 源碼目錄:src/main/java/
- 編譯輸出目錄爲:target/classes/
- 打包方式爲:jar
- 包輸出目錄爲:target/
- Maven此機制的來源就是超級POM文件,此文件在$MAVEN_HOME/lib/maven-model-builder-x.x.x.jar中的org/apache/maven/model/pom-4.0.0.xml路徑下
<repositories>
<repository>
<id>central</id>
<name>Maven Repository Swithboard</name>
<url>http://repo1.maven.org/maven2</url>
<layout>default</layout>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>central</id>
<name>Maven Plugin Repository</name>
<url>http://repo1.maven.org/maven2</url>
<layout>default</layout>
<snapshots>
<enabled>false</enabled>
</snapshots>
<releases>
<updatePolicy>never</updatePolicy>
</releases>
</pluginRepository>
</pluginRepositories>
<build>
<!--> 定義了項目的主輸出目錄 </!-->
<directory>${project.basedir}/target</directory>
<!--> 主代碼輸出目錄 </!-->
<outputDirectory>${project.build.directory}/classes</outputDirectory>
<!--> 最終構件的名稱格式 </!-->
<finalName>${project.artifactId}-${project.version}</finalName>
<!--> 測試代碼輸出目錄 </!-->
<testOutputDirectory>${project.build.directory}/test-
classes</testOutputDirectory>
<!--> 主源碼目錄 </!-->
<sourceDirectory>${project.basedir}/src/main/java</sourceDirectory>
<!--> 腳本源碼目錄 </!-->
<scriptSourceDirectory>src/main/scripts</scriptSourceDirectory>
<!--> 測試源碼目錄 </!-->
<testSourceDirectory>${project.basedir}/src/test/java</testSourceDirectory>
<!--> 主資源目錄 </!-->
<resources>
<resource>
<directory>${project.basedir}/src/main/resources</directory>
</resource>
</resources>
<!-- >測試資源目錄 </!-->
<testResources>
<testResource>
<directory>${project.basedir}/src/test/resources</directory>
</testResource>
</testResources><!--> 核心插件版本 </!-->
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.3</version>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.3</version>
</plugin>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>2.3</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.0.3</version>
</plugin>
......
</plugins>
</pluginManagement>
</build>
Nexus倉庫有四種類型
- group:倉庫組
- hosted:宿主倉庫
- proxy:代理倉庫
- virtual:虛擬倉庫
* Maven能夠從宿主倉庫下載構件,Maven也能夠從代理倉庫下載構件,而代理倉庫會簡介的從遠程倉庫下載並緩存構件 * 而通常爲了方便,咱們會從倉庫組下載構件,而倉庫組沒有實際的內容,它只是管理一組實際的倉庫,當它接收到請求時,它會轉向其包含的宿主倉庫或代理倉庫得到實際構件的內容
倉庫的Policy屬性
- Release:發佈版
- Snapshot:快照版
- 在POM文件中配置倉庫和插件倉庫------只對當前項目有效
<project>
.......
<repositories>
<repository>
<id>nexus</id>
<name>Nexus</name>
<url>http://localhost:8081/nexus/content/groupId/public/</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>nexus</id>
<Name>Nexus</Name>
<url>http://localhost:8081/nexus/content/groupId/public/</url>
<release>
<enabled>true</enabled>
</release>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
.......
</project>在settings.xml文件中配置---全局有效
- settings.xml文件並不支持直接配置repositories和pluginRepositories,可是能夠經過Maven提供的Profile機制,讓用戶將倉庫配置到settings.xml中的Profile中
<settings\> .... <profiles\> <profile\> <id\>nexus</id\> <repositories\> <repository\> <id\>nexus</id\> <name\>Nexus</name\> <url\>http://localhost:8081/nexus/content/groupId/public/</url\> <release\> <enabled\>true</enabled\> </release\> <snapshots\> <enabled\>ture</enabled\> </snapshots\> </repository\> </repositories\> <pluginRepositories\> <pluginRepository\> <id\>nexus</id\> <Name\>Nexus</Name\> <url\>http://localhost:8081/nexus/content/groupId/public/</url\> <release\> <enabled\>true</enabled\> </release\> <snapshots\> <enabled\>true</enabled\> </snapshots\> </pluginRepository\> </pluginRepositories\> </profile\> </profiles\> <!--> 激活指定id的profile </!--> <activeProfiles\> <activeProfile\>nexus</activeProfile\> </activeProfiles\> </settings\>
以上配置已經能讓Maven項目從Nexus私服下載構件了,可是Maven仍然會去訪問中央倉庫,如今咱們想要將全部請求都僅僅經過私服,這就須要藉助於Maven鏡像了
<settings>
.....
<mirrors>
<mirror>
<id>nexus</id>
<mirrorOf>*</mirrorOf>
<url>http://localhost:8081/nexus/content/groupId/public/</url>
</mirror>
</mirrors><profiles>
<profile>
<id>nexus</id>
<repositories>
<repository>
<id>central</id>
<name>central</name>
<url>http://central</url>
<release>
<enabled>true</enabled>
</release>
<snapshots>
<enabled>ture</enabled>
</snapshots>
</repository>
</repositories><pluginRepositories>
<pluginRepository>
<id>central</id>
<Name>Nexus</Name>
<url>http://central</url>
<release>
<enabled>true</enabled>
</release>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</profile>
</profiles><!--> 激活指定id的profile </!-->
<activeProfiles>
<activeProfile>nexus</activeProfile>
</activeProfiles>
</settings>
- 這裏倉庫和插件倉庫配置的id都爲central,它們將會覆蓋POM中央倉庫的配置,而且這裏的url已經可有可無,由於全部的請求都會經過鏡像訪問私服地址
若是隻是爲了代理外部公共倉庫,那麼Nexus的代理倉庫就已經徹底足夠了,對於另外一類Nexus倉庫---宿主倉庫來講,他們主要做用是存儲組織內部的,或一些沒法從公共倉庫得到的第三方構件,用戶能夠經過配置Maven自動部署構件至Nexus的宿主倉庫
使用Maven部署構件至Nexus
- 平常開發生成的快照版本構件能夠直接部署到Nexus中策略爲Snapshot的宿主倉庫中,項目正式發佈的構件則應該部署到Nexus中策略爲Release的宿主倉庫中
- POM文件配置
<project>
.....
<distributeManagement>
<repository>
<id>nexus-releases</id>
<name>Nexus Release Repository</name>
<url>http://localhost:8081/nexus/content/repositories/
release/</url>
</repository>
<snapshotRepository>
<id>nexus-snapshots</id>
<name>Nexus Snapshots Repository</name>
<url>http://localhost:8081/nexus/content/repositories/
snapshots</url>
</snapshotRepository>
</distributeManagement>
.....
</project>- Nexus的倉庫對於匿名用戶只是可讀的,爲了可以部署構件,還須要在settings.xml中配置認證信息
<settings>
....
<servers>
<server>
<id>nexus-releases</id>
<username>admin</username>
<password>admin123</password>
</server>
<server>
<id>nexus-snapshots</id>
<username>admin</username>
<password>admin123</password>
</server>
</servers>
....
</settings>
<properties>
<springframework.version>4.3.1</springframework.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${springframwork.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${springframwork.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
- 這是最多見的Maven屬性使用的方式,經過properties元素使得用戶自定義一個或多個Maven屬性,而後在POM的其餘地方使用${屬性名稱}的方式來引用該屬性,這種作法的最大意義是消除重複
Maven的屬性有六類
內置屬性
- ${basedir}:表示項目根目錄,即包含pom.xml文件的目錄
- ${version}:表示項目的版本
POM屬性:用戶可使用該類屬性引用POM文件中對應元素的值
${project.artifactId}:對應<project><artifactId>元素的值
- ${project.build.sourceDirectory}:項目的主源碼目錄.默認爲/src/main/java/
- ${project.build.testSourceDirectory}:項目的測試代碼源碼目錄.默認爲/src/test/java/
- ${project.build.directory}:項目構件輸出目錄.默認爲/target
- ${project.build.outputDirectory}:項目主代碼編譯輸出目錄.默認爲/target/classes
- ${project.build.testOutputDirectory}:項目測試代碼編譯輸出目錄.默認爲/target/test-classes
- ${project.build.groupId}:項目的groupId
- ${project.build.artifactId}:項目的artifactId
- ${project.build.build.finalName}:項目打包輸出文件的名稱,默認爲${project.artifactId}
-${project.version}
- 自定義屬性:<properties>元素下自定義的Maven屬性
- settings屬性:與POM屬性同理,用戶使用settings.來引用,如:${settings.localRepository}指向用戶本地倉庫的地址
Java系統屬性:全部Java系統屬性均可以使用Maven屬性引用,如${user.home}指向用戶目錄
- 能夠經過mvn help:system查看全部的java系統屬性
環境變量屬性:全部環境變量均可以用env.來引用,例如:${env.JAVA_HOME}
- 能夠經過 mvn help:system查看全部的環境變量
- 在不一樣的開發環境中,項目的源碼應該使用不一樣的方式進行構件,最多見的就是數據庫的配置了,在開發中,有些項目會在src/main/resources/目錄下放置不一樣環境下的數據庫配置
database.jdbc.driverClass = com.mysql.jdbc.Driver
database.jdbc.connectionURL = jdbc:mysql://localhost:3306/dev
database.jdbc.username = dev
database.jdbc.password = dev-passwd
database.jdbc.driverClass = com.mysql.jdbc.Driver
database.jdbc.connectionURL = jdbc:mysql://localhost:3306/test
database.jdbc.username = test
database.jdbc.password = test-passwd- 爲了應對環境的變化,咱們可使用Maven屬性將這些會發生變化的部分提取出來
database.jdbc.driverClass = ${db.driver}
database.jdbc.connectionURL = ${db.url}
database.jdbc.username = ${db.username}
database.jdbc.password = ${db.password}在settings.xml中經過profile定義不一樣環境下的配置數據
<profiles>
<profile>
<id>dev</id>
<properties>
<db.driver>com.mysql.jdbc.Driver</db.driver>
<db.url>jdbc:mysql://localhost:3306/dev</db.url>
<db.username>dev</db.username>
<db.password>dev-passwd</db.password>
</properties>
</profile><profile>
<id>test</id>
<properties>
<db.driver>com.mysql.jdbc.Driver</db.driver>
<db.url>jdbc:mysql://localhost:3306/test</db.url>
<db.username>test</db.username>
<db.password>test-passwd</db.password>
</properties>
</profile>
</profiles>
須要注意的是:
- Maven屬性默認只會在POM文件中才會被解析,也就是說${db.username}放到POM文件中會變成dev或test,可是在src/main/resources/目錄下仍然仍是${db.username},所以,須要讓Maven解析資源文件中的Maven屬性
- 資源文件處理的是maven-reosurces-plugin作事,它默認的行爲只是將項目主資源文件複製到主代碼編譯輸出目錄
開啓資源過濾
- Maven默認的主資源目錄和測試目錄的定義是在超級POM文件中
<resources\> <resource\> <directory\>${project.basedir}/src/main/resources</directory\> <filtering\>true</filtering\> </resource\> </resources\> <testResources\> <testResource\> <directory\>${project.basedir}/src/test/resources</directory\> <filtering\>true</filtering\> </testResource\> </testResources\>
- 經過mvn clean install -Pdev : 經過mvn 的 -P參數表示在命令行激活一個profile=dev的profile
激活Pofile
命令行激活
用戶可使用mvn命令行參數-P 加上profile的id來激活profile,多個id之間逗號分隔
- mvn clean install -Pdev,-Ptest
settings文件顯示激活
- 若是用戶但願某個profile默認一直處於激活狀態,就能夠配置settings.xml文件的activeProfile元素,表示其配置的profile對全部項目都處於激活狀態
<settings>
.....
<activeProfiles>
<activeProfile>dev</activeProfile>
</activeProfiles>
.....
</settings>系統屬性激活
- 用戶能夠配置當前某系統屬性存在時,自動激活profile
<profiles>
<profile>
<activation>
<property>
<name>test</name>
</property>
</activation>
.....
</profile>
</profiles>- 用戶能夠配置當前某系統屬性存在且等於a時,自動激活profile
<profiles>
<profile>
<activation>
<property>
<name>test</name>
<value>a</value>
</property>
</activation>
.....
</profile>
</profiles>用戶能夠在命令行聲明系統屬性
- mvn clean install -Dtest=x
操做系統環境變量激活
- Profile還能夠根據操做系統環境激活
<profiles>
<profile>
<activation>
<os>
<name>Windows10</name>
</os>
</activation>
.....
</profile>
</profiles>文件存在與否激活
- Maven能夠根據項目中某個文件是否存在來決定是否激活profile
<profiles>
<profile>
<activation>
<file>
<missing>a.properties</missing>
<exists>b.properties</exists>
</file>
</activation>
.....
</profile>
</profiles>默認激活
- 用戶能夠在定義profile的時候指定其默認激活
<profiles>
<profile>
<id>dev</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
.....
</profile>
</profiles>以上激活優先級從上之下依次減少
- 能夠經過mvn help:active-profiles來查看當前激活的profile
- 能夠經過mvn help:all-profiles來查看全部的profile
profile的種類
根據具體的須要,能夠在一下位置聲明profile
Maven
Maven可翻譯爲"知識的積累" or"專家",是一款成功的開源跨平臺的項目管理工具,不管小型的開源類庫項目,仍是大型的企業級應用;不管傳統的瀑布式開發,仍是流行的敏捷模式,Maven都能大顯身手.
1.1.1 構建工具
咱們一直在不停的尋找避免重複的方法,設計的重複,編碼的重複,文檔的重複,固然還有構建的重複.Maven最大化的消除了構建的重複,抽象了構建生命週期,而且爲絕大部分的構建任務提供了已實現的插件,咱們不須要再定義過程,甚至不須要去實現這些過程當中的一些任務,只須要遵循Maven中的約定.
同時Maven幫助咱們標準化構建過程,之前十個項目可能有十種構建方式,有了Maven後,全部項目的構建命令都是一直且簡單的.所以Maven做爲一個構建工具:
- 能夠幫咱們自動化構建,
- 能夠幫咱們抽象構建過程
- 提供構建任務是實現
- 跨平臺
- 對外提供一直的操做接口
1.1.2 不只僅是構建工具
Maven不只是構建工具,仍是一個依賴管理工具和項目信息管理工具,提供中央倉庫來幫忙咱們自動下載構建,經過引入一套經緯機制來系統準確的定位每個構建(artifact).
1.1.3 Maven
在Maven以前,有過程式的Make和Ant,開發者須要顯示的指定每個目標,以及完成該目標所須要執行的任務.針對每個項目,開發者都須要從新編寫這一過程,而其中就隱含着大量重複.
而Maven是聲明式的,項目構建過程和過程各個階段所需的工做都由插件實現,而且大部分插件都是現成的,開發者只須要聲明項目的基本元素,Maven就執行內置的,完整的構建過程.
Maven項目的核心是pom.xml,POM(Project Object Model,項目對象模型)定義了項目的基本信息,用於描述項目如何構建,聲明項目依賴等.
<modelVersion>4.0.0</modelVersion>:modelVersion指定了當前Pom模型的版本,固定爲4.0.0<groupId>com.lsy</groupId>:groupId定義了項目屬於哪一個組,這個組每每和項目所在的組織和公司相關
<artifactId>hello-world</artifactId>:artifactId定義了當前Maven項目在組中惟一的ID
<version>1.0-SNAPSHOT</version>:version指定了Hello World項目當前的版本,其中SNAPSHOT意爲快照,說明該項目還處於開發中
<name>Maven Hello World Project</name>:name元素聲明瞭一個對於用戶更爲友好的項目名稱.非必須
當運行mvn clean compile命令:clean告訴Maven清理輸出目錄target/,compile告訴Maven編譯項目主代碼,從輸出中看到Maven首先執行clean:clean任務,刪除target/目錄(默認狀況下,Maven構建的全部輸出都在target目錄中,接着執行resources:resources任務(未定義項目資源),最後執行compiler:compile任務,將項目主代碼編譯至target/classes目錄
其中clean:clean,resources:resources和compiler:compile對應了Maven插件及插件目標,好比clean:clean是clean插件的clean目標,compiler:compile是compiler插件的compile目標
首先添加Junit依賴
<dependencies> <dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.7</version>
<scope>test</scope>
</dependency>
<dependencies>
其中scope元素爲依賴範圍,若依賴範圍爲test則表示該依賴只對測試有效,若是不聲明依賴範圍,則默認是compile,表示該依賴對主代碼和測試代碼均有效
當運行mvn clean test命令,Maven實際執行的不止test任務,而是clean:clean,resources:resources,compiler:compile,resources:testResources以及compiler:testCompile,即在執行測試以前,會先自動執行項目資源處理,主代碼編譯,測試資源處理,測試代碼編譯等工做,這是Maven生命週期的一個特性.
因爲Maven核心插件之一compiler插件默認支持Java1.3,所以須要配置插件支持Java1.8
<build> <plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8<source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
將項目進行編譯,測試以後,下一個重要的步驟就是打包(package),當沒有指定打包類型時,默認打包類型是jar,當執行mvn clean package命令,Maven會在打包以前進行編譯,測試等操做,以後經過jar:jar任務負責打包,實際上就是jar插件的jar目標將項目主代碼打包成一個hello-world-1.0-SNAPSHOT.jar的文件
當其餘項目須要直接引用這個jar時,接下來須要一個安裝的步驟,執行mvn clean install,此命令執行了安裝任務install:install,將項目輸出的jar安裝到了Maven本地倉庫中,而構件只有被下載到本地倉庫後,才能由Maven項目使用.
mvn archetype:generate,當運行插件時,格式爲:groupId:artifactId:version:goal,默認使用最新的穩定版
- 在實際生活中,咱們能夠將地址看做是一種座標,省,市,區,街道等一系列信息能夠惟一標識城市中的任意居住地址,對應在Maven的世界中擁有很是巨大的構件,也就是日常使用的一些jar,war等文件
Maven定義了這樣一規則:世界上任意一個構建均可以使用Maven座標惟一標識,Maven座標的元素包括
- groupId:定義當前Maven項目隸屬公司的實際項目
- artifactId:該元素定義實際項目中的一個Maven模塊,推薦作法使用實際項目名稱爲artifact的前綴,便於尋找實際構建
- version:該元素定義Maven項目當前所處的版本
- package:該元素定義Maven項目的打包方式,默認爲jar包
- classifier:該元素用來幫助定義構建輸出一些附屬構建,附屬構件與主構件對應
<groupId>org.sonatype.nexus</groupId> <artifactId>nexus-indexer</artifactId> <version>2.0.0</version> <packaging>jar</packaging> <!--> 1. 以上5個元素,groupId,artifactId,version是必須定義的,packaging是可選的,而classifier是不能直接定義的 2. 項目構件的文件名格式:artifactId-version[-classifier].packaging
項目要引用Maven中的構建,就須要在pom文件中,經過座標來使用依賴,在根元素project下的dependencies能夠包含一個或多個dependency元素,用以聲明一個或多個項目依賴
- groupId,artifactId和version:依賴的基本座標,對於任何一個依賴來講,基本座標是最重要的
- type:依賴的類型,對應項目座標定義的packaging,通常沒必要聲明,默認爲jar
- scope:依賴的範圍
- optional:標記依賴是否可選
- exclusions:用來排除傳遞性依賴
- Maven項目在編譯項目主代碼時須要使用一套classpath,如編譯項目主代碼時須要使用spring-core,該文件以依賴的方式被引入到classpath中.其次,Maven在編譯和執行測試的時候會使用另一套classpath,依賴一樣會引入到相應的classpath中,最後在運行Maven項目時,又會使用一套classpath
依賴範圍就是用來控制依賴與三種classpath(編譯classpath,測試classpath,運行classpath)的關係
- compile:編譯依賴範圍,沒有指定時,爲默認依賴範圍.使用此依賴範圍的Maven依賴,對於編譯,測試運行三種classpath都有效,典型的例子爲:spring-core,在編譯,測試,運行階段都須要使用該依賴
- test:測試依賴範圍,使用此依賴範圍的Maven依賴,只對於測試的classpath有效,在編譯主代碼或者運行項目時將沒法使用此類依賴,典型的例子就是JUnit,它只有在編譯器測試代碼及運行測試的時候才須要
- provided:已提供依賴範圍,使用此依賴範圍的Maven依賴,對於編譯和測試classpath有效,但在運行時無效,典型的例子就是servlet-api,編譯和測試項目的時候須要該依賴,但在運行項目的時候,因爲容器已經提供,就不須要Maven重複引入了
- runtime:運行時依賴範圍,使用此依賴範圍的Maven依賴,對於測試和運行classpath有效,但在編譯主代碼時無效,典型的例子是JDBC驅動實現,項目主代碼的編譯只須要JDK提供的JDBC接口,只有在執行測試或運行項目的時候才須要實現上述接口的具體JDBC驅動
system:系統依賴範圍,該依賴與三種classpath的關係,和provided依賴範圍徹底一致,可是,使用system範圍的依賴時必須經過systemPath元素顯示地指定依賴文件的路徑,因爲此類依賴不是經過Maven倉庫解析的,並且每每與本機系統綁定,可能形成構建的不可移植性
<dependency> <groupId>javax.sql</groupId> <artifactId>jdbc-stdext</artifactId> <version>2.0</version> <scope>system</scope> <systemPath>${java.home}/lib/rt.jar</systemPath> </dependency>- import:導入依賴範圍,該依賴範圍不會對三種classpath產生實際的影響,主要用於導入其餘pom文件中的dependencyManagement元素對於依賴版本約束的內容
在使用Maven依賴時,如Spring Framework,此依賴又會依賴其餘的開源庫,所以實際中每每會下載一個很大的如spring-framework-2.5.6-with-dependencies.zip包,這樣每每就引入了不少沒必要要的依賴,而Maven的傳遞性依賴機制就能夠很好的解決這一問題
- 當A項目引入一個compile範圍的B依賴,而B依賴中有一個compile範圍的C依賴,那麼C依賴一樣會成爲A的compile範圍依賴
傳遞性依賴和依賴範圍
- 假設A依賴於B,B依賴於C,那麼A對於B是第一直接依賴,B對於C是第二直接依賴,A對於C是傳遞性依賴
- 當第二直接依賴是compile的時候,傳遞性依賴與第一直接依賴範圍一致
- 當第二直接依賴是test的時候,依賴不會得以傳遞
- 當第二直接依賴是provided的時候,只傳遞第一直接依賴範圍爲provided的依賴,且傳遞性依賴範圍爲provided
- 當第二直接依賴是runtime的時候,傳遞性地依賴的範圍與第一直接依賴的範圍一致,但compile例外,此時傳遞性依賴的範圍爲runtime
假若有這樣一個依賴關係,A依賴於B,B依賴於X和Y,B對於X和Y的依賴都是可選依賴,根據傳遞性依賴的定義,若是這三個依賴的範圍都是compile,那麼X,Y就是A的compile範圍傳遞性依賴,可是因爲X,Y都是可選依賴,因此依賴不會得以傳遞,所以X,Y不會對A有任何影響
爲何會有可選依賴這一特性呢?
- 當B實現了兩個特性,特性一依賴於X,特性二依賴於Y,而且這兩個特性是互斥的,用戶不可能同時使用兩個特性,好比B是一個持久層隔離工具包,支持多種數據庫,在使用這個工具包的時候,只會依賴一種數據庫
<dependencies> <dependency> <groupId>mysql</groupId> <artifact>mysql-connector-java</artifact> <version>5.6.0</version> <optional>true</optional> </dependency> <dependency> <groupId>postgresql</groupId> <artifactpostgresql</artifact> <version>8.4-701.jdbc3</version> <optional>true</optional> </dependency> </dependencies>
- 以上使用<optional>元素表示兩個依賴爲可選依賴,他們只會對當前項目B產生影響,當其餘項目依賴於B時,這兩個依賴不會被傳遞,因此當A依賴於B項目時,若是要使用mysql數據庫,那麼須要顯式的聲明mysql-connector-java這一依賴
- 關於可選依賴,在理想的狀況下,是不該該使用可選依賴的,使用可選依賴的緣由是某一個項目中實現了多個特性,而根據單一職責原則,應該針對不一樣的特性分別建立一個Maven項目,用戶根據須要選擇使用其中某一個依賴
傳遞性依賴會給項目隱式的引入不少依賴,這極大的簡化了項目依賴的管理,可是有時候這種特性䧥帶來問題
例如,當前項目有一個第三方依賴,而這個依賴因爲某些緣由依賴了另外一個類庫的SNAPSHOT,那麼整個SNAPSHOT就會成爲當前項目的傳遞性依賴,而SNAPSHOT的不穩定性會直接影響到當前的項目,這時候就須要排除掉該SNAPSHOT,而且在當前項目中聲明該類庫的某個正式發佈版
<dependencies> <dependency> <groupId>com.lsy.myproject</groupId> <artifactId>myproject-a</artifactId> <version>1.0.0</version> <exclusions> <exclusion> <groupId>com.lsy.myproject</groupId> <artifactId>myproject-b</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>com.lsy.myproject</groupId> <artifactId>myproject-b</artifactId> <version>1.1.0</version> </dependency> </dependencies>
當一些依賴來自同一個項目的不一樣模塊,這些依賴的版本都應該是相同的,未來升級也是一塊兒升級,如Spring Framework,這時可使用properties元素定義Maven屬性
<properties> <springframework.version>4.2.1</springframework.version> </properties> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <verison>${springframework.version}</verison> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <verison>${springframework.version}</verison> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <verison>${springframework.version}</verison> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <verison>${springframework.version}</verison> </dependency> </dependencies>
在軟件開發過程當中,一般會經過重構等方式不斷優化本身代碼,一樣,對於Maven項目的依賴也須要對其進行優化
- 去除多餘的依賴
- 顯示聲明某些必要的依賴
Maven會自動解析全部項目的直接依賴和間接依賴,而且根據規則判斷每一個依賴範圍,對於一些依賴衝突,也能進行調整,以確保任何一個構建只有惟一的版本在依賴中存在,此稱爲解析依賴
- mvn dependency:list 查看當前項目的已解析依賴
- mvn dependency:tree 以樹結構查看已解析依賴
mvn dependency:analyze 解析依賴
- Used undeclared dependencies:指項目中使用到的,可是沒有顯示聲明的依賴,這種依賴意味着潛在的風險,當前項目直接在使用它們,因此須要顯示聲明任何項目中直接用到的依賴
- Unused declared dependencies:指項目中未使使用的.但顯示聲明的依賴
在Maven世界中,任何一個以依賴,插件或項目構建的輸出,均可以成爲構件
任何一個構建都有其惟一的座標,根據這個座標能夠定義其在倉庫中的惟一存儲路徑,這就是Maven的倉庫佈局方式,如log4j:log4j:1.2.15這一依賴,其對應的倉庫路徑爲log4j/log4j/1.2.15/log4j-1.2.15.jar
- 路徑與座標的關係爲:groupId/artifactId/version/artifactId-verision.packaging
對於Maven來講,倉庫分爲兩類
- 本地倉庫
遠程倉庫
- 中央倉庫:Maven核心自帶的倉庫服務器,它包含了絕大部分開源的構件
- 私服:另外一種特殊的遠程倉庫,爲了節省寬帶和時間,應該在局域網內部構建一個私有倉庫服務器,用其代理全部外部的遠程倉庫,內部項目部署到私服上供其它項目使用
- 其餘公共庫
- 當Maven根據座標尋找構件時,它首先會查看本地倉庫,若是本地倉庫存在此構件,則直接使用,若是本地倉庫不存在此構件,或者須要查看是否有更新的構件版本,Maven就會去遠程倉庫查找,發現須要的構件後下載到本地倉庫再使用,若本地和遠程都沒有,則報錯
通常來講,在Maven項目目錄下,沒有注入lib/這樣用來存放依賴文件的目錄,當Maven執行編譯或測試時,若是須要使用依賴文件,它老是基於座標使用本地倉庫的依賴文件
默認狀況,在用戶目錄下路徑名爲.m2/repository/的倉庫目錄,當想自定義本地倉庫目錄地址時,能夠編輯~/.m2/setting.xml,設置localRepository元素來指定倉庫地址
<settings> <localRepository>D:/java/repository/</localRepository> </settings>- 默認狀況下,~/.m2/settings.xml文件是不存在的,用戶須要從Maven安裝目錄複製$M2_HOME/conf/settings.xml文件再編輯,建議不要直接修改全局目錄的settings.xml,而是在用戶目錄下進行修改
一個構建只有在本地倉庫中,才能由其它Maven項目使用
- 依賴Maven從遠程倉庫下載到本地倉庫中
- 將本地項目的構件安裝到Maven本地倉庫中 mvn clean install
安裝好Maven後,若是不執行任何Maven命令,本地倉庫目錄是不存在的,只有輸入第一條Maven命令後,Maven纔會建立本地倉庫,並根據配置和須要,從遠程倉庫下載至本地倉庫
- 這就比如藏書,本地倉庫比如書房,遠程倉庫比如書店,我須要讀書時先去書房找,當書房沒有時,就去書店買回放到書房,而且通常對每一個人來講,書房只有一個,而外面的書店能夠有多個
最原始的本地倉庫是空的,因此Maven必須知道至少一個可用的遠程倉庫,才能在執行Maven命令時下載到須要的構建,中央倉庫就是這樣一個默認的遠程倉庫,能夠經過解壓工具打開$M2_HOME/lib/maven-model-builder-3.0.jar中的org/apache/maven/model/pom-4.0.0.xml文件
<repositories> <repository> <id>central</id> <name>Maven Repository Switchboard</name> <url>http://repo1.maven/org/maven2</url> <layout>default</layout> <snapshots> <enable>false</enable> </snapshots> </repository> </repositories>
- 這段配置是全部Maven項目都會繼承的超級Pom文件,這段配置使用id central對中央倉庫進行惟一標識,其名稱爲Maven Repository Switchboard,它使用default倉庫佈局,也就是在以前介紹的倉庫佈局,最後snapshots元素表示不從該倉庫下載快照版
私服是一種特殊的遠程倉庫,它是架設在局域網內的倉庫服務,私服代理廣域網上的遠程倉庫,供局域網內的Maven用戶使用,當Maven須要下載構建的時候,它從私服請求,若是私服上不存在該構件,則從外部的遠程倉庫下載,緩存在私服上以後,再爲Maven的下載提供服務
- 此外,一些沒法從外部下載的構件也能夠從本地上傳到私服上供你們使用
私服的好處
- 節省外網帶寬:創建私服一樣能夠減小組織本身的開支,大量的對於外部倉庫的重複請求會消耗很大的帶寬,利用私服代理外部倉庫以後,對外的重複構件下載即可以消除,即下降外網帶寬的壓力
- 加速Maven構件:不停的鏈接請求外部倉庫是十分耗時的,但Maven的一些內部機制(如快照更新檢查井)要求Maven在執行構建時不停的檢查遠程倉庫數據,所以,當項目配置不少外部倉庫時,構建速度就會下降
- 部署第三方構件:當某個構件沒法從任何一個外部遠程倉庫獲取,創建私服以後,即可以將這些構件部署到這個內部的倉庫中,供內部的Maven項目使用
- 提升穩定性,加強控制:Maven構建高度依賴遠程倉庫,所以,大哥Internet不穩定的時候,Maven構建也會變得不穩定,甚至沒法構建,使用私服後,便是短暫時沒有Internet鏈接,因爲私服中有大量緩存,Maven依然能夠正常運行,而且私服中有不少額外的權限功能控制
- 下降中央倉庫的負荷:天天中央倉庫都須要面對大量的下載請求,使用私庫能夠下降對於中央倉庫的負荷
遠程倉庫的配置
不少狀況下,默認的中央倉庫沒法知足項目的需求,可能項目須要的構件存在於另外一個遠程倉庫中,能夠經過Pom/settings文件中來配置該倉庫
<!--> POM文件 </!--> <project> ..... <repositories> <repository> <id>jboss</id> <name>JBoss Repository</name> <url>http://repository.jboss.com/maven2/</url> <release> <enabled>true</enabled> </release> <snapshots> <enabled>false</enabled> </snapshots> <layout>default</layout> </repository> </repositories> </project> <!--> settings文件 </!--> <profiles> <profile> <id>dev</id> <activation> <activatedByDefault>true</activatedByDefault> </activation> <repositories> <repository> <id>jboss</id> <name>JBoss Repository</name> <url>http://repository.jboss.com/maven2/</url> <release> <enabled>true</enabled> </release> <snapshots> <enabled>false</enabled> </snapshots> <layout>default</layout> </repository> </repositories> </profile> </profiles>
- 在repositories元素下,可使用repository子元素聲明一個或多個遠程倉庫,而且聲明一個id,任何一個倉庫聲明的id必須是惟一id
- Maven自帶的中央倉庫使用的id爲central,若是其餘倉庫使用該id,則會覆蓋中央倉庫的配置,該配置中的url指向了倉庫的地址
該配置中的release和snapshots元素用來控制Maven對於發佈版構件和快照版構件的下載,對於release和snapshots元素來講除了enabled子元素,還有updatePolicy和checksumPolicy元素
updatePolicy:用來配置Maven從遠程倉庫檢查更新的頻率,默認爲daily
- daily:天天
- never:從不
- always:每次
- interval :X :每隔X分鐘一次
checksumPolicy:配置Maven檢查文件失敗時的策略,默認爲warn
- fail:Maven遇到驗證和錯誤就讓構建失敗
- warn:Maven遇到驗證和錯誤就發出警告信息
- ignore:Maven遇到驗證和錯誤時徹底忽略
<snapshots> <enabled>true</enabled> <updatePolicy>daily</updatePolicy> <checksumPolicy>ignore</checksumPolicy> </snapshots>
遠程倉庫的驗證
- 大部分遠程倉庫無需認證就能夠訪問,但出於安全考慮,咱們須要提供一些認證信息才能訪問一些遠程倉庫
- 配置認證信息和配置倉庫信息不一樣,倉庫信息能夠直接配置在POM文件中,可是認證信息必須配置在settings.xml文件中
其中server元素的id必須與POM文件中須要認證的repository元素的id徹底一致
<settings> ...... <servers> <server> <id>my-project</id> <username>user</username> <password>password</password> </server> </servers> ....... </settings>
部署至遠程倉庫
私服一大做用就是部署第三方構件,包括組織內部生成的構件以及沒法從外部倉庫直接獲取的構件,不管是平常開發中生成的構件,仍是正式版本發佈的構件,都須要部署到倉庫中
- distributionManagement包含repository和snapshotRepository子元素,前者表示發佈版構件的倉庫,後者表示快照版的倉庫
往遠程倉庫部署構件時,每每須要認證,認證配置需在settings.xml中建立一個server元素,其id與倉庫的id匹配,不管從遠程倉庫下載構件,仍是部署構件至遠程倉庫,當須要認證時,配置方式都是同樣的
<!--> 在POM文件中配置distributionManagement </!--> <project> ..... <distributionManagement> <repository> <id>jboss-release</id> <name>JBoss Release Repository</name> <url>http://192.168.1.99/content/repository/jboss-release</url> </repository> <snapshotRepository> <id>jboss-snapshots</id> <name>JBoss Snapshots Repository</name> <url>http://192.168.1.99/content/repository/jboss-snapshots</url> </snapshotRepository> </distributionManagement> ..... </project>
- 在Maven的世界中,任何一個項目或者構件都必須有本身的版本,版本的值多是1.0.0,1.3-alpha-4,2.0,2.1-SNAPSHOT或者2.1-20191214.221414-13,其中1.0.0,1.3-alpha-4和2.0是穩定的發佈版本,而2.1-SNAPSHOT和2.1-20091214.221414-13是不穩定的快照版本
- 對於一個穩定的版本,若是倉庫中已經包含,那麼Maven就不會再去對照遠程倉庫進行更新,除非每次執行Maven命令前,清除本地倉庫中等待穩定版本,而對於一個正在迭代的項目,若是要實時更新版本的內容就須要頻繁的修改新的版本名稱,這樣是對版本號的濫用
- 針對這種狀況,使用快照版時,Maven會自動爲構件打上時間戳,所以,Maven就能隨時找到倉庫中該構建最新版本的文件,一旦有新的更新,就會去同步到本地倉庫.當項目通過完善的測試後須要發佈的時候,再將快照版本更改成發佈版本
- 快照版本只應該在組織內部的項目或模塊之間依賴使用,由於這時,組織對這些快照版本的依賴具備徹底的理解和控制權,項目不該該依賴任何組織外部的快照版本依賴,因爲快照版本的不穩定性,隨時可能發生變化,這樣的依賴會有潛在的危險
當本地倉庫沒有依賴構件的時候,Maven會自動從遠程倉庫下載,當依賴版本爲快照版本時,Maven會自動找到最新的快照,這背後的依賴機制能夠歸納以下
- 當依賴的範圍時system的時候,Maven直接從本地文件系統解析構件
- 根據依賴座標計算倉庫路徑後,嘗試從本地倉庫尋找構件,若是發現相應的構件,則解析成功
- 在本地倉庫不存在相應構件的狀況下,若是依賴的版本時顯式的發佈版本構件,如1.2,2.1-beta-1等,則遍歷全部的遠程倉庫,發現後,下載並解析使用
- 若是依賴的版本時RELEASE或LATEST,則基於更新策略讀取全部遠程倉庫的元數據groupId/artifact/maven-metadata.xml,將其與本地倉庫的對應元數據合併後,計算出RELEASE或LATEST真實的值,而後基於這個真實的值檢查本地倉庫和遠程倉庫
- 若是依賴的版本是SNAPSHOT,則基於更新策略讀取全部遠程倉庫的元數據groupId/artifactId/maven-metadata.xml,將其與本地倉庫的對應數據合併後,獲得最新的快照版本的值,而後基於該值檢查本地倉庫,或者從遠程倉庫下載
若是最後解析獲得的構件版本是時間戳格式,如1.4.1-20191104.121455-8,則複製其時間戳格式的文件至非時間戳格式,如SNAPSHOT,並使用該非時間戳格式的構件
<!--> 基於groupId和artifactId的maven-metadata.xml </!--> <?xml version="1.0" encoding="UTF-8"?> <metadata> <groupId>org.sonatype.nexus</groupId> <artifactId>nexus</artifactId> <versioning> <latest>1.4.2-SNAPSHOT</latest> <release>1.3.7</release> <versions> <version>1.3.5</version> <version>1.3.6</version> <version>1.3.7</version> <version>1.4.0-SNAPSHOT</version> <version>1.4.1-SNAPSHOT</version> <version>1.4.2-SNAPSHOT</version> </versions> <lastUpdated>20191214221133</lastUpdated> </versioning> </metadata>該XML文件列出了倉庫中存在的構件全部可用的版本,同時latest元素指向了這些版本中最新的那個版本1.4.2-SNAPSHOT,而release元素指向了這些版本中最新的發佈版本1.3.7,Maven經過合併多個遠程倉庫及本地倉庫的元數據,就能計算出基於全部倉庫的latest和release
- 須要注意的是,在依賴聲明使用LATEST和RELEASE是不推薦的作法,由於Maven隨時可能解析到不一樣的構件,且Maven不會明確告訴用戶這樣的變化
若是倉庫X能夠提供倉庫Y存儲的全部內容,那麼就能夠認爲X是Y的一個鏡像,因爲地理位置的因素,中央倉庫的下載速度會比較慢,這時咱們能夠配置Maven使用鏡像來代替中央倉庫,編輯settings.xml
<settings> ...... <mirrors> <mirror> <id>maven.net.cn</id> <name>one of the central mirror in china</name> <url>http://maven.net.cn/content/groups/public/</url> <mirrorOf>central</mirrorOf> </mirror> </mirrors> ...... </settings>
<mirrorOf>的值爲central,表示該配置爲中央倉庫的鏡像,任何對於中央倉庫的請求都會轉至該鏡像,用戶也可使用一樣的方法配置其餘倉庫的鏡像,另外三個元素id,name,url與通常倉庫配置無異,表示該鏡像倉庫的惟一標識,名稱以及地址
- 若該鏡像要進行驗證,即基於該id配置倉庫認證
<settings> ..... <mirrors> <mirror> <id>internal-repository</id> <name>Internal Repository Mananger</name> <url>http://192.168.1.100/maven2/</url> <mirrorOf>*</mirrorOf> </mirror> </mirrors> ..... </settings>
以上mirrorOf元素的值爲*,表示該配置是全部Maven倉庫的鏡像,對於任何遠程倉庫的請求都會被轉至該指定的倉庫,若是鏡像倉庫須要驗證,則配置一個id爲internal-repository的<server>便可
- <mirrorOf>*<mirrorOf>:匹配全部遠程倉庫
- <mirrorOf>external:*<mirrorOf>:匹配全部遠程倉庫,使用localhost的除外,使用file://協議的除外,也就是說,匹配全部不在本機上的遠程倉庫
- <mirrorOf>repo1,repo2<mirrorOf>:匹配倉庫repo1和repo2,用逗號分隔多個倉庫
- <mirrorOf>*,! repo1<mirrorOf>:匹配全部的遠程倉庫,除repo1外,使用感嘆號將倉庫從匹配中排除
- 須要注意的是,因爲鏡像倉庫徹底屏蔽了被鏡像倉庫,當鏡像倉庫不穩定或中止服務時,Maven仍將沒法訪問被鏡像倉庫,所以沒法下載構件
在Maven出現以前,項目構建的生命週期就已經存在,可是不一樣的公司和開發人員雖然一樣在作構件工做,其對於不一樣的項目卻不可以重用,只能從新定製開發,而Maven的生命週期就是爲了對全部的構件過程進行抽象和統一
- 這個生命週期包含了項目的清理,初始化,編譯,測試,打包,集成測試,驗證,部署和站點生成等幾乎全部的構件步驟,也就是說幾乎全部項目的構件,都能映射到這樣一個生命週期上
Maven的生命週期是抽象的,這意味着生命週期自己不作任何實際的工做,在Maven的設計中,實際的任務(如編譯主代碼)都交由插件完成,這種思想和設計模式的模方法相似,在父類中定義算法的總體結構,子類能夠經過實現或重寫父類的方法來控制實際的行爲
public abstract class Template{ public void build(){ initialize(); compile(); test(); packagee(); integrationTest(); deploy(); } protect abstract void initialize(); protect abstract void compile(); protect abstract void test(); protect abstract void packagee(); protect abstract void integrationTest(); protect abstract void deploy(); }
- 在Maven的生命週期中抽象了各個步驟,定義了他們的次序,可是沒有提供具體的實現,而經過插件機制爲每一個構件步驟綁定一個或多個插件行爲,並且Maven爲大多數構件步驟都綁定了默認的插件,例如,針對編譯的maven-compiler-plguin,針對測試的maven-surefire-plugin等
- Maven定義的生命週期和插件機制一方面保證了全部Maven項目有一致的構件標準,另外一方面又經過默認的插件簡化和穩定實際項目的構件,此外,該機制還提供了足夠的擴展,用戶能夠經過配置現有的插件或自定義插件來自定義構件行爲
三套聲明週期
Maven擁有三套相互獨立的生命週期,他們分別爲clean,default和site,每一個生命週期包含一些階段,這些階段是有序的,而且後面的階段依賴於前面的階段,可是三套聲明週期自己是互相獨立的
clean生命週期:清理項目
- pre-clean:執行一些清理前須要完成的工做
- clean:清理上次構件生成的文件
- post-clean:執行一些清理後須要完成的工做
default聲明週期:定義了真正的構件所需執行的全部步驟
- validate
- initialize
- generate-sources
- process-sources:處理項目主資源文件,通常來講針對/src/main/resources目錄的內容進行變量替換等工做後,複製到項目輸出的主classpath目錄中
- compile:編譯項目的主源碼,通常來講針對/src/main/java目錄下的Java文件至目錄輸出的主classpath目錄中
- process-classes
- generate-test-sources
- process-test-sources:處理項目測試資源文件,通常來講針對/src/test/resources目錄的內容進行變量替換工做後,複製到項目輸出的測試classpath目錄
- test-compile:編譯項目的測試代碼,通常來講針對/src/test/java目錄下的java文件至輸出的測試classpath目錄中
- test:使用單元測試框架運行測試,測試代碼不會被打包或部署
- prepare-package
- package:接收編譯好的代碼,打包成可發佈的格式,如jar
- pre-integration-test
- integration-test
- post-integration-test
- verify
- install:將包安裝到Maven本地倉庫,供本地其餘Maven項目使用
- deploy:將最終的包複製到遠程倉庫,供其餘開發人員和Maven項目使用
site聲明週期:創建和發佈項目站點,Maven可以基於POM所包含的信息,自動生成一個友好的站點供交流和發佈項目信息
- pre-site:執行一些在生成項目站點以前須要完成的工做
- site:生成項目站點文檔
- post-site:執行一些在生成項目站點後須要完成的工做
- site-deploy:將生成的項目站點發布到服務器上
命令行與生命週期
從命令行執行Maven任務最主要方式就是調用Maven的生命週期,各個生命週期是相互獨立的,而生命週期的階段是有先後依賴關係的
- mvn clean:該命令調用clean生命週期的clean階段,實際執行階段爲pre-clean和clean
- mvn test:該命令調用default生命週期的test階段,實際執行的階段爲default生命週期的validate,initialize直到test的全部階段,這也解釋爲何在執行測試的時候,項目代碼可以自動編譯
- mvn clean install:該命令調用clean生命週期的clean階段和default生命週期的install階段
- mvn clean deploy site-deploy:該命令調用clean生命週期的clean階段,default生命週期的deploy階段和site生命週期的site-deploy階段
Maven的核心僅僅定義了抽象的生命週期,具體的任務是交由插件完成的,插件以獨立的構件形式存在
- 對於插件自己而言,爲了可以複用代碼,它每每可以完成多個任務,爲每一個功能編寫一個插件顯然不合理,由於這些任務背後有大量可複用的代碼,所以,這些功能彙集到一個插件裏面,每一個功能就是一個插件目標
Maven的生命週期和插件相互綁定,用以完成實際的構件任務,具體而言,是生命週期的階段與插件的目標相互綁定,以完成某個具體的構件任務
- 例如:編譯這一任務對應了default生命週期的compile這一階段,而maven-compiler-plugin這一插件的compile目標能完成此任務
自定義綁定
除了內置綁定之外,用戶還可以本身選擇將某個插件目標綁定到生命週期的某個階段上,這種自定義綁定方式能讓Maven項目在構件過程當中執行更多更豐富的任務
當須要建立項目的源碼jar包,maven-source-plugin能夠幫咱們完成任務,可是內置的插件綁定關係中並無涉及這一任務,所以須要自行配置,它的jar-no-fork目標能將項目主代碼打包成jar文件,能夠將其綁定到default生命週期的verify階段,在執行完集成測試和安裝構件以前建立源碼jar包
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-source-plugin</artifactId> <version>2.1.1</version> <executions> <execution> <id>attach-sources</id> <phase>verify</phase> <goals> <goal>jar-no-fork</goal> </goals> </execution> </executions> </plugin> </plugins> </build>在POM的build元素下的plugins子元素中聲明插件的使用,上例用到了是maven-source-plugin,其groupId爲org.apache.maven.plugins,用戶老是應該聲明一個非快照版本,這樣能夠避免因爲插件版本變化形成的構件不穩定性
- 除了基本的插件座標聲明外,還有插件執行配置,executions下每一個execution子元素能夠用來配置執行一個任務,該例配置了一個id爲attach-sources的任務,經過phase配置將其綁定到verify生命週期階段上,再經過goals配置指定要執行的插件目標,在運行mvn verify時就會執行該任務
有時候.便是不經過phase元素配置生命週期階段,插件目標也能綁定到生命週期中去,緣由是:有不少插件的目標在編寫時就已經定義了默認綁定階段,可使用maven-help-plugin查看詳細信息
mvn help:describe -Dplugin=org.apache.maven.plugins:maven-source-plugin:2.1 -Ddetail
- Bound to phase : package 默認綁定到package生命週期
- 當插件目標被綁定到不一樣生命週期階段的時候,其執行順序會由生命週期階段的前後順序決定,若是多個目標被綁定到同一個階段,他們的順序就由插件聲明的前後順序決定
用戶能夠經過配置插件目標的參數,進一步調整插件目標所執行的任務,幾乎全部Maven插件的目標均可以配置參數,用戶能夠經過命令行和POM配置等方式來配置這些參數
命令行插件配置
在平常的Maven使用中,咱們一般從命令行輸入並執行Maven命令,不少插件目標的參數都支持從命令行配置,用戶能夠在Maven命令中使用-D參數,並伴隨一個參數鍵=參數值的形式,來配置插件目標的參數
mvn install -Dmaven.test.skip = ture:給maven.surefire-plugin提供一個maventest.skip參數,當參數爲true時,就會跳過執行測試
- 參數-D是Java自帶的,其功能是經過命令行設置一個Java系統屬性,Maven簡單地重用了該參數,在準備插件的時候檢查系統屬性來實現插件參數的配置
POM中插件全局配置
並非全部的插件參數都適合用命令行配置,有些參數的值從項目建立到項目發佈都不會改變,或者說不多改變,這種狀況下在POM文件中一次性配置比重複在命令行輸入更合理
用戶能夠在聲明插件的時候,對插件進行一個全局的配置,全部基於該插件的目標任務,都會使用這些配置,如:maven-compiler-plugin來編譯1.8版本的源文件,生成與JVM1.8兼容的字節碼文件
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifact>maven-compiler-plugin</artifact> <version>2.1</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </build>
當遇到一個構建任務時,不只須要知道去哪裏找到合適的插件,還須要詳細的瞭解插件的配置點,因爲Maven的插件很是多,其中大部分沒有完善的文檔,所以,經過幫助命令來了解插件顯得很是重要
使用maven-help-plugin描述插件
除了訪問在線的插件文檔外,能夠藉助maven-help-plugin來獲取插件的詳細信息
mvn help:decribe -Dplugin = org.apache.maven.plugins:maven-compiler-plugin:2.1
- 這裏執行的是maven-help-plugin的decribe目標,在參數plugin中輸入須要描述插件的groupId,artifactId和version,Maven在命令行的輸出maven-compiler-plugin的簡要信息,包括插件的座標,目標前綴和目標等信息
在描述插件時,能夠省去版本信息,Maven會自動獲取最新版原本進行表述,並能夠用目標前綴來替換座標
- mvn help:describe -Dplugin=compiler
若是僅僅描述某個插件目標的信息,則加上goal參數,更詳細的信息,則加上detail參數
- mvn help:describe -Dplugin=compiler -Dgoal=compile -Ddetail
爲了方便用戶使用和配置插件,Maven不須要用戶提供完整的插件座標信息,就能夠解析獲得正確的插件
插件倉庫
- 與依賴構件同樣,插件構件一樣基於座標存儲在Maven倉庫中,在須要的時候Maven先從本地倉庫尋找,若是不存在,則從遠程倉庫查找,找到插件以後,再下載到本地倉庫使用
不一樣於repositories以及repository子元素,插件的遠程倉庫使用pluginRepositories和pluginRepository配置
<pluginRepositories> <pluginRepository> <id>central</id> <name>Maven Plugin Resository</name> <url>http://repo1.maven.org/maven2</url> <layout>default</layout> <snapshots> <enabled>false</enabled> </snapshots> <release> <enabled>true</enabled> <updatePolicy>never</updatePolicy> </release> </pluginRepository> </pluginRepositories>
插件默認的groupId
在POM中配置插件時,若是該插件時Maven的官方插件(即groupId爲org.apache.maven.plugins),就能夠省略groupId,Maven在解析該插件的時候,會自動使用默認groupId不起
<build> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>2.1</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </build>解析插件版本
一樣爲了簡化插件的配置和使用,在用戶沒有提供插件版本的狀況下,Maven會自動解析插件版本
- 首先Maven在超級POM中爲全部核心插件設定了版本,超級POM是全部Maven項目的父POM,全部項目都繼承這個超級POM配置,所以即便用戶不加任何配置,Maven在使用核心插件時,它的版本就已經肯定了
若是用戶使用的某個插件沒有設定版本,而且這個插件也不屬於核心插件,Maven機會去檢查全部倉庫中可用的版本,經過倉庫元數據groupId/artifactId/maven-metadata.xml文件,如maven-compiler-plugin插件爲例,它在org/apache/maven/plugins/maven-compiler-plugin/maven-metadata.xml
<?xml version="1.0" encoding="UTF-8"?> <metadata> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <versioning> <latest>2.1</latest> <release>2.1</release> <versions> <version>2.0-beta-1</version> <version>2.1</version> <version>2.2</version> <version>2.3</version> </versions> <lastUpdated>20190102092331</lastUpdated> </versioning> </metadata>- Maven遍歷本地倉庫和全部遠程倉庫,將倉庫元數據合併後,就能計算出latest和release的值,maven3將版本解析到最新的非快照版,如2.1
Maven命令支持使用插件前綴來簡化插件的使用,插件前綴和groupId:artifact是一一對應的,這種匹配關係存儲在倉庫元數據中,與以前提到的groupId/artifactId/maven-metadata.xml不一樣,這裏倉庫元數據爲groupId/maven-metadata.xml,通常插件都位於/org/apache/maven/plugins/和org/code-haus/mojo/,能夠經過settings.xml配置讓Maven檢查其餘groupId上的插件倉庫元數據
<settings> <pluginGroups> <pluginGroup>com.my.plugins</pluginGroup> </pluginGroups> </settings>在倉庫的元數據文件中能夠看到插件的前綴定義
<metadata> <plugins> <plugin> <name>Maven Clean Plugin</name> <prefix>clean</prefix> <artifact>maven-clean-plugin</artifact> </plugin> <plugin> <name>Maven Compiler Plugin</name> <prefix>compile</prefix> <artifact>maven-compile-plugin</artifact> </plugin> <plugin> <name>Maven Dependency Plugin</name> <prefix>dependency</prefix> <artifact>maven-dependency-plugin</artifact> </plugin> </plugins> </metadata>
aggregator
<modelVersion>4.0.0</modelVersion> <groupId>com.lsy.project</groupId> <artifact>project-aggregator</artifact> <version>1.0.0-SNAPSHOT</version> <packaging>pom</packaging> <name>Aggregator</name> <modules> <module>project-email</module> <module>project-register</module> </modules>
- 對於聚合模塊,其打包方式packaging的值必須爲pom
經過modules元素來實現聚合,用戶能夠經過在一個打包方式爲pom的Maven項目中聲明任意數量的module元素來實現模塊的聚合,這裏每一個module的值都是一個當前POM的相對目錄
- 如aggregator的POM路徑爲.../project-aggregator/pom.xml,那麼project-email對應 的目錄爲.../project-aggregator/project-email/,而且目錄中包含了pom.xml,src/main/java,src/test/java等內容,離開了project-aggregator也能獨立建立=
聚合模塊和其餘模塊的目錄結構並不是必定要父子關係,也能夠是平行關係
<modules> <module>../prject-email</module> <module>../project-register</module> </modules>- 當在聚合模塊中執行mvn clean install時,Maven首先解析聚合模塊的POM,分析要構件的模塊,並計算出一個反應堆構件順序(Reactor Build Order),而後根據這個順序構件各個模塊
聚合模塊的構件順序
- Maven按序讀取POM文件,若是POM沒有依賴模塊,那麼就構件模塊,不然就先構件其依賴模塊
從以上的聚合模塊和其餘模塊中能夠看到,多個被管理模塊的POM文件中會有大量重複的相同配置,他們有相同的groupId和version,相同的依賴,相同的插件配置,而經過POM文案的繼承能夠消除重複
project-parent父模塊
<modelVersion>4.0.0</modelVersion> <groupId>com.lsy.project</groupId> <artifactId>project-parent</artifactId> <version>1.0.0-SNAPSHOT</version> <packaging>pom</packaging> <name>Parent</name>
- 父模塊的POM文件使用與其餘模塊一直的groupId和version,它的packaging方式爲pom,與聚合模塊一致
- 父模塊主要是爲了消除配置的重複,所以它自己不包含除POM文件以外的項目文件,也就不須要src/main/java之類的文件夾了
project-email子模塊
<parent> <groupId>com.lsy.project</groupId> <artifactId>project-parent</artifactId> <version>1.0.0-SNAPSHOT</version> <relativePath>../project-parent/pom.xml</relativePath> </parent> <artifactId>project-email</artifactId> <name>Email</name> <dependencies> ..... </dependencies>
使用parent元素聲明父模塊,parent下子元素groupId,artifactId,version指定父模塊的座標,元素relativePath表示父模塊POM的相對路徑,表示Email模塊和其父模塊是在平行的目錄下
- 當構件項目時,Maven會首先根據relativePath檢查父POM文件,若是找不到再從本地倉庫找,relativePath的默認值爲../pom.xml,也就是說,Maven默認父POM在上一層目錄
- 子模塊沒有groupId和version,是由於子模塊隱式的從父模塊繼承了這兩個元素,從而消除了沒必要要的配置
可繼承的POM元素
- groupId:項目組ID,項目座標的核心元素
- version:項目版本,項目座標的核心元素
- description:項目的描述信息
- organization:項目的組織信息
- inceptionYear:項目的創始年份
- url:項目的URL地址
- develops:項目的開發者信息
- contributors:項目貢獻者信息
- distributionManagement:項目的部署配置
- issueManagement:項目的缺陷跟蹤系統信息
- ciManagement:項目的持續集成系統信息
- scm:項目的版本控制系統信息
- mailingList:項目的郵件列表信息
- properties:自定義屬性
- dependencies:項目的依賴配置
- dependencyManagement:項目的依賴管理配置
- repositories:項目的倉庫配置
- pluginRepositories:項目的插件倉庫配置
- build:包括項目的源碼目錄配置,輸出目錄配置,插件配置,插件管理配置等
- reporting:項目的輸出目錄配置,報告插件配置等
依賴管理
- dependencies元素是能夠被繼承的,說明依賴是會被繼承的,因此咱們能夠將子模塊共有的依賴配置到父模塊中,子模塊就能夠移除這些依賴,簡化配置
上述方法是可行的,可是可能未來會有新的模塊並不須要父模塊中的一些依賴,這就會產生不合理的現象,從而Maven提供了dependencyManagement元素,既能讓子模塊繼承到父模塊的依賴配置,又能保證子模塊依賴使用的靈活性
- 在dependencyManagement元素下的依賴聲明不會引入實際的依賴,不過它可以約束dependencies下的依賴使用
<modelVersion>4.0.0</modelVersion> <groupId>com.lsy.project</groupId> <artifactId>project-parent</artifactId> <version>1.0.0-SNAPSHOT</version> <packaging>pom</packaging> <name>Parent</name> <properties> <springframework.version>4.3.1</springframework.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${springframwork.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>${springframwork.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${springframwork.version}</version> </dependency> </dependencies> </dependencyManagement>* 這裏使用dependencyManagement聲明的依賴既不會給parent模塊引入依賴,也不會給子模塊引入依賴,不過這段配置是會被繼承的 * 子模塊POM ```xml <parent> <groupId>com.lsy.project</groupId> <artifactId>project-parent</artifactId> <version>1.0.0-SNAPSHOT</version> <relativePath>../project-parent/pom.xml</relativePath> </parent> <artifactId>project-email</artifactId> <name>Email</name> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> </dependency> </dependencies> ``` * 使用這種依賴管理機制,能夠在父POM中使用dependencyManagement聲明依賴可以統一項目規範中的依賴版本,當版本在父POM中聲明以後,子模塊使用依賴時就不須要聲明瞭.也不會發生多個子模塊使用依賴版本不一致的狀況 * scoper元素的import依賴範圍只在dependencyManagement元素下才有效果,使用該範圍的依賴一般指向一個POM文件,做用是將POM中的dependencyManagement配置導入併合併到當前POM的dependencyManagement元素中 * 因此除了複製配置和繼承父模塊這兩種方式外,還能夠經過import範圍依賴導入這一配置 ```xml <dependencyManagement> <dependencies> <dependency>com.lsy.project</dependency> <artifactId>project-parent</artifactId> <version>1.0-SNAPSHOT</version> <type>pom</type> <scope>import</scope> </dependencies> </dependencyManagement> ```
插件管理
- Maven提供了dependencyManagement元素幫助管理依賴,相似的,Maven也提供了pluginManagement元素管理插件,該元素中配置的依賴一樣不會形成實際的插件調用行爲,而POM文件中配置了真正的plugin元素,而且groupId和artifact一致時,纔會產生實際的插件行爲
父模塊
<build> <pluginManagement> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-source-plugin</artifactId> <version>3.1.1</version> <executions> <execution> <id>attach-source</id> <phase>verify</phase> <goals> <goal>jar-no-fork</goal> </goals> </execution> </executions> </plugin> </plugins> </pluginManagement> </build>子模塊
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifact>maven-source-plugin</artifact> </plugin> </plugins> </build>
- 子模塊使用了maven-source-plugin插件,同時又繼承了父模塊的pluginManagement配置
集合與繼承的關係
多模塊的聚合與繼承實際上是兩個概念,其目的是徹底不一樣的,前者爲了方便快速的構件項目,後者主要爲了消除重複配置
- 對於聚合模塊來講:它知道有哪些模塊被聚合,可是那些被聚合的模塊並不知道這個聚合模塊的存在
- 對於繼承關係的父POM來講:它不知道有哪些子模塊繼承於它,可是那些子模塊都必須知道本身的父模塊是什麼
- 在實際項目中,一個POM每每便是聚合模塊,又是父模塊
約定優於配置
Maven提倡"約定優於配置",Maven只須要一個簡單的POM文件,就能夠完成清除,構件等任務
- 源碼目錄:src/main/java/
- 編譯輸出目錄爲:target/classes/
- 打包方式爲:jar
- 包輸出目錄爲:target/
Maven此機制的來源就是超級POM文件,此文件在$MAVEN_HOME/lib/maven-model-builder-x.x.x.jar中的org/apache/maven/model/pom-4.0.0.xml路徑下
<repositories> <repository> <id>central</id> <name>Maven Repository Swithboard</name> <url>http://repo1.maven.org/maven2</url> <layout>default</layout> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories> <pluginRepositories> <pluginRepository> <id>central</id> <name>Maven Plugin Repository</name> <url>http://repo1.maven.org/maven2</url> <layout>default</layout> <snapshots> <enabled>false</enabled> </snapshots> <releases> <updatePolicy>never</updatePolicy> </releases> </pluginRepository> </pluginRepositories> <build> <!--> 定義了項目的主輸出目錄 </!--> <directory>${project.basedir}/target</directory> <!--> 主代碼輸出目錄 </!--> <outputDirectory>${project.build.directory}/classes</outputDirectory> <!--> 最終構件的名稱格式 </!--> <finalName>${project.artifactId}-${project.version}</finalName> <!--> 測試代碼輸出目錄 </!--> <testOutputDirectory>${project.build.directory}/test- classes</testOutputDirectory> <!--> 主源碼目錄 </!--> <sourceDirectory>${project.basedir}/src/main/java</sourceDirectory> <!--> 腳本源碼目錄 </!--> <scriptSourceDirectory>src/main/scripts</scriptSourceDirectory> <!--> 測試源碼目錄 </!--> <testSourceDirectory>${project.basedir}/src/test/java</testSourceDirectory> <!--> 主資源目錄 </!--> <resources> <resource> <directory>${project.basedir}/src/main/resources</directory> </resource> </resources> <!-- >測試資源目錄 </!--> <testResources> <testResource> <directory>${project.basedir}/src/test/resources</directory> </testResource> </testResources> <!--> 核心插件版本 </!--> <pluginManagement> <plugins> <plugin> <artifactId>maven-antrun-plugin</artifactId> <version>1.3</version> </plugin> <plugin> <artifactId>maven-assembly-plugin</artifactId> <version>2.3</version> </plugin> <plugin> <artifactId>maven-clean-plugin</artifactId> <version>2.3</version> </plugin> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>2.0.3</version> </plugin> ...... </plugins> </pluginManagement> </build>
Nexus倉庫有四種類型
- group:倉庫組
- hosted:宿主倉庫
- proxy:代理倉庫
- virtual:虛擬倉庫
- Maven能夠從宿主倉庫下載構件,Maven也能夠從代理倉庫下載構件,而代理倉庫會簡介的從遠程倉庫下載並緩存構件
- 而通常爲了方便,咱們會從倉庫組下載構件,而倉庫組沒有實際的內容,它只是管理一組實際的倉庫,當它接收到請求時,它會轉向其包含的宿主倉庫或代理倉庫得到實際構件的內容
倉庫的Policy屬性
- Release:發佈版
- Snapshot:快照版
在POM文件中配置倉庫和插件倉庫------只對當前項目有效
<project> ....... <repositories> <repository> <id>nexus</id> <name>Nexus</name> <url>http://localhost:8081/nexus/content/groupId/public/</url> <snapshots> <enabled>true</enabled> </snapshots> </repository> </repositories> <pluginRepositories> <pluginRepository> <id>nexus</id> <Name>Nexus</Name> <url>http://localhost:8081/nexus/content/groupId/public/</url> <release> <enabled>true</enabled> </release> <snapshots> <enabled>true</enabled> </snapshots> </pluginRepository> </pluginRepositories> ....... </project>在settings.xml文件中配置---全局有效
- settings.xml文件並不支持直接配置repositories和pluginRepositories,可是能夠經過Maven提供的Profile機制,讓用戶將倉庫配置到settings.xml中的Profile中
<settings> .... <profiles> <profile> <id>nexus</id> <repositories> <repository> <id>nexus</id> <name>Nexus</name> <url>http://localhost:8081/nexus/content/groupId/public/</url> <release> <enabled>true</enabled> </release> <snapshots> <enabled>ture</enabled> </snapshots> </repository> </repositories> <pluginRepositories> <pluginRepository> <id>nexus</id> <Name>Nexus</Name> <url>http://localhost:8081/nexus/content/groupId/public/</url> <release> <enabled>true</enabled> </release> <snapshots> <enabled>true</enabled> </snapshots> </pluginRepository> </pluginRepositories> </profile> </profiles> <!--> 激活指定id的profile </!--> <activeProfiles> <activeProfile>nexus</activeProfile> </activeProfiles> </settings>
以上配置已經能讓Maven項目從Nexus私服下載構件了,可是Maven仍然會去訪問中央倉庫,如今咱們想要將全部請求都僅僅經過私服,這就須要藉助於Maven鏡像了
<settings> ..... <mirrors> <mirror> <id>nexus</id> <mirrorOf>*</mirrorOf> <url>http://localhost:8081/nexus/content/groupId/public/</url> </mirror> </mirrors> <profiles> <profile> <id>nexus</id> <repositories> <repository> <id>central</id> <name>central</name> <url>http://central</url> <release> <enabled>true</enabled> </release> <snapshots> <enabled>ture</enabled> </snapshots> </repository> </repositories> <pluginRepositories> <pluginRepository> <id>central</id> <Name>Nexus</Name> <url>http://central</url> <release> <enabled>true</enabled> </release> <snapshots> <enabled>true</enabled> </snapshots> </pluginRepository> </pluginRepositories> </profile> </profiles> <!--> 激活指定id的profile </!--> <activeProfiles> <activeProfile>nexus</activeProfile> </activeProfiles> </settings>
- 這裏倉庫和插件倉庫配置的id都爲central,它們將會覆蓋POM中央倉庫的配置,而且這裏的url已經可有可無,由於全部的請求都會經過鏡像訪問私服地址
若是隻是爲了代理外部公共倉庫,那麼Nexus的代理倉庫就已經徹底足夠了,對於另外一類Nexus倉庫---宿主倉庫來講,他們主要做用是存儲組織內部的,或一些沒法從公共倉庫得到的第三方構件,用戶能夠經過配置Maven自動部署構件至Nexus的宿主倉庫
使用Maven部署構件至Nexus
- 平常開發生成的快照版本構件能夠直接部署到Nexus中策略爲Snapshot的宿主倉庫中,項目正式發佈的構件則應該部署到Nexus中策略爲Release的宿主倉庫中
POM文件配置
<project> ..... <distributeManagement> <repository> <id>nexus-releases</id> <name>Nexus Release Repository</name> <url>http://localhost:8081/nexus/content/repositories/ release/</url> </repository> <snapshotRepository> <id>nexus-snapshots</id> <name>Nexus Snapshots Repository</name> <url>http://localhost:8081/nexus/content/repositories/ snapshots</url> </snapshotRepository> </distributeManagement> ..... </project>Nexus的倉庫對於匿名用戶只是可讀的,爲了可以部署構件,還須要在settings.xml中配置認證信息
<settings> .... <servers> <server> <id>nexus-releases</id> <username>admin</username> <password>admin123</password> </server> <server> <id>nexus-snapshots</id> <username>admin</username> <password>admin123</password> </server> </servers> .... </settings>
<properties> <springframework.version>4.3.1</springframework.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${springframwork.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>${springframwork.version}</version> </dependency> </dependencies> </dependencyManagement>
- 這是最多見的Maven屬性使用的方式,經過properties元素使得用戶自定義一個或多個Maven屬性,而後在POM的其餘地方使用${屬性名稱}的方式來引用該屬性,這種作法的最大意義是消除重複
Maven的屬性有六類
內置屬性
- ${basedir}:表示項目根目錄,即包含pom.xml文件的目錄
- ${version}:表示項目的版本
POM屬性:用戶可使用該類屬性引用POM文件中對應元素的值
${project.artifactId}:對應<project><artifactId>元素的值
- ${project.build.sourceDirectory}:項目的主源碼目錄.默認爲/src/main/java/
- ${project.build.testSourceDirectory}:項目的測試代碼源碼目錄.默認爲/src/test/java/
- ${project.build.directory}:項目構件輸出目錄.默認爲/target
- ${project.build.outputDirectory}:項目主代碼編譯輸出目錄.默認爲/target/classes
- ${project.build.testOutputDirectory}:項目測試代碼編譯輸出目錄.默認爲/target/test-classes
- ${project.build.groupId}:項目的groupId
- ${project.build.artifactId}:項目的artifactId
- ${project.build.build.finalName}:項目打包輸出文件的名稱,默認爲\${project.artifactId}
-${project.version}
- 自定義屬性:<properties>元素下自定義的Maven屬性
- settings屬性:與POM屬性同理,用戶使用settings.來引用,如:${settings.localRepository}指向用戶本地倉庫的地址
Java系統屬性:全部Java系統屬性均可以使用Maven屬性引用,如${user.home}指向用戶目錄
- 能夠經過mvn help:system查看全部的java系統屬性
環境變量屬性:全部環境變量均可以用env.來引用,例如:${env.JAVA_HOME}
- 能夠經過 mvn help:system查看全部的環境變量
在不一樣的開發環境中,項目的源碼應該使用不一樣的方式進行構件,最多見的就是數據庫的配置了,在開發中,有些項目會在src/main/resources/目錄下放置不一樣環境下的數據庫配置
database.jdbc.driverClass = com.mysql.jdbc.Driver database.jdbc.connectionURL = jdbc:mysql://localhost:3306/dev database.jdbc.username = dev database.jdbc.password = dev-passwd database.jdbc.driverClass = com.mysql.jdbc.Driver database.jdbc.connectionURL = jdbc:mysql://localhost:3306/test database.jdbc.username = test database.jdbc.password = test-passwd爲了應對環境的變化,咱們可使用Maven屬性將這些會發生變化的部分提取出來
database.jdbc.driverClass = ${db.driver} database.jdbc.connectionURL = ${db.url} database.jdbc.username = ${db.username} database.jdbc.password = ${db.password}在settings.xml中經過profile定義不一樣環境下的配置數據
<profiles> <profile> <id>dev</id> <properties> <db.driver>com.mysql.jdbc.Driver</db.driver> <db.url>jdbc:mysql://localhost:3306/dev</db.url> <db.username>dev</db.username> <db.password>dev-passwd</db.password> </properties> </profile> <profile> <id>test</id> <properties> <db.driver>com.mysql.jdbc.Driver</db.driver> <db.url>jdbc:mysql://localhost:3306/test</db.url> <db.username>test</db.username> <db.password>test-passwd</db.password> </properties> </profile> </profiles>
須要注意的是:
- Maven屬性默認只會在POM文件中才會被解析,也就是說${db.username}放到POM文件中會變成dev或test,可是在src/main/resources/目錄下仍然仍是${db.username},所以,須要讓Maven解析資源文件中的Maven屬性
- 資源文件處理的是maven-reosurces-plugin作事,它默認的行爲只是將項目主資源文件複製到主代碼編譯輸出目錄
開啓資源過濾
- Maven默認的主資源目錄和測試目錄的定義是在超級POM文件中
<resources> <resource> <directory>${project.basedir}/src/main/resources</directory> <filtering>true</filtering> </resource> </resources> <testResources> <testResource> <directory>${project.basedir}/src/test/resources</directory> <filtering>true</filtering> </testResource> </testResources>- 經過mvn clean install -Pdev : 經過mvn 的 -P參數表示在命令行激活一個profile=dev的profile
激活Pofile
命令行激活
用戶可使用mvn命令行參數-P 加上profile的id來激活profile,多個id之間逗號分隔
- mvn clean install -Pdev,-Ptest
settings文件顯示激活
若是用戶但願某個profile默認一直處於激活狀態,就能夠配置settings.xml文件的activeProfile元素,表示其配置的profile對全部項目都處於激活狀態
<settings> ..... <activeProfiles> <activeProfile>dev</activeProfile> </activeProfiles> ..... </settings>系統屬性激活
用戶能夠配置當前某系統屬性存在時,自動激活profile
<profiles> <profile> <activation> <property> <name>test</name> </property> </activation> ..... </profile> </profiles>用戶能夠配置當前某系統屬性存在且等於a時,自動激活profile
<profiles> <profile> <activation> <property> <name>test</name> <value>a</value> </property> </activation> ..... </profile> </profiles>用戶能夠在命令行聲明系統屬性
- mvn clean install -Dtest=x
操做系統環境變量激活
Profile還能夠根據操做系統環境激活
<profiles> <profile> <activation> <os> <name>Windows10</name> </os> </activation> ..... </profile> </profiles>文件存在與否激活
Maven能夠根據項目中某個文件是否存在來決定是否激活profile
<profiles> <profile> <activation> <file> <missing>a.properties</missing> <exists>b.properties</exists> </file> </activation> ..... </profile> </profiles>默認激活
用戶能夠在定義profile的時候指定其默認激活
<profiles> <profile> <id>dev</id> <activation> <activeByDefault>true</activeByDefault> </activation> ..... </profile> </profiles>以上激活優先級從上之下依次減少
- 能夠經過mvn help:active-profiles來查看當前激活的profile
- 能夠經過mvn help:all-profiles來查看全部的profile
profile的種類
根據具體的須要,能夠在一下位置聲明profile
- pom.xml:很顯然,pom.xml中聲明的profile只對當前項目有效
- 用戶settings.xml:用戶目錄下.m2/settings.xml中的profile只對本機該用戶全部的Maven項目有效
- 全局settings.xml:Maven安裝目錄下conf/settings.xml中profile對本機全部Maven項目都有效
POM中profile可以使用的元素
<project> <repository></repository> <pluginRepository></pluginRepository> <distributionManagement></distributionManagement> <dependencies></dependencies> <dependencyManagement></dependencyManagement> <modules></modules> <properties></properties> <reporting></reporting> <build> <plugins></plugins> <defaultGoal></defaultGoal> <resources></resources> <testResources></testResources> <finalName></finalName> </build> </project>* pom.xml:很顯然,pom.xml中聲明的profile只對當前項目有效 * 用戶settings.xml:用戶目錄下.m2/settings.xml中的profile只對本機該用戶全部的Maven項目有效 * 全局settings.xml:Maven安裝目錄下conf/settings.xml中profile對本機全部Maven項目都有效- POM中profile可以使用的元素