爲什麼?
在日常工作中,我經常需要解決許多簡單的或者是複雜的Maven/Java EE工程結構的問題。爲了找到解決辦法,我經常要拿項目的結構做實驗,在不同應用服務器上對部署進行測試並且調優。
對新手來說,Maven可能有一個很漫長的學習曲線。如果你參與進一個經常需要非常複雜的配置的現代化的JavaEE應用的時候,事情會變得更糟。在我的職業生涯中也曾見過:很多初級程序員,當他們參與到一個大的團隊或者項目,很多時候,工程的結構已經被一些更高級的程序員調優和配置好了。他們會認爲這是沒問題的,並且也不會花時間來理解這些配置和結構。過去我也犯過這樣的錯誤。他們被給予簡單的編碼任務,然後就一頭扎進需求裏面去了。很不幸,他們忽略了應用結構的學習。更高級的同僚們大多數時候因爲時間限制也忘了在這個領域對他們進行培訓。這會導致一些事故,因爲之前沒有經驗,想讓應用跑起來的時候,會把應用結構弄的一團糟。Maven和它的約定的目的是幫助構建通用的結構,然後對項目的結構應該是什麼樣子做了約定。但同樣的,你需要理解這個工具和約定,然後纔可以掌握你的配置。
經常會聽到有人說“我在那裏添加了一個lib,可以正常工作”。如果你回答“那裏是什麼意思?”,很可能會得到許多有意思的回覆。碰巧或者是運氣好的時候,應用確實能運行。但是,在一個複雜的多模塊的應用裏面,大多數時候,能運行說明不了什麼——問題很快就會出現。
本系列的文章主要是針對Maven和JavaEE新手。如果你是一名高級程序員,歡迎分享或者把它當作一個示例。
我準備拿一個在我的日常工作中發現的有很多實際問題的例子開刀,嘗試給出基本的解釋或者是給出相關資源的連接。歡迎以一種乾淨的方式給出可操作或者可實現的建議、糾正或者是引用。學習Maven並且創建一個複雜但是卻容易維護的應用,最好的方式是白手起家,從一個空的pom文件開始。
我想要向讀者傳達的主要意思是,*學習你的應用的結構,底層的構建工具也是你工作的一部分,永遠不要假設總會有人會來處理。爲了挑戰更難的任務和改善作爲Java開發者的技能,這也是很重要的一步。
用到的主要技術
- 基於JavaEE7的應用
- 應用會被打成WAR包
- 應用由很多組件組成(wars、jars、ejbjars)
- 用Java7進行編譯
- 用Maven3進行打包
我的示例ear應用
作爲本文示例,我的應用最終會會是一個EAR包。裏面包含2個頂級的模塊,一個war包和一個ejb-jar。此外還包含了數據庫領域模型(JPA實體)類的jar。後面的文章中我會擴展這個結構,增加更多的資源。
下面的一個抽象的圖片展示了我們的ear包將包含哪些東西。將來,war模塊會包含servlet或者是jsf組件。services模塊會包含許多常用的無狀態的會話Bean(或是消息驅動Bean)。domain工程會有普通的用JPA2註解標註的Java類。
用Maven構造我們應用的基本結構
爲了構建上面說的ear,我們需要使用Maven定義模塊和應用的組成部分,它是我們的構建、打包、配置工具。這只是衆多步驟中的一步,但是如果你一開始就搞明白了,接下來就是一些簡單的技術或配置。這裏不是最終的解決方案,而是很多時候當你開始一個新的應用時,如何找到接近標準的問題解決方式。因此,這裏沒有華而不實的東西,讓我們遵守標準,開始構建一個堅實的基礎吧。
首先讓我們忘掉上面的那幅圖,然後想一下:在Maven中模塊是什麼,他們是如何進行定義的,怎麼把他們聯繫起來,如何定義他們之間的依賴關係。注意:我提倡的工作方式是遵守標準,而不是最終的解決方案。也就是說,你可以定義更少的模塊和依賴,然後把你的應用打包成ear。假設我要覆蓋非常複雜的結構,所以我總是會遵守標準,定義一個通用的結構。
假設你已經瞭解了Maven的一些基本改變,至少你應該熟悉一些術語。如果不是的話,請先看這裏。
請記住,Maven是關於:根據定義良好的結構把你的文件放到正確的位置。定義Maven插件,插件是用來做諸如編譯、打包、複製文件這樣工作的工具。Maven內置了很多插件。所以,你需要用適當的配置在適當的地方來定義這些插件。不需要寫make或者是ant腳本,只需要插入插件,然後讓Maven按照定義的順序來執行就可以了。
一個和我關係很好的前同事說過(最近的一封email裏寫道):在生活或者是編碼中打破常規是很好的,但是在Maven中永遠不要這麼做。他是對的!
如果你不知道如何安裝Maven,請看看這裏(windows)或這裏(Mac)。
我的Maven工程結構:抽象
我們正在使用Maven構建工程,所以需要考慮maven的pom和模塊。爲了能創建出我們需要的ear包(看上面),我們需要5個pom文件:
- 一個pom,作爲父pom。
- 一個pom,包含、定義最終的ear包,它負責給最終的包做配置。
- 一個pom,包含、定義web應用的代碼,也就是我們的war包。
- 一個pom,包含、定義ejb模塊的代碼,用來打包我們的ejb的模塊。
- 一個pom,包含JPA(數據庫實體)類。
正如你看到的那樣,每一個模塊都有自己的pom文件,一個父pom文件。很多人不在他們的結構中添加父pom文件,因爲他們的工程很小,所以不需要。當更多的模塊添加進來時,沒有父pom就變得一團糟。所以請記住,擁有並配置父pom文件是一件非常好的事情。在它裏面,你會定義你所有依賴的jar包的版本,配置maven的插件,所有的子pom文件都會繼承父pom的配置。
我的Maven工程的結構:父pom文件
正如前面說過的那樣,我們要白手起家。所以我要創建一個新的文件夾叫做“sample-parent”,在這個文件夾中,添加一個新的文件叫做“pom.xml”。
1
2
3
4
5
6
7
8
9
10
|
< project
xmlns = "http://maven.apache.org/POM/4.0.0"
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation = "http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" > < modelVersion >4.0.0</ modelVersion > < groupId >gr.javapapo</ groupId > < artifactId >sample-parent</ artifactId > < version >0.0.1-SNAPSHOT</ version > < packaging >pom</ packaging > </ project > |
是的,現在還沒什麼可興奮的,僅僅是給值是pom的packaging元素做了個記錄。之所以稱之爲父pom,因爲它會定義並管理子模塊,這是在模塊定義部分完成的。我們的初始的pom看起來就像下面這個樣子。這意味着,我們必須在sample-parent下面創建相關的文件夾,然後給它們每一個都添加pom.xml.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
< project
xmlns = "http://maven.apache.org/POM/4.0.0"
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation = "http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" > < modelVersion >4.0.0</ modelVersion > < groupId >gr.javapapo</ groupId > < artifactId >sample-parent</ artifactId > < version >0.0.1-SNAPSHOT</ version > < packaging >pom</ packaging > < modules > < module >sample-ear</ module > < module >sample-web</ module > < module >sample-services</ module > < module >sample-domain</ module > </ modules > </ project > |
讓我們繼續添加一些配置……
這是很重要的部分,因爲我們要定義下面的版本:
- 需要使用和配置的Maven插件。
- 所有的jar包:被其他的模塊所引用和使用的依賴。
- 其他的通用的屬性,比如我們要編譯的Java運行時的版本。
- 源文件或其他資源的默認的編碼。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
< properties > <!-- encoding--> < project.build.sourceEncoding >UTF-8</ project.build.sourceEncoding > <!--java version --> < java-version >1.7</ java-version > <!-- plugin versions --> < ejb-plugin-version >2.3</ ejb-plugin-version > < war-plugin-version >2.4</ war-plugin-version > < ear-plugin-version >2.9</ ear-plugin-version > < compiler-plugin-version >3.1</ compiler-plugin-version > <!-- dependency versions --> < javaee-api-version >7.0</ javaee-api-version > <!-- EJB spec version --> < ejb-spec-version >3.2</ ejb-spec-version > </ properties > |
在properties部分之後,繼續添加另一個重要的部分dependencyManagement。這裏用來定義在應用的模塊中可能會用到的依賴和各自的版本。在這一部分中,我們實際關注的是版本號,包含還是排除依賴是取決於子pom(也就是說它們是不會被自動添加到子pom中的)。它們的作用範圍也是一樣。所以DependencyManagement是一個集中控制版本號的地方。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
< dependencyManagement > < dependencies > < dependency > < groupId >javax</ groupId > < artifactId >javaee-api</ artifactId > < version >${javaee-api-version}</ version > </ dependency > < dependency > < groupId >junit</ groupId > < artifactId >junit</ artifactId > < version >${junit-version}</ version > </ dependency > </ dependencies > </ dependencyManagement > |
在我們的父pom中,跟dependencyManagemt相似的另一個重要的部分是pluginManagement。在這個部分定義所有maven插件的版本和通用的配置。這些插件在我們的應用配置和打包的過程中會被引用或者是使用到。下面的例子中,我定義了一個最基本的編譯器插件。當然,我們還需要更多的插件。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
<!-- Plugin management --> < build > < pluginManagement > < plugins > <!-- compiler plugin --> < plugin > < groupId >org.apache.maven.plugins</ groupId > < artifactId >maven-compiler-plugin</ artifactId > < version >${compiler-plugin-version}</ version > < configuration > < source >${java-version}</ source > < target >${java-version}</ target > < encoding >${project.build.sourceEncoding}</ encoding > </ configuration > </ plugin > </ plugins > </ pluginManagement > </ build > |
讓我們在pluginManagement部分添加更多以後會用到的的插件。定義ejb插件用老編譯和打包我們的ejb,定義war插件用來打包我們的war。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
<!-- ejb plugin --> < plugin > < groupId >org.apache.maven.plugins</ groupId > < artifactId >maven-ejb-plugin</ artifactId > < version >${ejb-plugin-version}</ version > < configuration > < ejbVersion >${ejb-spec-version}</ ejbVersion > </ configuration > </ plugin > <!-- war plugin -skinny wars mode! --> < plugin > < groupId >org.apache.maven.plugins</ groupId > < artifactId >maven-war-plugin</ artifactId > < version >${war-plugin-version}</ version > < configuration > < failOnMissingWebXml >false</ failOnMissingWebXml > < packagingExcludes >WEB-INF/lib/*.jar</ packagingExcludes > < archive > < manifest > < addClasspath >true</ addClasspath > < classpathPrefix >lib/</ classpathPrefix > </ manifest > </ archive > < webResources > < resource > < filtering >true</ filtering > < directory >src/main/webapp</ directory > < includes > < include >**/web.xml</ include > </ includes > </ resource > </ webResources > </ configuration > </ plugin > |
現在
你可以在這裏(tag是post1,bitbucket)下載最小的示例。時光飛逝,看起來我們什麼也還沒完成。但是,一步一步的定義了一個乾淨並且具體的父pom文件,他將是我們在下篇文章中要做的剩餘工作的基礎。
學習的重點
- 標準的maven工程佈局。
- 父pom文件。
- dependencyManagement和pluginManagement的重要性。
轉載自:http://www.importnew.com/12408.html