maven這個工具用了很久了,可是一直都用的迷迷糊糊的,沒有對它進行過系統性的學習,只是知道一些經常使用的功能怎麼實現,因此20190516這一天我從JD購買了徐曉斌老師所著的《maven實戰》一書,準備系統性的學習一下maven。java
我工做的主力語言是java,spring框架這個體系,因此pom文件每天見。spring
該文件下,有用來聲明當前項目座標的<groupId>,<artifactId>,<name>,<version>這幾個字段。還有用來聲明依賴的<dependencies>。以及用來聲明插件的<build>。app
maven使用由<groupId>,<artifactId>,<version>構成的座標來惟一確認一個構件(任何類型的maven項目均可被理解爲構件)。既然是座標,那麼<groupId>,<artifactId>,<version>的指向就具備惟一性。<version>的命名有有無「-SNAPSHOT」的區別,若是在版本後加了「-SNAPSHOT」,則表示該版本是一個快照版,是不穩定的。快照版有它獨特的用處:當你依賴了一個快照版的構件時,maven會在打包時去檢查本地倉庫中該快照的最新代碼(maven默認會天天和遠程倉庫同步一次數據,這是能夠配置的,具體請查閱書籍或者文檔),例如,B構件依賴了A構件的「0.1-SNAPSHOT」版本,我給A添加了一些代碼,並在倉庫從新deploy了一次A的「0.1-SNAPSHOT」。此時你從新構建B,A的「0.1-SNAPSHOT」的新代碼就已經被包含進去了。若是沒有「-SNAPSHOT」的幫助,B要想使用A的新特性,A須要以新的版本號發佈,B須要修改pom去依賴新版本的A,本質上是A提供了新版本座標,B去依賴新版本。而快照版則是,AB都不修改座標,但B能使用A的新代碼。至於maven是怎麼實現的,你們想知道就去看書吧,我就不贅述了,大概就是maven對快照會維護另外一個它本身生成的版本號。框架
<dependencies> <dependency> <groupId></groupId> <artifactId></artifactId> <version></version> <scope></scope>、 <optional></optional> </dependency> </dependencies>
依賴有兩個點須要注意,一是依賴的範圍,二是依賴的傳遞性。maven
依賴範圍有6:compile,test,provided,runtime,system,importide
在介紹這幾個範圍以前,還須要介紹一個叫作classpath的概念,我我的理解它是編譯環境的意思,我沒深究。maven有3套classpath,一是編譯classpath,二是測試classpath,三是運行時classpath。依賴範圍會決定一個構件是否被歸入某個classpath。好比,junit依賴範圍選則test,則運行時classpath就不會包含junit包。spring-boot
編譯環境對各個依賴範圍的關係是: compile表示進入全部classpath。test表示只進入測試classpath。provided表示進入編譯和測試classpath。runtime表示進入運行時和測試classpath。system和provided一致(但system須要多一個<systemPath>參數標籤作配合,它是用來引入非maven倉庫包的)。inport比較特殊,是用來支撐maven聚合與繼承特性的,它和依賴範圍不要緊,後文再介紹。工具
默認是compile範圍,常見的狀況就是給一個test範圍,表示只在測試classpath有效,這樣在最後的打包文件中,就不會把一些用來測試的包也收錄進去,節省空間。post
假如:A依賴B,B依賴C。學習
從A的角度來講,A對B是第一直接依賴,A對C是傳遞性依賴,B對C是第二直接依賴。
最後,有一個叫作<optional>的選項,當它被設置爲true時,整個依賴不會被傳遞。對A來講,看起來就像B沒有依賴C同樣。此時,A要成功編譯,它得本身添加一個C依賴。這個功能是用來解決某些接口有多種實現的場景。到須要的時候你們去查閱書籍吧。此時只要知道有這麼個功能就行。
maven歸根究竟是個編譯工具,maven將編譯任務拆成了一堆特定的任務,這些任務就組成了maven的生命週期。
三大任務:clean,default,site
clean中的子任務
pre-clean
clean
post-clean
default中的子任務
validate
initialize
generate-sources
process-sources
generate-resources
process-resources
compile
process-classes
generate-test-sources
process-test-sources
generate-test-resources
process-test-resources
test-compile
process-test-classes
test
prepare-package
pachage
pre-integration-test
integration-test
post-integration-test
verify
install
deploy
site中的子任務
pre-site
site
post-site
site-deploy
上面列舉的任務是按照maven執行的順序羅列的。 maven給其中的某幾個子任務綁定了默認的執行器,包括clean,compile,test,pachage,install,deloy等,因此拿到maven後你什麼都不配置,也能執行mvn clean,mvn install 這樣的命令。這些任務的功能我沒有記,不過只須要知道大概有這麼個體系就行,何時須要了,何時再去查閱書籍就能夠。
看到這些任務,你有沒有一種似曾相識的感受,反正我看到這些任務的時候,頓時就明白,命令行上輸入的mvn clean install
是什麼了,很親切。實際上,但你在命令行執行mvn指令的時候,就是告訴maven,去執行哪一個子任務,而因爲任務有前後順序,因此maven會從第一個任務開始執行,直到你指定的任務爲止。而clean,default,site這三個大任務是各自獨立的。mvn clean install表示:執行clean任務中的clean子任務,和default任務中的install任務。因此mvn clean install
的完整寫法實際上是:mvn clean:clean default:install
。因而maven會從pre-clean執行到clean,以及從validate執行到install。
認識了任務(也就是maven的生命週期),咱們就能夠認識插件了。maven插件有一個概念叫:goal,也就是目標。這個goal能夠理解爲是一個方法,用來執行一些邏輯。maven會根據你的配置,把插件的goal綁定到任務上,這個任務在maven中叫作phase!因此還記得你配置插件的時候怎麼寫的嗎?
<build> <plugins> <plugin> <groupId></groupId> <artifactId></artifactId> <version></version> <executions> <execution> <id></id> <phase></phase> <goals> <goal></goal> </goals> <configuration></configuration> </execution> </executions> </plugin> </plugins> </build>
在plugins標籤下,申明一個插件,給定插件座標(groupId,artifactId,version...),設置執行器,將執行器綁定到某個phase(生命週期)上,指定執行什麼goal(也就是插件的動做)。如此一來,當maven通過相應任務的時候,就會使用你配置的插件中的goal。
maven不只支持經過mvn命令行調用生命週期階段,還支持直接調用插件的goal。我如今最經常使用的莫非 mvn clean spring-boot:run 。這其中的spring-boot:run,就是
<plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin>
這個插件提供的功能,這裏咱們直接調用了spring-boot(spring-boot-maven-plugin的name)插件的run這個goal。
maven自身提供了dependence這個插件,用來查看項目依賴,有興趣的同窗能夠去查閱一下,它對於優化項目依賴有很大做用。
maven容許你在pom文件中使用"${}"語法來引用屬性,那麼maven提供了哪些屬性,咱們如何本身聲明屬性,就是須要學習的。此處不須要記住,須要時回頭來看這部分的總結就能夠了。
maven一共提供了6類屬性,分別以下:
內置屬性
內置屬性主要有經常使用的兩個
${basedir}
表示項目的根目錄,也就是包含pom文件的那個目錄
${version}
表示項目版本
POM屬性
這些屬性和POM元素對應,其中一些具備默認值,默認值已在超級POM中定義好了。常見的以下。
${project.build.sourceDirectory}
項目的源碼目錄:src/main/java/
${project.build.testSourceDirectory}
項目的測試源碼目錄:src/test/java/
${project.build.directory}
項目構建輸出目錄:target/
${project.outputDirectory}
項目源碼編譯輸出目錄:target/classes/
${project.testOutputDirectory}
項目測試源碼編譯輸出目錄:target/test-classes/
${project.groupId},${project.artifactId},${project.version}
項目座標
${project.build.finalName}
項目打包後的文件名,默認:${project.artifactId}-${project.version}
自定義屬性
在POM的<properties>元素下自定義的maven屬性,在其餘地方用${xxx}
形式使用,maven會自動替換。
Settings屬性
原理和POM屬性類似,只不過Settings屬性須要用戶使用${settings.xxx}
的形式,來引用settings.xml文件中的XML元素屬性。經常使用的有
${settings.localRepository}
用戶本地倉庫的地址
Java系統屬性
全部java系統屬性,maven均可以引用,詳細信息可經過mvn help:system
指令查看。
環境變量屬性
全部環境變量,maven均可經過${env.xxx}
的形式引用。
maven的屬性不只在POM中能夠引用,在其餘資源文件中也能夠引用,只須要設定過濾器就能夠。我以spring-boot項目爲例,spring-boot項目須要經過<parent>標籤設定一個spring boot starter爲父工程。如:
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.21.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent>
打開這個父工程的pom你會發現有這樣一段配置
<build> <resources> <resource> <directory>${basedir}/src/main/resources</directory> <filtering>true</filtering> <includes> <include>**/application*.yml</include> <include>**/application*.yaml</include> <include>**/application*.properties</include> </includes> </resource> <resource> <directory>${basedir}/src/main/resources</directory> <excludes> <exclude>**/application*.yml</exclude> <exclude>**/application*.yaml</exclude> <exclude>**/application*.properties</exclude> </excludes> </resource> </resources> </build>
原理是,maven使用插件maven-resources-plugin來將項目的資源文件複製到編譯輸出文件夾下去,經過在<build>標籤下申明<resources><resource>標籤,你能夠改變插件maven-resources-plugin的行爲。能夠看到,spring boot starter的配置啓動了filter去過濾形如**/application*.yml的文件,過濾器會將資源文件中的${xxx}
自動替換爲pom屬性。