現代軟件的現狀java
1、在這個技術飛速發展的時代,各種用戶對軟件的要求愈來愈高,軟件自己也變得愈來愈複雜程序員
2、所以,軟件設計人員每每會採用各類方式對軟件劃分模塊,以獲得更清晰的設計及更高的重用性spring
3、當把Maven應用到實際項目中的時候,也須要將項目分紅不一樣的模塊apache
舉個例子maven
好比,帳戶註冊服務就能夠劃分紅account-email,account-persist等5個模塊工具
Maven的聚合和繼承特性性能
Maven的聚合特性可以把項目的各個模塊聚合在一塊兒構建測試
Maven的繼承特性則能幫助抽象各個模塊相同的依賴和插件等配置,在簡化POM的同時,還能促進各個模塊配置的一致性,本章將結合實際的案例闡述Maven的這兩個特性ui
account-persist模塊的做用?編碼
該模塊負責帳戶數據的持久化,以XML文件的形式保存帳戶數據,並支持帳戶的建立、讀取、更新、刪除等操做
maven-resources-plugin插件
使用UTF-8編碼處理資源文件
爲何Maven要有聚合的功能?
好比,你一個項目有兩個模塊,若是沒有聚合的話,你須要在兩個模塊的目錄下分別執行mvn命令,若是有十個模塊,那會瘋掉
有了聚合就能夠只執行一次mvn命令,每一個模塊會自動執行
如何配置聚合?
1、首先咱們有兩個模塊分別是account-email和account-persist,要把它們兩個實現聚合
2、建立一個account-aggregator模塊,經過該模塊構建整個項目的全部模塊
account-aggregator模塊配置以下
<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/maven/-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.juvenxu.mvnbook.account</groupId>
<artifactId>account-aggregator</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Account Aggregator</name>
<modules>
<module>account-email</module>
<module>account-persist</module>
</modules>
</project>
對以上配置進行解釋:
1、有共同的groupId——com.juvenxu.mvnbook.account
2、artifactId爲獨立的——account-aggregator
3、版本號與其餘的兩個模塊一致
4、packaging其值被設置成POM,而其餘兩個模塊都用了默認值jar
5、對於聚合模塊來講,其打包方式packaging的值必須爲pom,不然就沒法構建
6、modules元素是實現模塊的聚合最核心的配置,module裏面配置是當前目錄的相對路徑
通常模塊聚合的通用約定
1、爲了方便用戶構建項目,一般將聚合模塊放在項目目錄的最頂層,其餘模塊則做爲聚合模塊的子目錄存在
2、這樣當用戶獲得源碼的時候,第一眼發現的就是聚合模塊的pom不用從多個模塊中去尋找聚合模塊來構建整個項目
3、聚合模塊只有一個pom.xml文件,沒有src/main/java、src/test/java等目錄,這也很容易理解,聚合模塊僅僅是幫助聚合其餘模塊構建的工具,它自己並沒有實質的內容
注意:聚合模塊與其餘目錄結構並不是必定要是父子關係,平行目錄結構也是能夠的
Maven的繼承機制
1、大量的前人經驗告訴咱們,重複每每就意味着更多的勞動和更多潛在的問題
2、在面向對象世界中,程序員可使用類繼承在必定程度上消除重複
3、在Maven的世界中,也有相似的機制能讓咱們抽出重複的配置,這就是POM的繼承
如何配置Maven的繼承?
1、好比在account-aggregator聚合模塊下,建立一個名爲account-parent的子目錄,而後再該子目錄下創建一個全部除account-aggregator以外的模塊的父模塊
2、在該子目錄下建立一個pom.xml文件
account-parent的POM配置以下
<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/maven/-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.juvenxu.mvnbook.account</groupId>
<artifactId>account-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Account Parent</name>
</project>
對上面配置進行說明:
1、groupId和version和其餘模塊一致
2、artifactId爲——account-parent
3、packaging爲pom,這一點與聚合模塊同樣,做爲父模塊的POM,其打包類型也必須爲pom
4、父模塊只有pom.xml,沒有src/main/java之類的文件夾
有了父模塊那麼子模塊如何配置?
修改account-email繼承account-parent
<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/maven/-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.juvenxu.mvnbook.account</groupId>
<artifactId>account-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../account-parent/pom.xml</relativePath>
</parent>
<artifactId>account-email</artifactId>
<name>Account Email</name>
<dependencies>
...
</dependencies>
<build>
<plugins>
...
</plugins>
</build>
</project>
對上述配置進行解釋:
1、上述POM中使用parent元素聲明父模塊,parent下的子元素groupId、artifactId和version指定了父模塊的座標,這三個元素是必須的
2、元素relativePath表示父模塊POM的相對路徑,../account-parent/pom.xml
3、當項目構建時,Maven會首先根據relativePath檢查父POM,若是找不到,再從本地倉庫查找,relative的默認值是../pom.xml,也就是說,Maven默認父POM在上一層目錄
4、這個POM沒有爲account-email聲明groupId和version,由於其隱式地從父模塊繼承了這兩個元素,這也就消除了一些沒必要要的配置,可是對於artifactId子模塊須要顯式聲明的
5、account-persist模塊的配置相似
6、最後還須要把account-parent加入到聚合模塊account-aggregator中,好比修改爲下面這樣的配置:
<modules>
<module>account-parent</module>
<module>account-email</module>
<module>account-persist</module>
</modules>
還有哪些POM元素能夠被繼承呢?
是的,除了groupId version等能夠繼承,還有如下的一些元素能夠被繼承
1、groupId:項目組ID,項目座標的核心元素
2、version:項目版本,項目座標的核心元素
3、description:項目的描述信息
4、organization:項目的組織信息
5、inceptionYear:項目的創始年份
6、url:項目的URL地址
7、developers:項目的開發者信息
8、contributors:項目的貢獻者信息
9、distributionManagement:項目的部署配置
10、issueManagement:項目的缺陷跟蹤系統信息
11、ciManagement:項目的持續集成系統信息
12、scm:項目的版本控制系統信息
13、mailingLists:項目的郵件列表信息
14、properties:自定義的Maven屬性
15、dependencies:項目的依賴配置
16、dependencyManagement:項目的依賴管理配置
17、repositories:項目的倉庫配置
18、build:包括項目的源碼目錄配置、輸出目錄配置、插件配置、插件管理配置等
19、reporting:包括項目的報告輸出目錄配置、報告插件配置等
如何控制依賴的繼承?
1、Maven提供的dependenyManagement元素既能讓子模塊繼承到父模塊的依賴配置,又能保證子模塊依賴使用的靈活性
2、在denpendenyManagement元素下的依賴聲明不會引入實際的依賴,不過它可以約束dependencies下的依賴使用
在account-parent中配置depencyManagement元素
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.juvenxu.mvnbook.account</groupId>
<artifactId>account-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Account Parent</name>
<properties>
<springframework.version>2.5.6</springframework.version>
<junit.version>4.7</junit.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
這裏使用dependencyManagement聲明的依賴既不會給account-parent引入依賴,也不會給它的子模塊引入依賴,不過這段配置是會被繼承的
繼承了dependencyManagement的account-email POM
<properties>
<javax.mail.version>1.4.1</javax.mail.version>
<greenmail.version>1.3.1b</greenmail.version>
</properties>
<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>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<version>${javax.mail.version}</version>
</dependency>
<dependency>
<groupId>com.icegreen</groupId>
<artifactId>greenmail</artifactId>
<version>${greenmail.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
對上述配置進行解釋:
1、全部的springframework依賴只配置了groupId和artifactId,省去了version
2、junit依賴不只省去了version,還省去了依賴範圍scope
配置繼承的好處?
1、使用這種依賴管理機制彷佛不能減小太多的POM配置
2、不過仍是強烈推薦採用這種方法,其主要緣由在於在父POM中使用dependencyManagement聲明依賴可以統一項目範圍中的依賴的版本
3、當依賴版本在父POM中聲明以後,子模塊在使用依賴的時候就無需聲明版本,也就不會發生多個子模塊使用依賴版本不一致的狀況
4、若是子模塊不聲明依賴的使用,即便該依賴已經在父POM的dependencyManagement中聲明瞭,也不會產生任何實際的效果,也就是說在子pom不聲明依賴,那麼該依賴就不會被引入。這正是dependencyManagement的靈活性所在
關於import的依賴範圍
1、import的依賴範圍只在dependencyManagement元素下才有效果
2、使用該範圍的依賴一般指向一個POM,做用是將目標POM中的dependencyManagement配置導入併合併到當前POM的dependencyManagement元素中
3、也就是說,除了複製配置或者繼承兩種方式以外,還可使用import範圍依賴將這一配置導入(dependencyManagement的配置)
使用import範圍依賴導入依賴管理配置
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.juvenxu.mvnbook.account</groupId>
<artifactId>account-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
注意,上述代碼中依賴的type值爲pom,import範圍依賴因爲其特殊性,通常都是指向打包類型爲pom的模塊。若是有多個項目,它們使用的依賴版本都是一致的,則就能夠定義一個使用dependencyManagement專門管理依賴的POM,而後再各個項目中導入這些依賴管理配置
插件管理
1、Maven提供了dependencyManagement元素幫助管理依賴,相似地,Maven也提供了pluginManagement元素幫助管理插件
2、在該元素中配置的依賴不會形成實際的插件調用行爲,當POM中配置了真正的plugin元素,而且其groupId和artifactId與pluginManagement中配置的插件匹配時,pluginManagement的配置纔會影響實際的插件行爲
在父POM中配置pluginManagement
<build>
<pluginManagement>
<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>
</pluginManagement>
</build>
當子模塊須要生成源碼包時,只須要以下簡單的配置
繼承了pluginManagement後的插件配置
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
</plugin>
</plugins>
</build>
1、若是子模塊不須要使用父模塊的pluginManagement配置的插件,能夠儘管將其忽略
2、若是子模塊須要不一樣的插件配置,自行覆蓋,妥妥的
聚合與繼承的關係
1、多模塊的Maven項目中的聚合與繼承實際上是兩個概念,其目的也是徹底不一樣的
2、前者主要是爲了方便快速構建項目,後者主要是爲了消除重複配置
3、對於聚合模塊來講,它知道有哪些被聚合的模塊,但哪些聚合的模塊不知道這個聚合模塊的存在
4、對於繼承關係的父POM來講,它不知道哪些子模塊繼承了它,但哪些子模塊都必須知道本身的父POM是什麼
5、若是非要說這兩個特性的共同點,那麼,聚合POM與繼承關係中的父POM的packaging都必須是pom,同時,聚合模塊與繼承關係中的父模塊除了POM以外都沒有實際的內容
6、能夠將聚合模塊和集成模塊合併
約定大於配置的優點?
1、全部程序員都基於HTTP協議開發Web應用,否則互聯網會亂成什麼樣子
2、Java成功的重要緣由之一是它能屏蔽大部分操做系統的差別
3、XML流行的緣由之一是全部語言都接受它
爲何使用約定而不是本身更靈活的配置呢?
使用約定能夠大量減小配置
構建簡單項目使用的Maven配置文件
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.juvenxu.mvnbook</groupId>
<artifactId>my-project</artifactId>
<version>1.0</version>
</project>
這段配置很是的簡潔,但用戶須要付出必定的代價,那就是遵循Maven的約定,Maven會假設用戶的項目是這樣的:
1、源碼目錄爲src/main/java/
2、編譯輸出目錄爲target/classes/
3、打包方式爲jar
4、包輸出目錄爲target/
遵循約定雖然損失了必定的靈活性,用戶不能隨意安排目錄一結構,可是卻能減小配置更重要的是,遵循約定可以幫助用戶遵照構建標準
若是我不想遵照約定該怎麼辦?
首先先問本身三遍,你真的須要這麼作嗎?
若是僅僅是由於喜愛,就不要耍個性,個性每每意味着犧牲通用性,意味着增長無謂的複雜度
使用Maven自定義源碼目錄
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.juvenxu.mvnbook</groupId>
<artifactId>my-project</artifactId>
<version>1.0</version>
<sourceDirectory>src/java</sourceDirectory>
</project>
對上面的配置進行解釋:
1、這個例子中將源碼目錄改爲了src/java而不是默認的src/main/java
2、這每每會形成交流問題,習慣Maven的人會奇怪,源代碼跑那裏去了
3、當這種自定義大量存在的時候,交流成本就會大大提升
4、只有在一些特殊的狀況下,這種自定義配置的方式才應該被正確使用以解決實際問題
5、例如你在處理遺留代碼,而且沒有辦法更改原來的目錄結構,這個時候就之只能讓Maven妥協
關於超級POM
1、任何一個Maven項目都隱式地繼承自該POM
2、大量超級POM的配置都會被全部Maven項目繼承,也是Maven所提倡的
3、在Maven 3中,超級POM在文件MAVEN_HOME/lib/maven-model-builder-x.x.x.jar中的org/apache/maven/model/pom-4.0.0.xml路徑下
4、在Maven 2中,超級POM在文件MAVEN_HOME/lib/maven-x.x.x-uber.jar中的org/apache/maven/model/pom-4.0.0.xml目錄下,這裏的x.x.x表示Maven的具體版本
超級POM中關於倉庫的定義
<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>
<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>
超級POM中關於項目結構的定義
<build>
<directory>${project.basedir}/target</directory>
<outputDirectory>${project.build.directory}/classes</outputDirectory>
<finalName>${project.artifactId}-${project.version}</finalName>
<testOutputDirectory>${project.build.directory}</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>
</build>
對上面代碼進行解釋:
1、這裏依次定義了,項目的主輸出目錄、主代碼輸出目錄、最終構件的名稱格式、測試代碼輸出目錄、主源碼目錄、腳本源碼目錄、測試源碼目錄、主源碼目錄和測試資源目錄
2、這就是Maven項目結構的約定
超級POM中關於插件版本的定義
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.3</version>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.2-beta-4</version>
</plugin>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>2.3</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.0.2</version>
</plugin>
...
</plugins>
</pluginManagement>
什麼是反應堆?
1、在一個多模塊的Maven項目中,反應堆(Reactor)是指全部模塊組成的一個構建結構
2、對於單模塊的項目,反應堆就是該模塊自己,但對於多模塊項目來講,反應堆就包含了各模塊之間繼承與依賴的關係,從而可以自動計算出合理的模塊構建順序
反應堆的構建順序
好比account-aggregator的聚合配置以下:
<modules>
<module>account-email</module>
<module>account-persist</module>
<module>account-parent</module>
</modules>
而後開始構建account-aggregator會看到以下的輸出
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO]
[INFO] Account Aggregator
[INFO] Account Parent
[INFO] Account Email
[INFO] Account Persist
[INFO] ------------------------------------------------------------------------
上述輸出依次爲account-aggregator、account-parent、account-email和account-persist
咱們看到輸出順序不是按modules元素中定義的順序
那麼,實際的構建順序是按什麼規則呢?
實際的構建順序是這樣造成的:Maven按序讀取POM,若是該POM沒有依賴模塊,那麼就構建該模塊,不然就先構建其依賴模塊,若是該依賴還依賴於其餘模塊,則進一步先構建依賴的依賴
有向非循環圖 (Directed Acyclic Graph, DAG)
模塊間的依賴關係會將反應堆構成一個有向非循環圖 (Directed Acyclic Graph, DAG)
各個模塊是該圖的節點,依賴關係構成了有向邊。這個圖不容許出現循環,所以,當出現模塊A依賴於B,而B又依賴於A的狀況時,Maven就會報錯
什麼是裁剪反應堆?
通常來講,用戶會選擇構建整個項目或者選擇構建單個模塊,但有些時候,用戶會想要僅僅構建完整反應堆中的某些個模塊,換句話說,用戶須要實時地裁剪反應堆。
經過命令行選項支持裁剪反應堆
輸入mvn -h 能夠看到這些選項:
n -am,--also-make 同時構建所列模塊的依賴模塊
n -amd,-also-make-dependents 同時構建依賴於所列模塊的模塊
n -pl,--projects <arg> 構建指定的模塊,模塊間用逗號分隔
n -rf -resume-from <arg> 從指定的模塊回覆反應堆
使用-pl選項指定構建某幾個模塊
mvn clean install -pl account-email, account-persist
使用-am選項能夠同時構建所列模塊的依賴模塊
mvn clean install -pl account-email -am
構建account-parent和account-email
使用-amd選項能夠同時構建依賴於所列模塊的模塊
mvn clean install -pl account-parent -amd
構建account-parent和其依賴account-email、account-persist
使用-rf選項能夠在完整的反應堆構建順序基礎上指定從哪一個模塊開始構建
mvn clean install -rf account-email
只會看到account-email和account-persist的構建
在-pl -am 或者-pl -amd的基礎上,還能應用-rf參數,以對裁剪後的反應堆再次裁剪
mvn clean install -pl account-parent -amd -rf account-email