閱讀860 返回首頁    go 阿裏雲 go 技術社區[雲棲]


Maven最佳實踐——目錄的約定

Maven是服務於項目生命周期的,有些人說它是build工具,但build隻是生命周期的一部分,它試圖抽象整個項目生命周期,實際上它也做到了。幾乎所有的項目都離不開Mave所定義的生命周期階段(clean compile test package site...)。不止如此,基於這些階段,Maven通過插件提供了絕大部分的默認實現,它們不用做任何配置或者僅需要很少的配置,就能幫你完成你的工作。

 

先看一個簡單的基於Ant的build.xml文件。

<project name="my-project" default="dist" basedir=".">
    <description>
        simple example build file
    </description>
  <!-- set global properties for this build -->
  <property name="src" location="src/main/java"/>
  <property name="build" location="target/classes"/>
  <property name="dist"  location="target"/>

  <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 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>


 

這段代碼做的事情是清除目錄,創建目錄,編譯源文件,複製依賴至目標目錄。這已經十分簡單了,但是,為此你還是需要做一些配置:指定源碼目錄,指定編譯字節碼目錄,指定目標目錄,你還需要記住一些ant的配置命令,如delete, mkdir, javac, jar。這看起來沒什麼問題,至少目前是這樣。

 

現在說說Ant或shell/bat存在的問題,首先,有很多配置需要你填寫,源碼目錄,字節碼目錄……,每一個項目,你都要重複這些勞動;第二,也是更重要的一點,你根本無法保證N個項目的ant配置(或shell)配置使用了相同的風格,後果是,每接受一個項目,開發者都要去學習這份腳本,以了解如何構建項目。如果第一個原因隻是為了技術的簡化的話,第二個原因更是軟件工程的因素,我們不是一個人在開發項目,不是隻開發一個項目,所以應該時刻謹記為了別人,為了將來。

 

現在看看使用Maven我們需要什麼配置,就一個pom.xml文件:

<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>org.sonatype.mavenbook</groupId>
  <artifactId>my-project</artifactId>
  <version>1.0</version>
</project>


 

不用驚訝,Maven不會變魔術,它能做到這麼簡單,是有條件的,條件就是你要遵守Maven約定。pom.xml所在的目錄應為項目的根目錄,假設該目錄為${proj-dir},那麼Maven有以下假設:

  • ${proj-dir}/src/main/java —— 存放項目的.java文件。
  • ${proj-dir}/src/main/resources —— 存放項目資源文件,如spring, hibernate配置文件。
  • ${proj-dir}/src/test/jave —— 存放所有測試.java文件,如JUnit測試類。
  • ${proj-dir}/src/test/resources —— 測試資源文件。
  • ${proj-dir}/target —— 項目輸出位置。

運行一條mvn clean package命令,Maven會幫你清除target目錄,重新建一個空的,編譯src/main/java類至target/classes,複製src/main/resources的文件至target/classes,編譯src/test/java至target/test-classes,複製src/test/resources的文件至target/test-classes;然後運行所有測試;測試通過後,使用jar命令打包,存儲於target目錄。Maven做的事情一點也不少,隻是都對用戶隱蔽起來了,它隻要求你遵循它的約定。

 

這麼做有什麼好處呢?第一,顯而易見,配置大量減少了,隨著項目變得越複雜,這種優勢就越明顯。第二,我這裏要強調的是,對於軟件工程來說,所有使用Maven的項目,對外都暴露統一的命令集。如mvn clean install。隻要項目被正確配置好了,任何一個新人,輸入一行Maven命令,就能將項目構建起來了,這大大減少了交流學習的時間。

 

這時可能會有人說,我不想遵守這種約定呢?我要把源碼放在${proj-dir}/src/code目錄下。首先,問自己三遍,你真的需要這樣做麼?如果僅僅是因為喜好,那麼別耍個性,個性意味著犧牲通用性,意味著增加無謂的複雜度。以下是一個“個性”的例子:

<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>org.sonatype.mavenbook</groupId>
  <artifactId>my-project</artifactId>
  <version>1.0</version>
  <build>
    <sourceDirectory>src/java</sourceDirectory>
    <testSourceDirectory>src/test</testSourceDirectory>
    <outputDirectory>output/classes</outputDirectory>
    <testOutputDirectory>output/test-classes</testOutputDirectory>
    <directory>target/jar</directory>
  </build>
</project>


很顯然,Maven允許你自定義,比如這個例子中,Maven就被配置成從src/java目錄尋找源碼,編譯class文件至output/classes,從src/test尋找測試源碼,編譯至output/test-classes目錄,最後,打包jar文件至target/jar目錄。Maven允許你這麼做,但不推薦你這麼做。因為一旦你使用這種自定義,習慣Maven約定的人一開始會覺得奇怪,src/main/java目錄去哪裏了?target下麵怎麼沒有我要的jar文件?這些都造成了無謂的交流成本提高。隻有一些特殊的情況,這些自定義手段能幫你解決實際的問題,比如你在處理遺留代碼,你沒辦法改變源碼目錄結構,這個時候隻有讓Maven妥協。

 

下麵總結一些Maven的默認值,也就是說,雖然你沒做什麼配置,但是你應該知道Maven假設它們成立,也就是所謂的約定:

Maven約定

目錄src/main/java java源碼目錄 目錄src/main/resources 資源文件目錄 目錄src/test/java 測試java源碼目錄 目錄src/test/resources 測試資源文件目錄 目錄target 打包輸出目錄 目錄target/classes 編譯輸出目錄 目錄target/test-classes 測試編譯輸出目錄 目錄target/site 項目site輸出目錄 目錄src/main/webapp web應用文件目錄(當打包為war時),如WEB-INF/web.xml jar 默認打包格式 *Test.java Maven隻會自動運行符合該命名規則的測試類 %user_home%/.m2 Maven默認的本地倉庫目錄位置 中央倉庫 Maven默認使用遠程中央倉庫:https://repo1.maven.org/maven2 1.3 Maven Compiler插件默認以1.3編譯,因此需要額外配置支持1.5

 

其實基本上所有的約定,或者說默認配置,都可以在Maven的超級POM(super pom)中找到。由於所有的POM都繼承了這個超級POM(類似於java中所有類都繼承於Object),因此它的默認配置就被繼承了。以Maven 2.0.9為例,你可以在%m2_home%/lib/下看到一個名為maven-2.0.9-uber.jar的文件,打開這個文件,可以找到org/apache/maven/project/pom-4.0.0.xml這個文件,這就是超級POM。

 

Maven提供了一套科學的默認配置,它要求你遵循這些配置約定,然後它就會幫你處理日常的事務compile, test, package等等。使用Maven的時候,你應該盡可能遵循它的配置約定,一方麵可以簡化配置,另一方麵可建立起一種標準,減少交流學習成本。一旦你習慣了這種約定,你得到的回報是巨大的。反之,恣意的做自定義,想要Maven像Ant一樣聽你的話,那麼你會討厭Maven,Maven也會討厭你。

 

 

原帖地址:https://juvenshun.iteye.com/blog/293975

 

 

最後更新:2017-04-03 07:57:28

  上一篇:go 優秀項目經理的七個習慣
  下一篇:go IOS開發賬號申請