1. 程式人生 > >第七節:Maven中的聚合與繼承

第七節:Maven中的聚合與繼承

     Maven的聚合特性能夠把專案的各個模組聚合在一起構建,而Maven的繼承特性則能幫助抽取各模組間相同的依賴和外掛配置,還能促進各個模組之間配置的一致性。 聚合:      我們在開發過程中,將專案拆分成獨立的子模組,每個模組都是一個獨立的maven project,在開始的時候我們可以獨立的編譯和測試執行每個模組,但是我們期望能夠使用簡單的操作來完成所有專案的編譯等工作,這時Maven給出了聚合的配置方式。我們可以建立一個專門負責聚合工作的Maven project — aggregator,然後通過該模組構建整個專案的所有模組。      該聚合專案特徵以及約定:
  • 該aggregator本身也做為一個Maven專案,它必須有自己的POM
  • 約定:為了方便構建,通常將聚合模組放在專案目錄層的最頂層,其它子模組作為其子目錄存在。這樣當我們開啟專案的時候,第一個看到的就是聚合模組的POM。
  • 聚合模組的版本和被聚合模組版本需一致
  • 它的打包方式必須為:pom,即<packaging>pom</packaging>
  • 引入了新的元素:<modules>及其下的<module>,使用者可以在一個打包方式為POM的Maven專案下宣告任意數量的module元素來實現模組的聚合。我們宣告<module>child-project-A</module>,那麼聚合專案的POM就會預設去./child-project-A
    的目錄下去尋找子模組,所以在module中宣告的子模組應與聚合專案的POM處於同一目錄下。如果我們不遵循約定,將子模組放於跟聚合模組同一個目錄下,則modules的宣告需要改成<module>../child-project-A</module>.
  • 聚合模組的內容僅僅是一個pom.xml檔案,因為它只是用來幫助其它模組構建的工具,本身並沒有實質的內容,所以它不包含src/main/java、src/test/java等目錄.
      繼承: 我們在專案開發的過程中,可能多個模組獨立開發,但是多個模組可能依賴相同的元素或者需要引入相同的外掛,比如說每個模組都需要Junit,使用spring的時候,其核心jar也必須都被引入,在編譯的時候,maven-compiler-plugin外掛也要被引入,這在每一個子模組裡都是需要配置的,但是通過POM繼承,我們可以抽取出重複的配置,精簡程式碼。      我們可以建立一個專門負責繼承工作的父模組 — parent,他有以下特徵:
  • 該parent本身也做為一個Maven專案,它必須有自己的POM
  • 由於它只是為了消除配置的重複,所以繼承模組的內容僅僅是一個pom.xml檔案,本身並沒有實質的內容
  • 它的打包方式必須為:pom,即<packaging>pom</packaging>
     繼承他的子模組有以下特徵:
  • 在繼承他的子模組的pom檔案中,我們用<parent>元素來宣告父模組,<parent>元素下的子元素groupId,artifactId,version指定了父模組的座標,<relativePath>元素指定了父模組pom檔案的路徑(相對於該子模組的pom),
          其預設值為../,相當於去上一層目錄找父pom。但是有時候會有問題,例如我們只從倉庫中遷出了一個子模組,此時           <relativePath>元素多半就失效了,子模組只能根據父模組的座標再去倉庫中尋找。
  • 子模組必須顯示宣告artifactId,他隱式得從父模組繼承了groupId和version兩個元素,只要當子模組與父模組的這兩個元素不一致時,才需要顯式宣告他們。

可繼承的POM元素:
  • groupId:專案組ID,專案座標的核心元素
  • version:專案版本, 專案座標的核心元素
  • dependencies:專案的依賴配置
  • repositories:專案的倉庫配置
  • distributionManagement:專案的部署配置
  • dependencyManagement:專案的依賴管理配置
  • pluginManagement:專案的外掛管理配置
  • build:包括專案的原始碼目錄配置、輸出目錄配置、外掛配置、外掛管理配置等
  • properties:自定義的maven屬性
  • description: 專案的描述資訊
  • organization: 專案的組織資訊
  • inceptionYear: 專案的創始年份
  • url: 專案的URL地址
  • developers: 專案開發者資訊
  • contributors: 專案的貢獻者資訊
  • issueManagement: 專案的缺陷跟蹤系統資訊
  • ciManagement: 專案的持續整合系統資訊
  • scm: 專案的版本控制系統資訊
  • mailingLists: 專案的郵件列表資訊
  • reporting: 包括專案的報告輸出目錄配置、報告外掛配置等
