使用maven-pom進行依賴管理與自動構建

使用maven-pom進行依賴管理與自動構建html

使用maven-pom進行依賴管理與自動構建

最後編輯於 :pencil:{docsify-updated}java

1、讓咱們先了解一下maven

1.maven是優秀的依賴管理工具

隨着開發生態環境的不斷髮展,幾乎全部的java應用都會使用第三方的類庫,尤爲是在這個開源的世界裏, java應用依賴管理已經很難再由人工完成——依賴管理包括解決依賴傳遞、版本衝突、依賴臃腫等問題。git

maven經過groupIdartifactIdversion造成的座標定位系統能準確的定位每個構件(artifact), 開發者能夠經過在pom文件中列出所依賴的構件的座標,讓maven工具從maven倉庫中自動下載全部須要的構件; 另外一方面,經過pom文件間的依賴傳遞、繼承等方式下降依賴管理的難度。github

2.maven是優秀的構建工具

在咱們的開發過程當中,除了編寫代碼之外,有很大一部分時間是花在編譯、運行單元測試、生成文檔、打包和部署這些工具上, 爲也提升工做效率,使開發人員能更多的將精力用於開發工做,咱們須要像maven這樣的工具, 如流水線般的將全部部署以自動化的方式完成。web

3.maven是約定大於配置、簡單易學的工具

在java開發中,經常使用的構建工具備Ant、maven、gradle三種。spring

3.1 Ant

Ant是過程式的,開發者須要顯示的指定每一個目標,以及完成該目標鎖須要執行的任務。shell

Ant不只限於對java項目進行構建,也能夠對其餘語言(如C語言)的項目進行構建。apache

<project name="MyProject" default="dist" basedir="../../../../../../../ProgramFiles/JetBrains/IntelliJ IDEA 2019.3.2/jbr/bin">
  <description>
    simple example build file
  </description>
  <!-- set global properties for this build -->
  <property name="src" location="src"/>
  <property name="build" location="build"/>
  <property name="dist" location="dist"/>

  <target name="init">
    <!-- Create the time stamp -->
    <tstamp/>
    <!-- Create the build directory structure used by compile -->
    <mkdir dir="${build}"/>
  </target>

  <target name="compile" depends="init"
 description="compile the source">
    <!-- Compile the Java code from ${src} into ${build} -->
    <javac srcdir="${src}" destdir="${build}"/>
  </target>

  <target name="dist" depends="compile"
 description="generate the distribution">
    <!-- Create the distribution directory -->
    <mkdir dir="${dist}/lib"/>

    <!-- Put everything in ${build} into the MyProject-${DSTAMP}.jar file -->
    <jar jarfile="${dist}/lib/MyProject-${DSTAMP}.jar" basedir="${build}"/>
  </target>

  <target name="clean"
 description="clean up">
    <!-- Delete the ${build} and ${dist} directory trees -->
    <delete dir="${build}"/>
    <delete dir="${dist}"/>
  </target>
</project>

與maven、gradle不一樣,ant自己沒有對應的中央倉庫,若是想使用maven同樣, 經過聲明的方式管理依賴,並自動處理依賴管理等問題,則使用集成ivy插件管理依賴。 但ant不支持多模塊的管理方式,而maven和gradle支持。windows

ant的這種聲明方式,使開發者能夠根據本身的須要靈活配置,定製本身的項目構建流程; 另外一方面,這種靈活的配置方式,也使得ant的配置相對繁瑣,可讀性較差,學習成本也更高 ——事實上,ant的官方網站上列出的插件就有139種,並且部分工具的官網已經404了。api

3.2 maven

maven與gradle同樣,都是聲明式的配置方式,相對於Ant來講,配置更加方便。 同時maven有內建生命週期,約定了項目的代碼結構,只須簡單配置,就能夠完成構建任務。

頂級pom中定義的項目結構(即默認項目結構)

項目根目錄
  |-- src
  |    |-- main
  |    |    |-- java        -> 主代碼文件
  |    |    |-- resources   -> 主資源文件
  |    |     -- scripts     -> 雖然在maven的默認結構裏有這個目錄,但官網文檔中已刪除了這一級目錄,不建議使用
  |     -- test
  |         |-- java        -> 測試代碼文件
  |          -- resources   -> 測試資源文件
   -- pom.xml

小提示:可經過mvn -DarchetypeCatalog=internal archetype:generate初始化標準結構的maven項目

另外一方面,maven內建生命週期,使用沒法更加項目的構建順序,若是想在構建中加上一些其餘處理邏輯, 則須要用編寫maven插件的方式來完成,成本相對高昂。

3.3 Gradle

gradle是相對新穎的構建工具,它使用一種基於Groovy的特定領域語言(DSL)來聲明項目設置, 目前也增長了基於Kotlin語言的kotlin-based DSL,拋棄了基於XML的各類繁瑣配置。

gradle沒有maven般的生命週期,真正起做用的是所引入的Plugin,所以相對更爲靈活。

另外一方面,gradle做爲新生工具,不管是程序仍是文檔都在不斷的完善中,使用和學習相對困難。 同時,因爲gradle的配置是基於groovy/kotlin語法的,所以使用gradle須要掌握相應的語法知識。

gradle是對ant和maven特色折衷的結果,相對於ant其可讀性更好,相對於maven其更爲靈活。

2、pom基本結構

POM是project object mode的簡寫,maven經過pom文件對項目進行描述, 在開發者在pom文件中對項目屬性和構建過程進行定義後,maven便可自動構建項目並生成站點。

1.概覽

