1. 程式人生 > >Maven:約定優於配置。

Maven:約定優於配置。

標準的重要性已不用過多強調,想象一下,如果不是所有程式設計師都基於HTTP協議開發Web應用,網際網路會亂成怎樣。各個版本的IE、Firefox等瀏覽器之間的差別已經讓很多開發者頭痛不已。而Java成功的重要原因之一就是他能遮蔽大部分作業系統的差異,XML流行的原因之一是所有語言都接受它。Maven當然還不能和這些即成功又成熟的技術相比,但Maven的使用者都應該清楚,Maven提倡“約定優於配置”,這是Maven最核心的核心理念之一。

那麼為什麼要使用約定而不是自己更靈活的配置呢?原因之一是,使用約定可以大量減少配置。先看一個簡單地Ant配置檔案,如下所示。

<project name="my-project" default="dist" basedir=".">
    <description>
        simple example build file
    </description>
    <!- 設定構建的全域性屬性 -->
    <property name="src" location="src/main/java" />
    <property name="build" location="target/classes" />
    <property name="dist" location="target"/>
    
    <target name="init">
        <!- 建立時間戳 -->
        <tstamp />
        <!- 建立編譯使用的構建目錄 -->
        <mkdir dir="${build}" />
    </target>
    
    <target name="compile" depends="init" description="compile the source">
        <!- 將java程式碼從目錄${src}編譯至${build} -->
        <javac srcdir="${src}" destdir="${build}" />
    </target>
    
    <target name="dist" depends="compile" description="generate the distribution">
        <!-建立分發目錄-->
        <mkdir dir="${dist}/lib" />
        <!- 將${build}目錄的所有內容打包至MyProject-${DSTAMP}.jar file-->
        <jar jarfile="${dist}/lib/MyProject-${DSTAMP}.jar" basedir="${build}"/>
    </target>
    
    <target name="clean" description="clean up">
        <!- 刪除${build}和${dist}目錄樹 -->
        <delete dir="${build}" />
        <delete dir="${dist}"/>
    </target>
</project>

這段程式碼做的事情就是清除構建目錄、建立目錄、編譯程式碼、複製依賴至目標目錄,最後打包。這時一個專案構建要完成的最基本的事情,不過為此還是需要寫很多的XML配置:原始碼目錄是什麼、編譯目標目錄是什麼、分發目錄是什麼,等等。使用者還需要記住各種Ant任務命令,如delete、mkdir、javac和jar。

做同樣的事情,Maven需要什麼配置呢?Maven只需要一個最簡單的POM,見下面。

<project>
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.test</groupId>
    <artifactId>my-project</artifactId>
    <version>1.0</version>
</project>

這段配置簡單的令人驚奇,但為了獲得這樣簡潔的配置,使用者是需要付出一定的代價的,那就是遵循Maven的約定。Maven會假設使用者的專案是這樣的:

  • 原始碼目錄為src/main/java
  • 編譯輸出目錄為target/classes/
  • 打包方式為jar
  • 包輸出目錄為target/

遵循約定雖然損失了一定的靈活性,使用者不能隨意安排目錄結構,但是卻能減少配置。更重要的是,遵循約定能夠幫助使用者遵守構建標準。

如果沒有約定,10個專案可能使用10種不同的專案目錄結構,這意味著交流學習成本的增加,當新成員加入專案的時候,他就不得不花時間去學習這種構建配置。而有了Maven的約定,大家都知道什麼目錄放什麼內容。此外,與Ant的自定義目標名稱不同,Maven在命令列暴露的使用者介面是統一的,像mvn clean install 這樣的命令可以用來構建幾乎任何的Maven專案。 也許這時候會問,如果我不想遵守約定該怎麼辦?這時,請首先問自己三遍,你真的需要這麼做嗎?如果僅僅是因為喜好,就不要耍個性,個性往往意味著犧牲通用性,意味著增加無謂的複雜度,例如,Maven允許你自定義原始碼目錄,如下所示。

<project>
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.test</groupId>
    <artifactId>my-project</artifactId>
    <version>1.0</version>
    <build>
        <sourceDirectory>src/java</sourceDirectory>
    </build>
</project>

該例中原始碼目錄就成了src/java而不是預設的src/main/java。但這往往會造成交流問題,習慣Maven的人會奇怪,原始碼去哪裡了?當這種自定義大量存在的時候,交流成本就會大大提高。只有在一些特殊的情況下,這種自定義配置的方式才應該被正確使用以解決實際問題。例如你在處理遺留程式碼,並且沒有辦法更改原來的目錄結構,這個時候就只能讓Maven妥協。

任何一個Maven專案都隱式的繼承自超級POM,這有點類似於任何一個Java類都隱式的繼承於Object類。因此,大量超級POM的配置都會被所有Maven專案繼承,這些配置也就成為了Maven所提倡的約定。

對於Maven 3,超級POM在檔案 $ MAVEN_HOME/lib/maven-model-builder-x.x.x.jar中的org/apache/maven/model/pom-4.0.0.xml路徑下。對於Maven2,超級POM在檔案$ MAVEN_HOME/lib/maven-x.x.x-uber.jar中的org/apache/maven/project/pom-4.0.0.xml目錄下。這裡的x.x.x表示Maven的具體版本。

Maven設定核心外掛的原因是防止由於外掛版本的變化而造成構建不穩定。

超級POM實際上很簡單,但從POM我們就能知曉Maven約定的由來,不僅理解了什麼是約定,為什麼要遵循約定,還能明白約定是如何實現的。