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