<project xmlns="http://maven.apache.org/POM/4.0.0"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <!-- The Basics -->
    <groupId>...</groupId>
    <artifactId>...</artifactId>
    <version>...</version>
    <packaging>...</packaging>
    <dependencies>...</dependencies>
    <parent>...</parent>
    <dependencyManagement>...</dependencyManagement>
    <modules>...</modules>
    <properties>...</properties>

    <!-- Build Settings -->
    <build>...</build>
    <reporting>...</reporting>

    <!-- More Project Information -->
    <name>...</name>
    <description>...</description>
    <url>...</url>
    <inceptionYear>...</inceptionYear>
    <licenses>...</licenses>
    <organization>...</organization>
    <developers>...</developers>
    <contributors>...</contributors>

    <!-- Environment Settings -->
    <issueManagement>...</issueManagement>
    <ciManagement>...</ciManagement>
    <mailingLists>...</mailingLists>
    <scm>...</scm>
    <prerequisites>...</prerequisites>
    <repositories>...</repositories>
    <pluginRepositories>...</pluginRepositories>
    <distributionManagement>...</distributionManagement>
    <profiles>...</profiles>
</project>

2.項目座標

聲明式的項目依賴管理,除了須要一個存儲有全部jar包的中央倉庫外,還須要一套合適的命名系統, 可以保證每一個jar包都有一個惟一的名字,包括不一樣版本。
在maven中,jar包的座標經過groupIdartifactIdversion三個字段決定, 相似於xyz座標系的三個軸。

<project xmlns="http://maven.apache.org/POM/4.0.0"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
 
  <groupId>org.codehaus.mojo</groupId>
  <artifactId>my-project</artifactId>
  <version>1.0</version>
</project>
2.1 groupId

一般而言,一個組織(公司、基金會等)的項目一般使用同一個groupId, 所以通常而言groupId是該組織所擁有的域名,如jacoco對應的groupId爲org.jacoco, 而對應的網站爲http://jacoco.org(域名的優先級順序是反過來的),

事實上不必定嚴格遵循該規則,如maven的groupId爲org.apache.maven, 而maven官方插件的groupId爲org.apache.maven.plugins, 儘管同並不存在plugins.maven.apache.org這個四級域名, 並且他們同屬於apache基金會(apache.org)下的項目。

另外也有徹底和域名不相關的,如junit包的groupId爲junt

另外須要注意的一點是,雖然groupId沒必要與項目的包結構相對應,可是遵循這種作法是一種最佳實踐, 由於這樣能夠有效避免包之間的類衝突。

2.2 artifactId

artifactId一般是已知項目(或項目模塊)的名稱,它與groupId一塊兒構成了一個項目(或項目模塊)的定位, 用於將該項目(或項目模塊)與世界上其餘全部項目(或項目模塊)分開。

實踐

雖然在groupId中就能夠看到項目名稱,但在artifactId中通常仍會將項目名稱做爲開頭, 這是由於咱們在溝通時一般會用 artifactId 進行交流,而不會帶上 groupId , 如張三告訴李四須要在項目中引入maven-enforcer-plugin包作項目自動檢查。

2.3 version

如同字面的含義,version表明的是項目(或項目模塊)的版本。

groupIdartifactIdversion一同定位了一個發佈包的惟一位置。 如maven-enforcer-plugin-3.0.0-M3.jar在倉庫中的位置爲 $M2_REPO/org/apache/maven/plugins/maven-enforcer-plugin/3.0.0-M3, 而其對應的pom中的內容以下:

<parent>
  <groupId>org.apache.maven.enforcer</groupId>
  <artifactId>enforcer</artifactId>
  <version>3.0.0-M3</version>
</parent>

<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>

parent標籤咱們後續會提到,parent標籤下是父pom的座標,子pom繼承父pom的全部定義,包括version

3.package標籤

package標籤定義了構件的打包方式和使用方式,若是不指定值,默認使用jar做爲打包方式。

這裏只說明兩個咱們使用較多的打包方式:pom、jar。

3.1 打包爲pom

定義打包方式爲pom是有一些特殊的項目,其自己除了pom文件外,不包含其餘文件, 這些項目中的pom主要是用來被其餘項目引用、繼承,或者用來管理項目擁有的模塊。

這裏的繼承概念與java中類的繼承概念相似,子pom擁有父pom的全部配置, 能夠經過自定義的方式覆蓋/複寫父pom中已經存在的配置。

咱們拿springboot集成中的兩個打包方式爲pom的項目舉例說明。 在springboot的官方集成教程中,會告訴你可使用兩種方式集成,一種經過父pom繼承:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.1.RELEASE</version>
</parent>

另外一種是經過引入spring-boot的依賴管理:

<dependencyManagement>
    <dependencies>
        <dependency>
            <!-- Import dependency management from Spring Boot -->
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>2.1.1.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

若是咱們找到spring-boot-starter-parent項目,就會發現這個項目裏僅僅只有pom文件 springboot-starter

這是由於在項目中,咱們一般會存在多個子模塊,而這些子模塊會須要一些公用的配置, 好比統一的構建流程、統一的依賴版本管理,而這些內容就以pom的形式抽取出來, 造成了這種打包方式爲pom的項目。

須要注意的是,打包方式爲pom的項目裏,除了pom文件,其餘文件不會參與構建。 事實上若是咱們去看pom打包對應的默認插件配置就會發現,只有install和deploy兩個生命週期有插件綁定:

<phases>
  <install>
    org.apache.maven.plugins:maven-install-plugin:2.4:install
  </install>
  <deploy>
    org.apache.maven.plugins:maven-deploy-plugin:2.7:deploy
  </deploy>
</phases>
3.2 打包爲jar

jar包是java程序包經常使用的發佈形式,其能夠做爲庫文件被其餘項目使用, 也能夠經過java -jar命令直接運行。

jar包中即有靜態資源,也有編譯後生成的class文件,這意味着其打包流程也更爲複雜:

