Maven最佳实践 划分模块 配置多模块项目 pom modules
所有用Maven管理的真實(shí)的項(xiàng)目都應(yīng)該是分模塊的,每個(gè)模塊都對(duì)應(yīng)著一個(gè)pom.xml。它們之間通過(guò)繼承和聚合(也稱作多模塊,multi-module)相互關(guān)聯(lián)。那么,為什么要這么做呢?我們明明在開發(fā)一個(gè)項(xiàng)目,劃分模塊后,導(dǎo)入Eclipse變成了N個(gè)項(xiàng)目,這會(huì)帶來(lái)復(fù)雜度,給開發(fā)帶來(lái)不便。
為了解釋原因,假設(shè)有這樣一個(gè)項(xiàng)目,很常見的Java?Web應(yīng)用。在這個(gè)應(yīng)用中,我們分了幾層:
- Dao層負(fù)責(zé)數(shù)據(jù)庫(kù)交互,封裝了Hibernate交互的類。
- Service層處理業(yè)務(wù)邏輯,放一些Service接口和實(shí)現(xiàn)相關(guān)的Bean。
- Web層負(fù)責(zé)與客戶端交互,主要有一些Structs的Action類。
對(duì)應(yīng)的,在一個(gè)項(xiàng)目中,我們會(huì)看到一些包名:
- org.myorg.app.dao
- org.myorg.app.service
- org.myorg.app.web
- org.myorg.app.util
這樣整個(gè)項(xiàng)目的框架就清晰了,但隨著項(xiàng)目的進(jìn)行,你可能會(huì)遇到如下問(wèn)題:
我們會(huì)發(fā)現(xiàn),其實(shí)這里實(shí)際上沒(méi)有遵守一個(gè)設(shè)計(jì)模式原則:“高內(nèi)聚,低耦合”。雖然我們通過(guò)包名劃分了層次,并且你還會(huì)說(shuō),這些包的依賴都是單向的,沒(méi)有包的環(huán)依賴。這很好,但還不夠,因?yàn)榫蜆?gòu)建層次來(lái)說(shuō),所有東西都被耦合在一起了。因此我們需要使用Maven劃分模塊。
一個(gè)簡(jiǎn)單的Maven模塊結(jié)構(gòu)是這樣的:
---- app-parent
???????????? |-- pom.xml (pom)
???????????? |
???????????? |-- app-util
???????????? |??????? |-- pom.xml (jar)
???????????? |
???????????? |-- app-dao
???????????? |??????? |-- pom.xml (jar)
???????????? |
???????????? |-- app-service
???????????? |??????? |-- pom.xml (jar)
???????????? |
???????????? |-- app-web
???????????? ? ? ? ?? |-- pom.xml (war) ??
上述簡(jiǎn)單示意圖中,有一個(gè)父項(xiàng)目(app-parent)聚合很多子項(xiàng)目(app-util, app-dao, app-service, app-web)。每個(gè)項(xiàng)目,不管是父子,都含有一個(gè)pom.xml文件。而且要注意的是,小括號(hào)中標(biāo)出了每個(gè)項(xiàng)目的打包類型。父項(xiàng)目是pom,也只能是pom。子項(xiàng)目有jar,或者war。根據(jù)它包含的內(nèi)容具體考慮。
這些模塊的依賴關(guān)系如下:
app-dao????? --> app-util
app-service --> app-dao
app-web???? --> app-service
注意依賴的傳遞性(大部分情況是傳遞的,除非你配置了特殊的依賴scope),app-dao依賴于app-util,app-service依賴于app-dao,于是app-service也依賴于app-util。同理,app-web依賴于app-dao,app-util。
用項(xiàng)目層次的劃分替代包層次的劃分能給我們帶來(lái)如下好處:
接下來(lái)討論一下POM配置細(xì)節(jié),實(shí)際上非常簡(jiǎn)單,先看app-parent的pom.xml:
Xml代碼 ?Maven的坐標(biāo)GAV(groupId, artifactId, version)在這里進(jìn)行配置,這些都是必須的。特殊的地方在于,這里的packaging為pom。所有帶有子模塊的項(xiàng)目的packaging都為pom。packaging如果不進(jìn)行配置,它的默認(rèn)值是jar,代表Maven會(huì)將項(xiàng)目打成一個(gè)jar包。
該配置重要的地方在于modules,例子中包含的子模塊有app-util, app-dao, app-service, app-war。在Maven build app-parent的時(shí)候,它會(huì)根據(jù)子模塊的相互依賴關(guān)系整理一個(gè)build順序,然后依次build。
這就是一個(gè)父模塊大概需要的配置,接下來(lái)看一下子模塊符合配置繼承父模塊。、
Xml代碼 ?app-util模塊繼承了app-parent父模塊,因此這個(gè)POM的一開始就聲明了對(duì)app-parent的引用,該引用是通過(guò)Maven坐標(biāo)GAV實(shí)現(xiàn)的。而關(guān)于項(xiàng)目app-util本身,它卻沒(méi)有聲明完整GAV,這里我們只看到了artifactId。這個(gè)POM并沒(méi)有錯(cuò),groupId和version默認(rèn)從父模塊繼承了。實(shí)際上子模塊從父模塊繼承一切東西,包括依賴,插件配置等等。
此外app-util配置了一個(gè)對(duì)于commons-lang的簡(jiǎn)單依賴,這是最簡(jiǎn)單的依賴配置形式。大部分情況,也是通過(guò)GAV引用的。
再看一下app-dao,它也是繼承于app-parent,同時(shí)依賴于app-util:
Xml代碼 ?該配置和app-util的配置幾乎沒(méi)什么差別,不同的地方在于,依賴變化了,app-dao依賴于app-util。這里要注意的是version的值為${project.version},這個(gè)值是一個(gè)屬性引用,指向了POM的project/version的值,也就是這個(gè)POM對(duì)應(yīng)的version。由于app-dao的version繼承于app-parent,因此它的值就是1.0-SNAPSHOT。而app-util也繼承了這個(gè)值,因此在所有這些項(xiàng)目中,我們做到了保持版本一致。
這里還需要注意的是,app-dao依賴于app-util,而app-util又依賴于commons-lang,根據(jù)傳遞性,app-dao也擁有了對(duì)于commons-lang的依賴。
app-service我們跳過(guò)不談,它依賴于app-dao。我們最后看一下app-web:
Xml代碼 ?app-web依賴于app-service,因此配置了對(duì)其的依賴。
由于app-web是我們最終要部署的應(yīng)用,因此它的packaging是war。為此,你需要有一個(gè)目錄src/main/webapp。并在這個(gè)目錄下?lián)碛衱eb應(yīng)用需要的文件,如/WEB-INF/web.xml。沒(méi)有web.xml,Maven會(huì)報(bào)告build失敗,此外你可能還會(huì)有這樣一些子目錄:/js, /img, /css ... 。
看看Maven是如何build整個(gè)項(xiàng)目的,我們?cè)?app-parent 根目錄中運(yùn)行 mvn clean install ,輸出的末尾會(huì)有大致這樣的內(nèi)容:
...
...
[INFO] [war:war]
[INFO] Packaging webapp
[INFO] Assembling webapp[app-web] in [/home/juven/workspaces/ws-others/myapp/app-web/target/app-web-1.0-SNAPSHOT]
[INFO] Processing war project
[INFO] Webapp assembled in[50 msecs]
[INFO] Building war: /home/juven/workspaces/ws-others/myapp/app-web/target/app-web-1.0-SNAPSHOT.war
[INFO] [install:install]
[INFO] Installing /home/juven/workspaces/ws-others/myapp/app-web/target/app-web-1.0-SNAPSHOT.war to /home/juven/.m2/repository/org/myorg/myapp/app-web/1.0-SNAPSHOT/app-web-1.0-SNAPSHOT.war
[INFO]?
[INFO]?
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO] ------------------------------------------------------------------------
[INFO] app-parent ............................................ SUCCESS [1.191s]
[INFO] app-util .............................................. SUCCESS [1.274s]
[INFO] app-dao ............................................... SUCCESS [0.583s]
[INFO] app-service ........................................... SUCCESS [0.593s]
[INFO] app-web ............................................... SUCCESS [0.976s]
[INFO] ------------------------------------------------------------------------
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 4 seconds
[INFO] Finished at: Sat Dec 27 08:20:18 PST 2008
[INFO] Final Memory: 3M/17M
[INFO] ------------------------------------------------------------------------
注意Reactor Summary,整個(gè)項(xiàng)目根據(jù)我們希望的順序進(jìn)行build。Maven根據(jù)我們的依賴配置,智能的安排了順序,app-util, app-dao, app-service, app-web。
最后,你可以在 app-web/target 目錄下找到文件 app-web-1.0-SNAPSHOT.war ,打開這個(gè)war包,在 /WEB-INF/lib 目錄看到了 commons-lang-2.4.jar,以及對(duì)應(yīng)的app-util, app-dao, app-service 的jar包。Maven自動(dòng)幫你處理了打包的事情,并且根據(jù)你的依賴配置幫你引入了相應(yīng)的jar文件。
使用多模塊的Maven配置,可以幫助項(xiàng)目劃分模塊,鼓勵(lì)重用,防止POM變得過(guò)于龐大,方便某個(gè)模塊的構(gòu)建,而不用每次都構(gòu)建整個(gè)項(xiàng)目,并且使得針對(duì)某個(gè)模塊的特殊控制更為方便。本文同時(shí)給出了一個(gè)實(shí)際的配置樣例,展示了如何使用Maven配置多模塊項(xiàng)目。
------------------------------------------------------------------------------------------------------------------------------------------
Super POM(project object model)
Maven內(nèi)置了一個(gè)默認(rèn)的POM(不在項(xiàng)目中,因此不可見),每一個(gè)project都會(huì)繼承自這個(gè)默認(rèn)的POM,因此叫Super POM。除非在項(xiàng)目的配置(POM)中顯式的修改,否則使用默認(rèn)的配置。不同的Maven版本,默認(rèn)的POM也不一樣,遇到問(wèn)題則需要自己檢查。
最小的POM
一個(gè)project就是一個(gè)artifact,project的全稱為: <groupId>:<artifactId>:<version>。
在Maven1中是project.xml,Maven2改成了pom.xml。在Maven1中還有一個(gè)maven.xml用于包含可以執(zhí)行的目標(biāo),在Maven2已經(jīng)配置到了pom.xml中。
[html]?view plaincopy
artifact可以是任何東西,包括JAR, WAR,POM, EBA文件等。group只是artifact的命名空間,有點(diǎn)類似于java的包。如果項(xiàng)目還處在開發(fā)階段,在版本后會(huì)有一個(gè)"SNAPSHOT",Maven只允許snapshot artifact被更新,release版本是不能更新的。
modelVersion很重要,因?yàn)椴煌膍odel,POM的格式是不一樣的。
以上幾個(gè)節(jié)點(diǎn)構(gòu)成了一個(gè)最小的POM,這些是POM必須擁有的信息。
繼承
如果我們需要把一個(gè)artifact放到另一個(gè)artifact中,就需要設(shè)置繼承關(guān)系,這個(gè)繼承關(guān)系是由子的module來(lái)維護(hù)的,因此會(huì)在module的pom中有一個(gè)parent節(jié)點(diǎn)。
[html]?view plaincopy剛才上面講的是目錄結(jié)構(gòu)是如下的時(shí)候:
|-- my-module |?? `-- pom.xml `-- pom.xml如果目錄結(jié)構(gòu)是下面的樣子,
|-- my-module |?? `-- pom.xml `-- parent ???? `-- pom.xml 則需要在parent中添加relativePath節(jié)點(diǎn): [html]?view plaincopy集成(aggregation)
和繼承有點(diǎn)類似,但是父子關(guān)系是由父的POM來(lái)維護(hù)的。方法是在POM中添加modules節(jié)點(diǎn):
[html]?view plaincopy節(jié)點(diǎn)module其實(shí)是目錄名稱,因此如果module不在app中,而是與app平級(jí),則可以寫成這樣:
[html]?view plaincopy項(xiàng)目插值與變量
整個(gè)POM相當(dāng)于一個(gè)對(duì)象,子節(jié)點(diǎn)就是一個(gè)個(gè)屬性,因此可以直接訪問(wèn):
[html]?view plaincopy另外,還有幾個(gè)特殊的變量:
| project.basedir | 當(dāng)前項(xiàng)目所在的目錄 |
| project.baseUri | 當(dāng)前項(xiàng)目所在的目錄,只不過(guò)用 URI的格式表示.?從Maven 2.1.0開始 |
| maven.build.timestamp | Build的開始時(shí)間.從Maven 2.1.0-M1開始 |
在project也可以自定義自己的變量,方法是在properties中添加屬性:
[html]?view plaincopy總結(jié)
以上是生活随笔為你收集整理的Maven最佳实践 划分模块 配置多模块项目 pom modules的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 之前在抖音上在厨房穿着睡衣拿锅跟勺子的姐
- 下一篇: 平仄中的中是什么意思(平仄怎么分分别是第