依賴管理: 由上我們可知dependencies是可以被繼承的,我們就想到把幾個子模組共同依賴的元素A轉移到parent中,這樣我們又進一步的優化了配置。可是問題也隨之而來,如果有一天又建立了一個繼承parent的子模組,但是這個模組不需要依賴元素A,n那這時候如何處理?      在父模組的POM中dependencyManagement元素幫我們解決了這個問題。我們在dependencyManagement元素配置 依賴(同樣是使用dependencies,dependency元素),但是這裡(父模組的POM中)宣告的依賴不會給父模組引入依賴也不會給子模組引入依賴,這段配置會被繼承,子模組只有在POM中顯式得宣告依賴的groupId與artifactId,才能得到我們從父模組中繼承而來的完整的依賴配置(例如包含了版本號,依賴範圍等),如果不宣告,即使依賴已經在父模組的dependencyManagement中被聲明瞭,但是也不會有什麼實際的效果。             通過這樣的配置,我們可以統一專案範圍中依賴的版本,降低各個子模組間由於依賴版本不一樣可能引起的衝突。      更進一步,如果我們有多個父模組,那又該怎麼抽取出各個父模組POM檔案中重複的配置呢?我們只要在各個父模組POM檔案中引入這些配置就好了。這裡使用了一個新的依賴範圍:import,並且設定這個依賴的type為pom,程式碼如下:
外掛管理:      類似於管理依賴的dependencyManagement,Maven提供了pluginManagement來管理外掛,他的機制和dependencyManagement一樣,當子模組需要不同的外掛配置,可以自行配置以覆蓋父模組的pluginManagement配置。
<project>
    ...
    <build>
        <pluginManagement>
            <plugins>
                <plugin>
                    <artifactId>maven-assembly-plugin</artifactId>
                    <version>2.2-beta-2</version>
                    <executions>
                        <execution>
                            <id>create-project-bundle</id>
                            <phase>package</phase>
                            <goals>
                                <goal>single</goal>
                            </goals>
                            <configuration>
                                <descriptorRefs>
                                    <descriptorRef>project</descriptorRef>
                                </descriptorRefs>
                            </configuration>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </pluginManagement>
	</build> 
	...
</project>
 
<build>
    <plugins>
        <plugin>
            <artifactId>maven-assembly-plugin</artifactId>
        </plugin>
    </plugins>
</build>

繼承和聚合總結:      對於聚合模組來說,它知道有哪些被聚合的模組,而對於被聚合的模組來說,它們不知道被誰聚合了,也不知道它的存在      對於繼承關係的父POM來說,它不知道自己被哪些子模組繼承了,對於子POM來說,它必須知道自己的父POM是誰      在一些實踐中我們會發現:一個POM既是聚合POM,又是父POM,這麼做主要是為了方便。 約定優於配置: 這是Maven最核心的設計理念之一,他大大簡化了我們需要手動配置的內容,我們舉個例子:Maven中約定了原始碼目錄為src/main/java/,編譯輸出目錄為target/classes/,有了這個約定,我們就不再需要手動建立這些目錄,Maven會自動建立他們,在執行像mvn clean install這樣的命令時,只要按照約定,我們不需要額外配置什麼,這條命令就可以用來構建幾乎任何的Maven專案。 反應堆:      反應堆是指所有模組組成的一個構建結構,包含了各個模組之間的依賴和繼承關係,能夠計算出合理的模組構建順序。      構建順序:按照聚合專案中<modules>中宣告<module>元素的順序來讀取各個子模組的POM檔案,如果POM檔案沒有依賴模組(父模組),那麼就構建該模組,否則就先構建其依賴模組,如果該依賴還依賴於其他模組,則進一步先構建依賴的依賴模組。      當我們想僅僅構建完整反應堆中的某些模組時,我們需要呼叫以下命令:
  • -am(also-make):同時還會構建所列專案的依賴模組(父模組)
  • -amd(also-make-dependents):同時還會構建所列模組的子模組
  • -pl(-project)<arg..> : 構建指定模組,模組間用逗號分隔
  • -rf(resume-from)<arg> :在完整的反應堆構建順序基礎上指定從哪個模組開始構建
    假設有child-A,child-B依賴於parent,完整的反應堆構建順序為:parent ---> child-A —> child-B      mvn clean install -pl child-A -am:此時會先構建parent,在構建child-A      mvn clean install -pl child-A,child-B:此時會構建child-A和child-B,但是不會構建parent      mvn clean install -pl parent -amd:此時會先構建parent,然後再構建child-A和child-B      mvn clean install -pl child-A -amd:此時會從完整的反應堆構建順序的child-A開始到child-B