<phases>
  <process-resources>
    org.apache.maven.plugins:maven-resources-plugin:2.6:resources
  </process-resources>
  <compile>
    org.apache.maven.plugins:maven-compiler-plugin:3.1:compile
  </compile>
  <process-test-resources>
    org.apache.maven.plugins:maven-resources-plugin:2.6:testResources
  </process-test-resources>
  <test-compile>
    org.apache.maven.plugins:maven-compiler-plugin:3.1:testCompile
  </test-compile>
  <test>
    org.apache.maven.plugins:maven-surefire-plugin:2.12.4:test
  </test>
  <package>
    org.apache.maven.plugins:maven-jar-plugin:2.4:jar
  </package>
  <install>
    org.apache.maven.plugins:maven-install-plugin:2.4:install
  </install>
  <deploy>
    org.apache.maven.plugins:maven-deploy-plugin:2.7:deploy
  </deploy>
</phases>
3.2 其餘

目前maven支持這些打包方式: pom、jar、maven-plugin、ejb、war、ear、rar。 其中每種打包方式都有相應的打包插件對其進行打包,這些插件都已經被maven默認進行了綁定, 具體打包方式對應的插件配置有興趣的能夠去 官網進行查看。

4.項目間關係

在上文中咱們提到了多種項目間關係,包含依賴關係、繼承關係、聚合關係(上文提到的模塊管理)。

4.1依賴關係

項目間依賴關係是最經常使用的關係,當咱們項目A使用一個項目B打成的jar時,A就對B造成了依賴。

須要注意的是,若是的java項目開發已經沒法離開依賴了,A所依賴的項目B,實際上也開發中也依賴了項目C。 此時就生成的依賴的傳遞,即若是A依賴了B,B依賴了C,那麼A依賴C。

依賴傳遞的存在,會使得單點的依賴關係也變得複雜起來。實際在項目中,咱們會依賴大量的jar包, 此時就可能存A依賴的B、C同時使用兩個不一樣版本的D:

A
 |-B
    |-D:1.1
 |-C
    |-D:1.2

咱們知道,一個項目的不一樣版本一般會有大量的類是重複了,若是jvm在加載同名類時,會忽略重複的類, 這樣可能致使出現其中一個包只有一半代碼的狀況,這明顯是不合理的。這種依賴的出現就叫到依賴衝突, 當出現衝突時,maven會選取其中的一個版本放入最終的打包後文件中(如jar),而忽略其餘版本。

具體的選取策略能夠參考這篇短文

4.2繼承關係

繼承關係咱們在上文中已經提到,咱們能夠經過parent標籤引入父級pom, 此時子pom擁有了父級pom中的全部定義,並能夠在子pom中複寫相應的定義。

因爲pom的繼承關係可能有多級,所以若是你想確認當前項目的最終pom結構時, 就必需要將全部的pom整合到一塊兒,此時咱們能夠經過mvn help:effective-pom命令生成整合後的pom (若是使用idea,右鍵maven菜單下也有相應的生成操做)。

若是在一個沒有父pom的項目中執行mvn help:effective-pom,你會發現最終生成的pom中, 忽然出現了大量你沒有定義的內容,這是所以maven自身定義了一個super pom, 全部的pom都默認繼承該pom(可類比java中的Object類), 在上文中咱們提到的maven項目的默認結構,就是在這裏定義的。

super pom的具體內容,能夠去官網查看

子pom並不會繼承父pom的全部內容,僅如下內容將會被繼承: - groupId - version - scm - issueManagement - ciManagement - mailingLists - properties - dependencyManagement - dependencies - repositories - pluginRepositories - build - reporting - profiles - description - url - inceptionYear - organization - licenses - developers - contributors

須要注意的是如下幾個標籤不會被繼承: - artifactId - package - name - prerequisites

4.3聚合關係

聚合關係經常出如今有多個模塊的項目中,好比項目A有三個子項目aa、ab、ac, 這時咱們能夠在目錄A下也添加一個pom,並定義aa、ab、ac是項目A的三個子項目, 這樣咱們對項目A作構建(如mvn clean package)時,maven就會分別對aa、ab、ac作相應的構建操做。

A
 |-- pom.xml
 |-- aa
       |-- pom.xml
 |-- ab
       |-- pom.xml
 |-- ac
       |-- pom.xml

子項目的聲明方式以經過modules標籤進行,當咱們在項目A的pom中添加以下配置, aa、ab、ac就會成爲A的子項目:

<modules>
    <module>aa</module>
    <module>ab</module>
    <module>ac</module>
</modules>

aa、ab、ac並非這三個項目的相對路徑(相對於A)

在這裏,可能有的人已經注意到了,這種項目層級結構,很是適合將A項目的pom做爲父pom, 將aa、ab、ac的pom中相同的內容抽取至父pom中進行管理。
實際上在項目的實踐中,一般也是這麼處理的,但也有一些例外。好比aa、ab是springboot項目, 而ac倒是老舊的spring項目,此時可能出現:A做爲aa和ab的父級pom統一管理相關配置, 同時aa和ab做爲子項目存在,而ac不繼承A,只做爲子項目存在,這樣咱們即可以統一管理aa和ab的配置, 也可以經過一條命令,對aa、ab、ac一塊兒完成構建操做。 (實際實踐過程當中,不推薦這種模式,咱們能夠把ac提到與A平級的位置,這樣的結構更清晰)

值得一提的是,若是aa與ab間存在依賴關係,好比aa依賴於ab,那麼即便aa比ab先聲明,maven在構建時, 也會很聰明的先對ab進行構建,再構建aa。

5.dependencies標籤

<dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <type>jar</type>
      <scope>test</scope>
      <optional>true</optional>
    </dependency>
</dependencies>

dependencies標籤下聲明瞭當前項目所使用的依賴構件。

須要注意的是,這裏聲明的文件必須可以在maven倉庫中找到, maven默認使用中央倉庫,中央倉庫中包括了全部經常使用的開源jar包, 但對於一些閉源的三方jar包,就沒法在中央倉庫中找到。
所以,公司通常都會有本身的私有倉庫,咱們須要修改maven的settings.xml文件, 使maven使用私服處理依賴和發佈。
對於私有倉庫上沒有的項目,咱們可能經過如下命令,將其上傳至私有倉庫:

