使用Maven創(chuàng)建應用介紹將要創(chuàng)建的應用名叫Proficio,拉丁語的"help"。 設置應用程序的目錄結構在設置Proficio的目錄結構時,注意Maven強調(diào)的實踐標準化和構建模塊化構建是很重要的。 這種實踐自然將產(chǎn)生分離的可重用的開發(fā)工程。決定如何最優(yōu)化的分解應用的原則叫做“分離關注點(Separation of Concerns)”原則,即SoC原則。 SoC有助于識別、封裝、操作于有相關特殊概念、目標、任務或目的的軟件片段。關注點是組織和分解軟件的動力,更多的易于管理和理解的部分,每個都用于說明一個或多個特定關注點。 如上所述,Proficio樣例工程將被設置為多個Maven模塊:
在Proficio的頂層POM中,可以看到所有子模塊元素。一個模塊指向另一個Maven工程,實際上它是指向另一個POM。Proficio的頂層POM文件如下: <project> <modelVersion>4.0.0</modelVersion> <groupId>com.devzuz.mvnbook.proficio</groupId> <artifactId>proficio</artifactId> <packaging>pom</packaging> <version>1.0-SNAPSHOT</version> <name>Maven Proficio</name> <url>http://maven.</url> [...] <modules> <module>proficio-model</module> <module>proficio-api</module> <module>proficio-core</module> <module>proficio-stores</module> <module>proficio-cli</module> </modules> [...] </project> 上面的版本號1.0-SNAPSHOT。對于一個有多模塊的應用,通常將所有模塊一起發(fā)布,所有模塊使用一個公共的版本號。這是推薦的一種方式。 注意上面的packaging元素,這里它被設置為pom。對于包含模塊的POM文件,packaging必須設置為pom:這告訴Maven你準備創(chuàng)建一個模塊集。 Proficio應用的模塊打包類型:
proficio-stores模塊有兩個子模塊。 使用工程繼承Maven最重要的功能之一就是工程繼承。使用工程繼承允許你在一個地方規(guī)定組織機構信息,規(guī)定部署信息,或規(guī)定通用的依賴。由Proficio工程產(chǎn)生的每個工程的POM文件,每個的頂部都有: [...] <parent> <groupId>com.devzuz.mvnbook.proficio</groupId> <artifactId>proficio</artifactId> <version>1.0-SNAPSHOT</version> </parent> [...] 這個片段允許你從指定的頂層的POM繼承。頂層POM中指定了依賴JUnit 3.8.1。在這種情況下子工程中不再申明這個依賴也可以使用這個包。 為了了解在繼承處理時發(fā)生的東西可以執(zhí)行mvn help:effective-pom命令。這個命令將顯示出最終的POM。在proficio的子工程中執(zhí)行這個命令時可以看到依賴中出現(xiàn)了JUnit 3.8.1。 管理依賴關系Maven可以讓不同的工程共享程序包。 可以在頂層的POM中描述所有子工程共享的依賴。 例如Proficio的頂層POM: <project>
[...]
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.devzuz.mvnbook.proficio</groupId>
<artifactId>proficio-model</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.devzuz.mvnbook.proficio</groupId>
<artifactId>proficio-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.devzuz.mvnbook.proficio</groupId>
<artifactId>proficio-store-memory</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.devzuz.mvnbook.proficio</groupId>
<artifactId>proficio-store-xstream</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.devzuz.mvnbook.proficio</groupId>
<artifactId>proficio-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-container-default</artifactId>
<version>1.0-alpha-9</version>
</dependency>
</dependencies>
</dependencyManagement>
[...]
</project>
注意${project.version}指定了版本,它與應用的版本對應。 在dependencyManagement一節(jié),有多個Proficio依賴并且還依賴于Plexus IoC container。dependencyManagment元素與頂層POM的dependencies有重要區(qū)別。 dependencyManagement元素中包括的dependencies元素僅用于說明引用的版本號,對并不影響工程的依賴關系圖,然而頂層的dependencies元素將影響依賴關系圖。查看proficio-api模塊的POM將只看到引用而沒有指定版本: <project> [...] <dependencies> <dependency> <groupId>com.devzuz.mvnbook.proficio</groupId> <artifactId>proficio-model</artifactId> </dependency> </dependencies> </project> 這個依賴的版本號是從Proficio的頂層POM文件的dependencyManagement繼承過來的。 dependencyManagement指定了引用proficio-model的版本號為1.0-SNAPSHOT(被設置為${project.version})這個版本號將注入到上面的依賴中。dependencyManagement中說明的dependencies 只用于當某個依賴沒有版本號的情況。 使用快照當開發(fā)的應用具有多個模塊時,通常每個模塊的版本都在變更。API可能正在經(jīng)歷變遷或你的實現(xiàn)正在發(fā)生改變,或者在進行重構。你在構建時需要非常容易的實時獲取最新版本,這是Maven的快照(snapshot)的概念。快照是Maven的一個artifact。查看 Proficio的頂層POM可以看到指定了快照版本。 <project>
[...]
<version>1.0-SNAPSHOT</version>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.devzuz.mvnbook.proficio</groupId>
<artifactId>proficio-model</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.devzuz.mvnbook.proficio</groupId>
<artifactId>proficio-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-container-default</artifactId>
<version>1.0-alpha-9</version>
</dependency>
</dependencies>
</dependencyManagement>
[...]
</project>
解決依賴沖突和使用版本號范圍在Maven 2.0中通過引入依賴傳遞,使得可以在簡單的POM文件中只指定你直接需要的依賴,并且Maven可以計算出完整的依賴關系圖。但是,隨著圖的增漲,不可避免的將產(chǎn)生一個或多個artifacts需要依賴的不同版本。這種情況下,Maven必須選擇使用哪個版本。 Maven通常選擇這個關系樹中頂層的“最接近的版本(nearest)”,Maven選擇版本號跨度最小的版本。如果在POM中指定了版本,則將被使用而不管其它的原因。但這種方式也有下面的限制:
手工解決沖突,可以從依賴樹中移除不正確的版本,或者可以用正確的版本來覆蓋掉樹中的版本。移除不正確的版本需要在運行Maven時指定-X標識來找出不正確的版本。例如,如果在proficio-core模塊運行mvn -X test將輸出: proficio-core:1.0-SNAPSHOT junit:3.8.1 (selected for test) plexus-container-default:1.0-alpha-9 (selected for compile) plexus-utils:1.0.4 (selected for compile) classworlds:1.1-alpha-2 (selected for compile) junit:3.8.1 (not setting scope to compile; local scope test wins) proficio-api:1.0-SNAPSHOT (selected for compile) proficio-model:1.0-SNAPSHOT (selected for compile) plexus-utils:1.1 (selected for compile) 這樣可以找出當前操作所使用的詳細版本信息,一旦找出了不正確的版本,你可以將它從依賴關系圖中移除。例如,這個例子中,plexus-utils出現(xiàn)了兩次,Proficio需要1.1版。為確保這個依賴,可以修改plexus-container-default的依賴,例如: <dependency> <groupId>org.codehaus.plexus</groupId> <artifactId>plexus-container-default</artifactId> <version>1.0-alpha-9</version> <exclusions> <exclusion> <groupId>org.codehaus.plexus</groupId> <artifactId>plexus-utils</artifactId> </exclusion> </exclusions> </dependency> 這保證了Maven將忽略1.04版的plexus-utils,而使用1.1版。 另一種方法確保在依賴中使用特定版本,是將它直接包含在POM中,例: <dependencies> <dependency> <groupId>org.codehaus.plexus</groupId> <artifactId>plexus-utils</artifactId> <version>1.1</version> <scope>runtime</scope> </dependency> </dependencies> 但是這種方式是不被推薦的除非你是在制作一個綁定了自己的依賴的artifact,并且它自身不會作為一個依賴(例如,是一個WAR文件)。原因是這種做法歪曲了真實的依賴關系圖,在工程自身作為依賴被重用時將導致問題。 在這里指定了runtime作用范圍。這是因為,在這種情況下,依賴只是用于打包而不是編譯。實際上,如果依賴是在編譯時需要,它應該總是出現(xiàn)在當前POM的依賴中——而不管另一個依賴是否使用了它。 上面的這些解決都只是理想化的,但它可以提高你自己的依賴的質(zhì)量,避免你在構建自己的產(chǎn)品時的風險。這一點在構建一個應用程序框架時是非常重要的,因為它將廣泛的被其它人使用。為達到這個目標,可以使用版本范圍來替代這種方式。 當上面的plexus-utils的版本被設置為1.1時,標明首選依賴的是1.1版,但其它版本可能也能夠接受。Maven并不知道哪個版本可以工作,因此當與其它依賴沖突時,Maven確保所有的版本使用前面描述的“最近依賴(nearest dependency)”技術來決定使用哪個版本。 但是,你可能需要一個plexus-utils 1.1版中的功能。這時,依賴應該指定為下面的形式: <dependency> <groupId>org.codehaus.plexus</groupId> <artifactId>plexus-utils</artifactId> <version>[1.1,)</version> </dependency> 這表示在版本沖突時仍將使用nearest dependency技術,但是版本號必須符合給定的范圍。如果版本不匹配,則下一個最接近的版本將被測試,如此繼續(xù)。最后,如果沒有匹配的版本,或本來就沒有沖突,則使用指定的版本[1.1,)。這表示將從倉庫中獲取最小的版本號大于或等于1.1的版本。 版本范圍范例表:
通過指定使用的版本范圍,使得構建時依賴管理機制更加可靠并且減少異常的情況。但應該避免過度的詳細。例如,如果兩個版本范圍依賴圖不交叉,那么構建將失敗。 為了解版本范圍是如何工作的,需要了解版本是怎樣進行比較的。下面展示了Maven是如何分割版本號的。 1.0.1-20060211.131141-1 從左至右依次為: 1為主版本號 0為次版本號 1為Bug修正號 20060211.131141為限定版本號 1為構建號 在目前的版本方案中,快照版本是一種特殊的情況,在這種情況下限定號和構建號可以同時存在。在正式版本中,可以只提供限定號或只提供構建號。有意設置的限定號標識出了一個較優(yōu)先的版本(例如:alpha-1,beta-1,rc1)。對于快照版本,限定號必須是文本“snapshot”或時間戳。構建號是一個自增號在發(fā)布時標明是補丁構建。 版本中的元素依次決定哪個版本較新——首先是主版本號,如果主版本號相等,則比較次版本號,接下來是Bug修正號,限定號,最后比較構建號。帶限定號的版本比不帶限定號的版本要舊;比如1.2-beta比1.2舊。包含了構建號的版本比不帶構建號的版本新;比如1.2-beta-1比1.2- beta新。某些情況下,版本可能會不匹配這個語法。在這些情況下,兩個版本號將作為字符串進行比較。 當使用快照版本測試編譯發(fā)布版本或自己測試發(fā)布測試版本時應該將它們部署到快照倉庫,這將在第七章討論。這保證了在版本范圍中的beta版本才會使用,除非工程顯式的申明了使用快照版本。 最后要注意的是當使用版本范圍時版本更新是如何被決定的。這個機制與前面介紹的快照版本更新機制是相同的,即每天從版本庫中更新一次版本。但是,這可以通過配置每個倉庫來設置更新的頻率,或在命令行使用-U參數(shù)強制Maven執(zhí)行更新。 例: <repository> [...] <releases> <updatePolicy>interval:60</updatePolicy> </releases> </repository> 利用構建生命周期第二章中將Maven描述為一個正確調(diào)整插件執(zhí)行方式或順序的應用框架,這實際上就是Maven的默認構建生命周期。 Maven默認的構建生命周期對于大多數(shù)工程來說不需要增加任何內(nèi)容就可以滿足了——當然,有時工程需要增加不同的內(nèi)容到Maven的默認構生命周期來滿足構建的需求。 例如,Proficio需要從model生成Java源碼。Maven通過允許申明插件來滿足這個需求,將它綁定到Maven默認生命周期的一個標準階段——generate-sources階段。 Maven的插件是為特定任務而創(chuàng)建的,這意味著插件將被綁定到默認的生命周期的一個特定階段。在Proficio中,Modello插件被用于生成Proficio的數(shù)據(jù)模型的Java源碼。查看proficio-model的POM可以看到plugins元素配置了Modello插件。 <project> <parent> <groupId>com.devzuz.mvnbook.proficio</groupId> <artifactId>proficio</artifactId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>proficio-model</artifactId> <packaging>jar</packaging> <name>Proficio Model</name> <build> <plugins> <plugin> <groupId>org.codehaus.modello</groupId> <artifactId>modello-maven-plugin</artifactId> <version>1.0-alpha-5</version> <executions> <execution> <goals> <goal>java</goal> </goals> </execution> </executions> <configuration> <version>1.0.0</version> <packageWithVersion>false</packageWithVersion> <model>src/main/mdo/proficio.mdo</model> </configuration> </plugin> </plugins> </build> </project> 這與第二章中maven-compiler-plugin的申明非常相似,但這里可以看到額外的execution元素。Maven中的插件可以有多個goal,因此你需要指定你希望運行插件的哪個goal,這可以通過在execution中的goal元素來指定。 使用ProfilesProfile是Maven提供的用于創(chuàng)建構建生命周期中的不同環(huán)境變量、不同的平臺、不同JVM、不同的測試數(shù)據(jù)庫、或引用不同的本地文件系統(tǒng)的方法。通常你可以在POM中封裝,以保證構建的可移植性,但有時你需要考慮變化的交叉系統(tǒng)的情況,這也是Maven中引入 profile的原因。 Profile使用POM中的一個子集元素來指定,可以用多種方式來啟用。Profile在構建時修改POM,意味著它將用于給不同的目標環(huán)境中的參數(shù)集(比如,在開發(fā)環(huán)境、測試環(huán)境、產(chǎn)品環(huán)境下應用服務器根路徑)不同參數(shù)值。 Profile也可以很容易的實現(xiàn)團隊中不同成員生成不同的構建結果。也可以通過profile阻止構建的移植性。Profile可以定義在下面三個地方:
優(yōu)先級依次為POM文件、profiles.xml、settings.xml。這也是Maven中的基本原則。 settings.xml中設置profile會影響所有的構建,因此它適合“全局”的profiles。profiles.xml允許設置單個工程的構建而不用修改POM?;赑OM的profiles是首選方式,因為這樣更具有移植性(它們將在布署時發(fā)布到倉庫,對于源于倉庫的子構建或依賴來說也同樣有效)。 因為移植性的原因,那些不會發(fā)布到倉庫中的文件不允許修改任何基礎構建。因此,profiles.xml和settings.xml只允許定義:
其它的信息必須在POM的profile中指定或在POM自身指定。例如,如果settings.xml中有一個profile它可以注入一個依賴,你的工程運行需要settings注入的依賴,一旦這個工程部部署到倉庫中它將不能解決它的依賴。因為其中一個依賴設置在settings.xml的 profile中了。 注意:respositories、pluginRepositories和properties也可以在POM內(nèi)部的profiles指定。因此,在POM外部指定的profiles只允許使用POM內(nèi)部指定的profiles選項的一個小的子集。 可以在POM中定義的profile:
構建元素的子集,由下面組成:
有多種方法啟用profiles:
mvn -Pprofile1,profile2 install
<settings> [...] <profiles> <profile> <id>profile1</id> [...] </profile> </profiles> <activeProfiles> <activeProfile>profile1</activeProfile> </activeProfiles> [...] </settings>
<profile> <id>profile1</id> [...] <activation> <jdk>1.4</jdk> </activation> </profile>
<profile> <id>profile1</id> [...] <activation> <property> <name>debug</name> </property> </activation> </profile>
<profile> <id>profile1</id> [...] <activation> <property> <name>environment</name> <value>test</value> </property> </activation> </profile>
在熟悉profiles后,可以使用它組裝不同的Proficio系統(tǒng):一個配置方案是memory-base存儲,另一個是XStream-based存儲。這些將在proficio-cli模塊中使用。proficio-cli模塊的profile定義如下: <project> [...] <!-- Profiles for the two assemblies to create for deployment --> <profiles> <!-- Profile which creates an assembly using the memory based store --> <profile> <id>memory</id> <build> <plugins> <plugin> <artifactId>maven-assembly-plugin</artifactId> <configuration> <descriptors> <descriptor>src/main/assembly/assembly-store-memory.xml</descriptor> </descriptors> </configuration> </plugin> </plugins> </build> <activation> <property> <name>memory</name> </property> </activation> </profile> <!-- Profile which creates an assembly using the xstream based store --> <profile> <id>xstream</id> <build> <plugins> <plugin> <artifactId>maven-assembly-plugin</artifactId> <configuration> <descriptors> <descriptor>src/main/assembly/assembly-store-xstream.xml</descriptor> </descriptors> </configuration> </plugin> </plugins> </build> <activation> <property> <name>xstream</name> </property> </activation> </profile> </profiles> </project> 可以看到兩個profiles:一個id為memory另一個id為xstream。在每個profile中你可以配置插件。也可以看到profile通過一個系統(tǒng)屬性進行激活。這個例子依賴于前面已經(jīng)執(zhí)行過的一些構建步驟,因此應該先在工程的頂級目錄執(zhí)行mvn install確保需要的組件被安裝到本地倉庫。 如果想基于memory-based存儲進行構建,可以執(zhí)行: mvn -Dmemory clean assembly:assembly 如果想基于XStream-based存儲進行,可以執(zhí)行: mvn -Dxstream clean assembly:assembly 這兩種方式構建的結果都保存在target目錄中,如果對輸出使用jar tvf命令,可以看到memory-base方式構建時只包含了proficio-store-memory-1.0-SNAPSHOT.jar,當使用 XStream-based方式時,只包含了proficio-store-xstream-1.0-SNAPSHOT.jar。 部署應用當前Maven支持多種部署方式包括文件系統(tǒng)部署、SSH2部署、SFTP部署、FTP部署和外部SSH部署。為了進行部署,需要正確的配置POM中的distributionManagement元素,通常是在頂級POM中,因為子POM可以繼承這些信息。 文件系統(tǒng)部署<project>
[...]
<distributionManagement>
<repository>
<id>proficio-repository</id>
<name>Proficio Repository</name>
<url>file://${basedir}/target/deploy</url>
</repository>
</distributionManagement>
[...]
</project>
SSH2部署<project> [...] <distributionManagement> <repository> <id>proficio-repository</id> <name>Proficio Repository</name> <url>scp://sshserver.yourcompany.com/deploy</url> </repository> </distributionManagement> [...] </project> SFTP部署<project> [...] <distributionManagement> <repository> <id>proficio-repository</id> <name>Proficio Repository</name> <url>sftp://ftpserver.yourcompany.com/deploy</url> </repository> </distributionManagement> [...] </project> 外部SSH部署前面三個部署方式是包含在Maven內(nèi)部的,因此只需要distributionMangement元素,但使用外部SSH命令部署則還要使用一個構建擴展。 <project> [...] <distributionManagement> <repository> <id>proficio-repository</id> <name>Proficio Repository</name> <url>scpexe://sshserver.yourcompany.com/deploy</url> </repository> </distributionManagement> <build> <extensions> <extension> <groupId>org.apache.maven.wagon</groupId> <artifactId>wagon-ssh-external</artifactId> <version>1.0-alpha-6</version> </extension> </extensions> </build> [...] </project> 這個構建擴展指定使用Wagon外部SSH提供都,它將你的文件移動到遠程服務器上。Wagon是Maven中通用的用于傳送的機制。 FTP部署FTP部署也必須指定一個構建擴展。 <project> [...] <distributionManagement> <repository> <id>proficio-repository</id> <name>Proficio Repository</name> <url>ftp://ftpserver.yourcompany.com/deploy</url> </repository> </distributionManagement> <build> <extensions> <extension> <groupId>org.apache.maven.wagon</groupId> <artifactId>wagon-ftp</artifactId> <version>1.0-alpha-6</version> </extension> </extensions> </build> [...] </project> 一旦配置完POM后,可以執(zhí)行 mvn deploy 進行部署。 為應用程序創(chuàng)建Web站點前面已經(jīng)完成了Proficio的構建、測試、部署,現(xiàn)在可以為這個應用創(chuàng)建一個標準的Web站點。對于Procio這樣的應,推薦在頂級目錄創(chuàng)建用于生成站點的資源目錄。 所有用于生成站點的文件保存在src/site目錄。src/site目錄中也有子目錄保存支持文檔。Maven支持大量同的文件格式。 當前支持得最好的格式:
Maven也有限的支持:
在后面的章節(jié)將了解支持較好的那些格式,但你應該熟悉下面的目標:
查看Proficio應用的src/site可以看到站點的描述: <project name="Proficio">
<bannerLeft>
<name>Proficio</name>
<href>http://maven./</href>
</bannerLeft>
<bannerRight>
<name>Proficio</name>
<src>http://maven./images/apache-maven project.png</src>
</bannerRight>
<skin>
<groupId>org.apache.maven.skins</groupId>
<artifactId>maven-default-skin</artifactId>
<version>1.0-SNAPSHOT</version>
</skin>
<publishDate format="dd MMM yyyy" />
<body>
<links>
<item name="Apache" href="http://www./"/>
<item name="Maven" href="http://maven./"/>
<item name="Continuum" href="http://maven./continuum"/>
</links>
<head><meta name="faq" content="proficio"/></head>
<menu name="Quick Links">
<item name="Features" href="/maven-features.html"/>
</menu>
<menu name="About Proficio">
<item name="What is Proficio?" href="/what-is-maven.html"/>
</menu>
${reports}
</body>
</project>
這是一個相當標準的Web站點描述,每個元素的說明如下:
Maven中最流行的功能之一就是花較少的功夫就可以生成標準的報表。只要簡單的在站點描述中包含${reports}引用,默認情況下是包含的,工程信息報表將自動生成的被添加上來。標準的工程信息報表包含下面的內(nèi)容:
盡管標準報表很有用,通常你需要自定義工程的報表。報表的創(chuàng)建和顯示控制是在POM的build/reports元素中。你可以選擇生成報表的信息,只要列舉出需要包含在站點中的報表即可。這個插件的配置方式如下: <project> [...] <reporting> [...] <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-project-info-reports-plugin</artifactId> <reportSets> <reportSet> <reports> <report>dependencies</report> <report>project-team</report> <report>mailing-list</report> <report>cim</report> <!-- Issue tracking report will be omitted <report>issue-tracking</report> --> <report>license</report> <report>scm</report> </reports> </reportSet> </reportSets> </plugin> </plugins> [...] </reporting> [...] </project> 執(zhí)行 mvn site 生成站點。 生成的站點放在target目錄下。如果有其它的資源,如圖片、PDF等可以存放在src/site/resources。當站點被生成時src/site/resources將被復制到站點的頂級目錄。 |
|
|