讀書replay《maven實戰》.2.20190613

前情提要

maven這個工具用了很久了,可是一直都用的迷迷糊糊的,沒有對它進行過系統性的學習,只是知道一些經常使用的功能怎麼實現,因此20190516這一天我從JD購買了徐曉斌老師所著的《maven實戰》一書,準備系統性的學習一下maven。java

pom結構

我工做的主力語言是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

依賴範圍 <scope>

依賴範圍有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是第二直接依賴。

  • 當第二直接依賴是compile時,傳遞性依賴的範圍和第一直接依賴範圍一致。(如C是B的compile依賴,則B是A的什麼範圍依賴,C就是A的什麼範圍依賴)。
  • 當第二直接依賴是test時,依賴不會傳遞。
  • 當第二直接依賴是provided時,只在第一直接依賴爲provided時傳遞,並也是以provided範圍傳遞的。(如c->b=provided,b->a=provided,c->a=provided)
  • 當第二直接依賴是runtime的時候,傳遞性依賴與第一直接依賴的範圍一致,除了compile,它會被傳遞爲runtime。(如c->b=runtime,b->a=compile,c->a=runtime。如c->b=runtime,b->a=test,c->a=test)

最後,有一個叫作<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屬性

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屬性。

相關文章
相關標籤/搜索