shell script mvn deploy:deploy-file -Dfile=non-maven-proj.jar -DgroupId=some.group \ -DartifactId=non-maven-proj -Dversion=1.0 -Dpackaging=jar

5.1 classifier標籤

在引用依賴時,僅僅使用groupIdartifactIdversion做爲座標可能還並不太足夠。 考慮一下這種場景:有一個項目,該項目須要提供一個基於jre1.7的jar包, 同時還提供了一個基於jre1.8的jar包,即一套源碼須要使用兩種jdk打成不一樣的jar包。

這種狀況下咱們會配置兩套打包方式,默認使用1.8打包,並可選擇1.7打包,配置以下:

<profile>
    <id>jdk17</id>
    <properties>
        <!--使用jdk1.7打包-->
        <java.version>1.7</java.version>
    </properties>
    <build>
        <plugins>
            <plugin>
                <artifactId>maven-jar-plugin</artifactId>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>jar</goal>
                        </goals>
                        <configuration>
                            <!--配置classifier,與1.8版本的包區分-->
                            <classifier>jdk17</classifier>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</profile>

若是用1.8打包發佈的jar名稱爲demo-1.0.jar,那麼用1.7打包時, 發佈的jar的名稱爲demo-1.0-jdk17.jar——實際場景下,classifier標籤能夠配置成任意字符串, 並不必定是jdk17。

而使用者在引入1.7版本的依賴時,就須要額外添加classifier標籤

<dependency>
  <groupId>some.example</groupId>
  <artifactId>demo</artifactId>
  <version>1.0</version>
  <classifier>jdk17</classifier>
</dependency>

classifier標籤有一處更常見的地方,是sources.jar:
classifier_sources

這就是咱們上文中說到的一個pom,打出了兩個包,源碼包對應的classifier標籤就是sources, 而測試源碼包對應的值爲test-sources

5.2 type標籤

type標籤描述了依賴類型,若是不定義該值,使用默認值jar。
對於不一樣類型的依賴,maven的處理策略也不相同:

type classifier extension packaging language added to classpath includesDependencies
pom   = type = type none    
jar   = type = type java true  
test-jar tests jar jar java true  
maven-plugin   jar = type java true  
ejb   jar = type java true  
ejb-client client jar ejb java true  
war   = type = type java   true
ear   = type = type java   true
rar   = type = type java   true
java-source sources jar = type java    
javadoc javadoc jar = type java true  

實際開發中,咱們只會用到jarpom兩種類型,你們能夠注意到pom類型的依賴是不會產生依賴傳遞, 也不會被加入classpath中。

5.3 scope標籤

該標籤訂義了該包在哪些階段會被放到classpath中,另外還定義了依賴的可傳遞性。其聚會範圍以下:

classpath即類、類庫加載路徑,在咱們的依賴中,大多數依賴會在編譯、測試、運行都用到, 但也有一些依賴可能只有在編譯、測試階段運到(好比測試相關的包),還有部分包可能在編譯、測試時須要, 但實際發佈時不須要(好比jdk相關的包)。

含義 是否會將該依賴向下傳遞
compile 該值是默認值。該依賴在全部階段的都會放入classpath中,是最經常使用的值。
provided 只有在編譯和測試時放入classpath中,實際打包時忽略該包
runtime 編譯時不須要,只在測試和打包時須要
test 只在對測試代碼作編譯和執行時須要
system 手動指定所依賴包的本地存在路徑 只傳遞自己
  • compile是最經常使用的,通常項目引入的包在編譯、測試、發佈階段都會用到。
  • provided通常用於基礎包,在基礎包的開發過程當中會依賴一些三方包, 但可能不想爲使用者帶來依賴衝突的負擔,此時可使用該範圍,由使用者自行決定該三方包的版本 (固然須要是兼容的版本);另外一種不推薦的使用方式是將該依賴做爲可選依賴向下傳遞, 舉一個例子:咱們開發了一個基礎工具類包,裏面含有許多工具,像poi工具、時間轉換工具、中文數字工具, 另外裏面還包含了TapConfigUtil用來獲取配置, 此時咱們注意到配置中心的集成是以springboot爲基礎的, 而咱們想讓該工具包也能夠被其餘類型的項目使用, 這種狀況下咱們能夠給commons-aap-appconfig包加上provided範圍。 若是某個使用者只不須要使用TapConfigUtil,那麼他就不須要引入commons-aap-appconfig包。 (事實上commons-aap-appconfig包裏引入spring相關的依賴裏使用了provided範圍)
  • runtime不多用到,通常狀況下使用compile就能夠了, 使用runtime甚至可能給IDE帶來一些問題
  • test用在測試相關的包上,測試代碼的安全性會更低(畢竟沒人會寫測試代碼來測試測試代碼 ——對外發布的測試工具除外),使用test標記測試相關的包,能夠避免被其餘開發人員錯誤的使用。
  • system須要配置systemPath標籤使用,用來指定使用倉庫中沒有的包。 與其相比,上傳私服是一種更好的方式,由於system標記的包,並不會將其依賴的包傳遞給使用者, 這意味着你須要本身處理好依賴關係。
5.4 systemPath標籤

僅在依賴項範圍是system時使用,不然構建將失敗。
systemPath標籤中的路徑必須是絕對路徑,所以建議使用maven變量拼裝路徑, 例如${project.basedir}/lib。

5.5 optional標籤

在上面介紹scope標籤provided取值時,咱們講了它能夠實現讓使用都按需決定是否引入依賴, 這樣以達到避免引入不須要的依賴。
事實上provided更多的是用來要求使用方或運行時系統提供具體的包,而不是用於這種用途。
配置依賴的可選應該使用optional標籤來實現。

5.6 exclusions標籤

dependency標籤下還存在exclusions標籤,咱們在前面講到了依賴傳遞可能帶來的依賴衝突問題, exclusions標籤就是排除掉不想被傳遞過來的依賴。

這裏須要注意的是,A->B->C時,若是你在引入依賴A的時候,將B排除,那麼C也將被排除, 除非有其餘地方也引入了C(有點像對「樹」進行操做,排除進至關於直接剪掉了一個「子樹」)

<exclusions>
    <exclusion>
        <groupId>org.objenesis</groupId>
        <artifactId>objenesis</artifactId>
    </exclusion>
</exclusions>

exclusion下只有groupIdartifactId兩個標籤,由於只不可能同時依賴一個包的不一樣版本。

5.7 其餘

version標籤除了指定具體版本外,還能夠指定範圍版本,有興趣的能夠查看 官網, 不過不推薦在項目中使用。

各小版本在maven項目中的前後發佈順序能夠看 這裏

6.dependencyManagement標籤

正如其名字所表明的含義,該標籤的做用就是管理依賴。
在上面的dependency標籤的介紹中,咱們看到在dependency標籤下有衆多的子標籤, 想象一下咱們有多個服務,若是每一個服務都在引入同一依賴時配置好全部內容, 那將是很是麻煩,並且若是須要對某個所依賴的jar包作升級時,咱們不得不改每個pom中的version信息。

可能有人已經想到,咱們能夠將這些依賴放到父pom裏面就能夠了。這是一種很好的策略, 但讓咱們考慮項目A、B,A中引入了a、b三個包,B中引入b、c兩個包, 若是咱們使用父pom管理公用依賴,將只有b會被放到父pom裏,而a、c由A、B本身管理。 若是後來B項目的功能增長,也須要引入a包,那麼咱們就須要把a包從A項目中提到父pom中, 以保證對依賴的統一管理——由於,這種方式並不能真正實現依賴的統一管理。

如今考慮另外一種方式,咱們在父pom裏列出項目中全部使用到的依賴包,並配置好全部內容, 但並不真正的對這些包產生依賴,僅僅是列出來。若是那個子pom真正須要用到, 那麼他只須要聲明一些儘量少的信息,就能夠對對應的包產生依賴 ——這種方案就是使用dependencyManagement標籤

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>${spring.boot.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>com.demo.company</groupId>
            <artifactId>someproject-commons-versions</artifactId>
            <version>1.0.0</version>
        </dependency>
    </dependencies>
</dependencyManagement>

dependencyManagement標籤下只有一個dependencies標籤, 裏面的內容和上文中的dependencies標籤幾乎同樣,惟一不同的是, 在dependencyManagement標籤下的scope標籤新增一種取值——import

import只能用於導入package值爲pom的依賴包, 當在項目A的pom中使用import導入項目B時,A、B的dependencyManagement標籤下的內容將被合併。 這是一個很是有用的特性,這意味着咱們能夠建立一個項目專門用來作統一版本控制, 在這個項目裏,咱們將全部的包的versionscope等內容都定義好, 其餘項目只須要在dependencyManagement下導入該包,而不須要破壞其自己的繼承結構。

雖然dependencyManagement有這樣的方便, 但須要注意的是dependencyManagement下的內容不只對當前pom、子pom生效, 一樣會覆蓋因爲依賴傳遞而引入的jar包。好比在項目A中,咱們依賴了aa-1.0、bb-2.0, 而在bb-2.0中依賴了aa-2.0,此時,若是aa的版本是在dependencyManagement中定義, 那麼aa-2.0將被放棄,aa-1.0生效,這樣將致使bb-2.0可能使用了根本不存在的類和api, 最終項目構建失敗。

7.Properties標籤

如其字面意思——屬性。properties標籤下定性的屬性,能夠在pom的任何地方被引用, 引用方式爲${屬性名},其中屬性名即標籤名,咱們能夠利用這些屬性管理一組依賴, 或者將一些容易變化的值集中到properties標籤下管理。

<properties>
    <org.powermock.version>2.0.2</org.powermock.version>
</properties>
<dependencies>
    <dependency>
        <groupId>org.powermock</groupId>
        <artifactId>powermock-module-testng</artifactId>
        <version>${org.powermock.version}</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.powermock</groupId>
        <artifactId>powermock-api-mockito2</artifactId>
        <version>${org.powermock.version}</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.powermock</groupId>
        <artifactId>powermock-module-junit4</artifactId>
        <version>${org.powermock.version}</version>
        <scope>test</scope>
    </dependency>
</dependencies>

除了自定義屬性外,maven插件也會提供一些屬性,以達到對其進行配置的目的。 同時,maven還提供如下屬性能夠在pom文件中使用: - env.X : 之前綴env開頭的屬性,對應於系統環境變量, 好比經過${env.PATH}能夠取到環境變量PATH的值。這裏須要注意的是, maven對環境變量的大小寫是敏感的,windows下的環境變量在maven中必須所有大寫。 - project.X : project爲前綴是屬性是項目的屬性值, 包括了pom中的標籤——好比${project.parent.artifactId}能夠獲取 *<parent>* 下 *<artifactId>* 的值;除此以外還有一些其餘屬性,具體可參考 [官網說明](http://maven.apache.org/ref/3.6.3/maven-model-builder/#Model_Interpolation)。 值得一提是,該屬性值並非取當前pom中的標籤,而是取**effective-pom**中的標籤, 也就是說能夠取到從父pom中繼承的一些內容。 - settings.X : 與**project.X**相似,還過**settings.X**取的是**settings.xml**中的內容, 具體內容一樣參考 [官網說明](http://maven.apache.org/ref/3.6.3/maven-model-builder/#Model_Interpolation)。 - java系統屬性 : 全部能夠經過`java.lang.System.getProperties()`方法獲取的變量, 都可以在pom中獲取,如**${java.home}**。具體可參考 oracle官方文檔

3、配置構建任務

在上一章的內容是pom的一些基礎標籤的使用,靈活使用這些基礎標籤就能夠達成項目的基本構建任務。

除了基本構建外,咱們還每每在構建時須要進行打包前依賴衝突檢測、發佈前進碼檢查、 發佈時同步發佈源碼包、自動生成並部署項目站點等任務,maven做爲強大的構建工具, 經過構建插件的方式,將全部的這些任務所有沿着maven構建生命週期自動執行。

1.maven構建的生命週期

maven構建的生命週期定義了打包和發佈項目時須要經歷的流程,maven內置的生命週期有 clean(清理)、default(打包發佈)、site(生成站點文檔)三種。

這三種生命週期由一些階段(phase)組成,三種生命週期對應的階段以下:

1.1 clean生命週期
Phase Description
pre-clean execute processes needed prior to the actual project cleaning
clean remove all files generated by the previous build
post-clean execute processes needed to finalize the project cleaning
1.2 default生命週期
Phase Description
validate validate the project is correct and all necessary information is available.
initialize initialize build state, e.g. set properties or create directories.
generate-sources generate any source code for inclusion in compilation.
process-sources process the source code, for example to filter any values.
generate-resources generate resources for inclusion in the package.
process-resources copy and process the resources into the destination directory, ready for packaging.
compile compile the source code of the project.
process-classes post-process the generated files from compilation, for example to do bytecode enhancement on Java classes.
generate-test-sources generate any test source code for inclusion in compilation.
process-test-sources process the test source code, for example to filter any values.
generate-test-resources create resources for testing.
process-test-resources copy and process the resources into the test destination directory.
test-compile compile the test source code into the test destination directory
process-test-classes post-process the generated files from test compilation, for example to do bytecode enhancement on Java classes.
test run tests using a suitable unit testing framework. These tests should not require the code be packaged or deployed.
prepare-package perform any operations necessary to prepare a package before the actual packaging. This often results in an unpacked, processed version of the package.
package take the compiled code and package it in its distributable format, such as a JAR.
pre-integration-test perform actions required before integration tests are executed. This may involve things such as setting up the required environment.
integration-test process and deploy the package if necessary into an environment where integration tests can be run.
post-integration-test perform actions required after integration tests have been executed. This may including cleaning up the environment.
verify run any checks to verify the package is valid and meets quality criteria.
install install the package into the local repository, for use as a dependency in other projects locally.
deploy done in an integration or release environment, copies the final package to the remote repository for sharing with other developers and projects.
1.3 site生命週期
Phase Description
pre-site execute processes needed prior to the actual project site generation
site generate the project's site documentation
post-site execute processes needed to finalize the site generation, and to prepare for site deployment
site-deploy deploy the generated site documentation to the specified web server

每一個生命週期擁有的階段互不重疊,且在構建時每一個生命週期中的階段按順序進行。 這意味着當咱們使用mvn命令構建時,若是指定了某一階段,maven會先執行全部前置階段, 再執行指定的階段——這使得maven的使用至關容易,使用都只須要記住經常使用的幾個階段 (如clean、deploy、site)便可單獨完成項目的構建和發佈。

2.插件的「構建階段」

除了maven內置的階段外,maven容許插件自定義階段,好比maven-dependency-plugin 插件中經常使用的copy-dependencies階段。插件階段與內置階段同樣可使用mvn命令運行, 惟一不一樣的是須要在階段前加上插件的座標信息,如mvn dependency:copy-dependencies (使用非maven官方插件時,須要將dependency換成groupId:artifactId的形式)。

在使用這些插件前,咱們須要在pom文件中引入插件:

<build>
  <plugins>
    <plugin>
      <artifactId>maven-dependency-plugin</artifactId>
      <version>2.8</version>
    </plugin>
  </plugins>
</build>

事實上基本構建插件maven都有內置,super pom中甚至進行了缺乏配置,不須要咱們再手動添加

雖然maven容許插件自定義階段,但不容許修改內置的生命週期,

3.build標籤

build標籤下定義了構建的相關信息和配置:

<build>
  <defaultGoal>install</defaultGoal>
  <directory>${basedir}/target</directory>
  <finalName>${artifactId}-${version}</finalName>
  <resources>
    <resource>
      <filtering>true</filtering>
      <directory>${basedir}/src/main/resources</directory>
      <includes>
        <include>**/application*.yml</include>
        <include>**/application*.yaml</include>
        <include>**/application*.properties</include>
      </includes>
    </resource>
    <resource>
      <targetPath>META-INF/plexus</targetPath>
      <filtering>false</filtering>
      <directory>${basedir}/src/main/plexus</directory>
      <includes>
        <include>configuration.xml</include>
      </includes>
      <excludes>
        <exclude>**/*.properties</exclude>
      </excludes>
    </resource>
  </resources>
  <testResources>
    ...
  </testResources>
  <filters>
    <filter>filters/filter1.properties</filter>
  </filters>
</build>
3.1 defaultGoal

默認構建目標(階段),若是配置該值,在執行mvn命令時能夠不指定構建階段。

<defaultGoal>clean package</defaultGoal>
3.2 directory

構建生成的相關文件的存放位置,默認配置爲${project.basedir}/target

3.3 finalName

構建生成jar包(或其餘類型的包)的名稱,默認爲${artifactId}-${version}

須要注意的是,finalName並不是如字面意思通常是打包後的最終名稱, 實際打包插件可能會在後面添加後綴:若是咱們配置finalName配置爲demo, 同時爲打包插件配置classifier標籤爲jdk17(上面咱們講到過), 那麼實際生成的jar名稱爲demo-jdk17.jar

3.4 resources 與 testResources

resources標籤用來指定資源文件的存放路徑,這些文件不會參與編譯,會直接拷貝到相應的路徑下。

  • targetPath: 拷貝目標路徑,在default週期的process-resources階段,資源將被拷貝到對應的路徑下。 若是不指定,文件將被拷貝至根目錄下。
  • filtering: 是否啓用過濾,若是啓用,則容許在resource指定的文件中使用佔位符替換 ,默認不啓用。這裏的替換範圍包括全部在pom中可使用的變量,替換方式爲${placeholder} ——這是官方文檔的說法,實際在spring boot項目中的有效使用格式爲@placeholder@
  • directory: 資源文件的源路徑,默認爲${project.basedir}/src/main/resources
  • includes: 源路徑下須要包含的文件,容許使用通配符的方式匹配
  • excludes: 源路徑下須要排除的文件,容許使用通配符的方式匹配

testResources中的內容與resources一致,不一樣的是其對應的生命週期和directory的默認值。

3.5 filter

filter能夠用來引入一些properties文件,在這樣文件裏定義的屬性, 也會加入到開啓了filtering的資源文件的佔位符替換範圍。

3.6 plugins 與 pluginManagement
3.6.1 plugins

plugins標籤用於引入和配置打包插件。

<build>
  ...
  <pluginManagement>
    <plugins>
        ...
    </plugins>
  </pluginManagement>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-jar-plugin</artifactId>
      <version>2.6</version>
      <extensions>false</extensions>
      <inherited>true</inherited>
      <configuration>
        <classifier>test</classifier>
      </configuration>
      <dependencies>...</dependencies>
      <executions>...</executions>
    </plugin>
  </plugins>
</build>
  • extensions: 是否加載該插件擁有的擴展包(後方會講到),默認爲false
  • inherited: 是否容許子級pom繼承,默認爲true
  • configuration: configuration下的內容較爲特殊,maven並未約束其子級標籤的範圍。 實際上子標籤對應是的插件的可配置屬性,所以configuration下的內容須要經過閱讀插件的使用文檔來進行配置 這裏須要注意的是,不管configuration出如今父級pom仍是在pluginManagement中, 其中的定義屬性最終會在合併後傳遞給插件。 (合併策略參考官方網站)
  • dependencies: 與項目的dependencies結構一致,不一樣的是這些依賴不做用於項目,而是做用的插件, 經過指定依賴,咱們能夠用來指定插件依賴的包的版本等信息。
  • executions: 對插件提供的maven階段進行配置,該標籤下的內容較爲複雜,實際使用也比較多,值得單獨一講

executions下包含的是execution列表,每一個execution能夠理解爲一個任務。

<plugin>
  <artifactId>maven-antrun-plugin</artifactId>
  <version>1.1</version>
  <executions>
    <execution>
      <id>echodir</id>
      <goals>
        <goal>run</goal>
      </goals>
      <phase>verify</phase>
      <inherited>false</inherited>
      <configuration>
        <tasks>
          <echo>Build Dir: ${project.build.directory}</echo>
        </tasks>
      </configuration>
    </execution>
  </executions>
</plugin>
  • id: 任務的id,用來區分不一樣的execution,該值會在該任務執行時打印出來, 格式:[plugin:goal (id)]
  • goals: 須要插件執行的構建目標(階段),每一個插件具體提供的構建目標能夠參考官方文檔, 或者經過maven-help-plugin查看插件的信息: mvn help:describe -Dplugin=com.github.spotbugs:spotbugs-maven-plugin:3.1.12.2
  • phase: 指定須要綁定的maven內置構建階段。在這裏須要注意,插件的構建階段爲goal, 內置構建階段爲phase,由於插件的階段是沒有生命週期的。phase標籤的做用, 就是將execution對應的任務綁定到maven的某個內置構建階段,在maven生命週期時, 當執行到該phase時,會自動運行該execution下的goals,從而對maven構建進行擴展。 好比咱們能夠將pmd插件的pmd、check關聯到verfiy階段,這樣當咱們運行mvn verify時, 會自動進行代碼問題的檢查。
  • inherited: 是否容許該execution被子pom繼承,默認爲true
  • configuration: 與外層的configuration內容一致,不過僅對該execution生效
3.6.2 pluginManagement

pluginManagement的做用與dependencyManagement的做用相似,咱們能夠在父pom裏配置全部插件, 但不實際引入插件,子pom按需在plugins標籤下引入具體插件,這樣就能夠達到插件配置集中管理的效果 —— 同dependencies下引入dependencyManagement中的依賴同樣, plugins下引入pluginManagement中的插件只須要聲明groupIdartifactId

<build>
  <pluginManagement>
    <plugins>
      <plugin>
        <artifactId>maven-antrun-plugin</artifactId>
        <version>1.1</version>
        <executions>
          <execution>
            <id>echodir</id>
            <goals>
              <goal>run</goal>
            </goals>
            <phase>verify</phase>
            <inherited>false</inherited>
            <configuration>
              <tasks>
                <echo>Build Dir: ${project.build.directory}</echo>
              </tasks>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </pluginManagement>
  <plugins>
    <plugin>
      <artifactId>maven-antrun-plugin</artifactId>
    </plugin>
  </plugins>
</build>

4.profiles標籤

profiles容許咱們定義多個profile,並設置每一個profile的激活條件,以達到自如切換多套配置的目的。

<profiles>
  <profile>
    <id>test</id>
    <activation>...</activation>
    <build>...</build>
    <modules>...</modules>
    <repositories>...</repositories>
    <pluginRepositories>...</pluginRepositories>
    <dependencies>...</dependencies>
    <reporting>...</reporting>
    <dependencyManagement>...</dependencyManagement>
    <distributionManagement>...</distributionManagement>
  </profile>
</profiles>
4.1 id

如字面意思,即套profile的標識,咱們能夠經過mvn命令的-P選項直接經過id激活對應的profile: mvn -P test clean deploy

4.2 activation

profile的自動激活條件。

除了經過手動指定profile外,還能夠根據一些條件,自動激活profile

<profile>
  <id>test</id>
  <activation>
    <activeByDefault>false</activeByDefault>
    <jdk>1.5</jdk>
    <os>
      <name>Windows XP</name>
      <family>Windows</family>
      <arch>x86</arch>
      <version>5.1.2600</version>
    </os>
    <property>
      <name>sparrow-type</name>
      <value>African</value>
    </property>
    <file>
      <exists>${basedir}/file2.properties</exists>
      <missing>${basedir}/file1.properties</missing>
    </file>
  </activation>
</profile>

其中activeByDefault表示是否爲默認激活,默認值爲false。若是配置爲true, 當沒有配置文件被激活時,激活該配置文件

其餘的標籤是一些具體激活條件,在maven3.2.2版本只要知足一項激活條件就成立(或關係), 在maven3.2.2之後必須知足全部項激活條件才成立(與關係): - jdk: 指定構建時的jdk版本,除了指定具體版本外,也能夠指定範圍 - os: 操做系統信息,具體約束條件內容可查看這裏 - property: 將該屬性的key-value知足條件時該項成立。這裏的屬性的範圍與pom文件一致。 - file: 文件存在或缺失,配置路徑須爲絕對路徑。該項須要注意的是,file標籤下的屬性替換, 只容許出現basedir、系統變量、maven命令傳遞變量這三種。

在咱們配置好profiles,若是想知道在當前環境下哪一個配置將被激活, 能夠經過mvn help:active-profiles查看。

4.3其餘標籤

modulesdependenciesdependencyManagement標籤咱們在前文中已經講過, profile下這些標籤的可配置內容與其在根標籤(project)下的一致, profile下的配置能夠覆蓋根標籤下的配置。

repositoriespluginRepositoriesreportingdistributionManagement 這些標籤在根標籤下也存在,只是咱們尚未聊到,與上面的這些標籤同樣, 這些標籤的可配置內容與其在根標籤下的一致,profile下的配置能夠覆蓋根標籤下的配置。

build標籤較爲特殊,其在profile下的可配置內容只包含前文中咱們提到的全部內容, 但在根標籤下的build標籤還有一些獨佔內容咱們沒有提到。

5.project標籤下的build標籤

project標籤下的build標籤下還有幾個標籤,這些標籤僅出如今project標籤下的build中, 沒法在profile中的內容配置。

這些標籤包括目錄結構標籤——sourceDirectorysourceDirectorytestSourceDirectoryoutputDirectorytestOutputDirectory,以及extensions標籤。

5.1 *Directory標籤

這些標籤訂義了項目的目錄結構,標籤的含義和其英文名字一致,標籤的值必須爲絕對路徑 (通常使用`${project.basedir}進行拼接)。

這樣須要注意的是除了上述的幾個標籤外,咱們還能夠找到一個scriptSourceDirectory標籤, 該標籤實際沒有用處,已經被官宣廢棄。

5.2 extensions標籤

extensions下能夠引入一些jar對maven能力進行擴展,好比引入wagon-ssh添加對ssh協議的支持, 與plugins中的插件不一樣,擴展程序不須要提供構建目標(gloas),只是提供能力基礎, 所以須要另行引入插件配合使用。

咱們舉一個利用ssh協議進行包自動部署的例子:

<build>
  <extensions>
    <groupId>org.apache.maven.wagon</groupId>
    <artifactId>wagon-ssh</artifactId>
    <version>2.10</version>
  </extensions>
  <plugins>
    <plugin>
      <groupId>org.codehaus.mojo</groupId>
      <artifactId>wagon-maven-plugin</artifactId>
      <version>1.0</version>
      <configuration>
        <serverId>devServerRoot</serverId>
        <formFile>target/demo.jar</formFile>
        <url>scp://root@x.x.x.x:/root/applications/demo</url>
        <commands>
          <command>/root/applications/demo/restart.sh</command>
        </commands>
        <desplayCommandOutputs>true</desplayCommandOutputs>
      </configuration>
    </plugin>
  </plugins>
</build>

其中serverId中的的devServerRootserver標籤中的idserver標籤下能夠定義服務器的帳號密碼信息,通常在settings.xml中配置,也能夠在pom中配置。

<server>
  <id>devServerRoot</id>
  <username>root</username>
  <password>password</password>
</server>

5.Reporting標籤

reporting下的內容與site生命週期相關,reporting標籤下能夠定義一些插件用於生成報告文檔, 好比javadoc。
build相似,reporting也提供了插件的配置能力,與build不一樣的是, reporting下的插件沒法被綁定到指定的maven階段(默認綁定site階段)

<reporting>
  <plugins>
    <excludeDefaults>false</excludeDefaults>
    <outputDirectory>${basedir}/target/site</outputDirectory>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-javadoc-plugin</artifactId>
      <version>3.1.1</version>
      <reportSets>
        <reportSet>
          <id>sunlink</id>
          <reports>
            <report>javadoc</report>
          </reports>
          <inherited>true</inherited>
          <configuration>
            <links>
              <link>http://java.sun.com/j2se/1.5.0/docs/a
              </link>
            </links>
          </configuration>
        </reportSet>
      </reportSets>
    </plugin>
  </plugins>
</reporting>
  • excludeDefaults: 是否禁用默認報告信息,默認爲false。這裏的默認報告信息指的是 maven-project-info-reports-plugin,該插件會生成項目信息以及左側的導航欄。
  • outputDirectory: 報告生成位置。與build下的outputDirectory不一樣,報告的默認生成位置爲 ${basedir}/site
  • plugins: 引入報告生成插件並對其進行配置。
6.1 plugins中的reportSet標籤

build下的plugin相比,reporting下的配置就相對較爲簡單,只有一個相似於executionsreportSets標籤——二者均定義了插件在某一maven階段的構建任務。

  • id: 構建任務的id,與execution相似,會在構建時打印出來
  • reports: 相似於executiongoals,但實際上插件會將其構建目標作出區分, 報告相關的目標只能夠出如今reportSet中(好比javadoc:javadoc),而不容許出如今execution
  • inherited: 是否容許子pom繼承
  • configuration: 與execution中的配置相似,對該次任務的插件進行配置

有一點須要注意的是,一樣的插件能夠同時出如今build下和reporting下, 雖然咱們能夠用pluginManagement統一管理版本, 但build下的相關配置(configuration)不會傳遞到reporting

相關文章
相關標籤/搜索