使用Maven爲一個項目生成多個Jar包

雖然在Java平臺下,各類構建工具如Maven、Gradle、SBT已經獲得了較爲普遍地運用,同時Maven約定的模塊目錄結構也獲得了業界的承認,成爲了Java平臺下項目結構的事實標準。但咱們總沒法避免與各類遺留系統或老系統打交道。在沒有Maven的時代,是Ant統治的時代,它由於提供了較爲靈活的編寫Task的功能,而忽略了制定一套看似呆板,實則有效的標準模板。因而,在不一樣的企業,不一樣的Java項目,出現了各類各樣奇怪的目錄結構與打包要求。拖着這些沉重的歷史包袱,咱們天然但願完全革命,把那些看不順眼的東西所有改造,讓整個項目面目一新。這是好事兒,所謂長痛不如短痛,一會兒把問題腫瘤給割了,痛快!但是現實總有那麼一些阻力會讓咱們縮手縮腳,咱們不能揮起革命的利刃一陣亂砍,這弄很差會砍傷本身的手。因而乎,咱們須要作戰略性的撤退。退一步海闊天空嘛。java

我面對的就是這樣一個軟件系統。這個Java開發的軟件系統一直沒有依賴管理,僅僅編寫了Ant任務用於發佈打包。咱們的任務是漸進地引入Maven,並在從Build到deploy的整個生命週期中,逐步替換Ant,與持續集成搭配起來。這個系統的多數模塊都劃分了服務端與客戶端。然而不巧的是,各個模塊的服務端和客戶端都集中在一個模塊中。同時,這個項目的目錄結構並不是標準的Maven結構,以下圖所示。所以,還須要自定義Source與TestSource的目錄結構。在原來的Ant任務中,是將它們打包成了兩個Jar包。如今,咱們須要在Maven中一樣作到這一點。apache

分析這個目錄結構,無非是在打包時,對文件進行include或exclude。我查閱到Maven的一位開發者Tim O’Brien寫的一篇博客Sonatype的博客,詳細介紹了具體的作法。固然,在博客中,他一再強調了這種作法的不可取,建議在項目模塊上作出好的分解,保證一個Module對應一個Jar包。這篇博客介紹了兩種作法,一個是在Profile中定義,一個則是在build中定義,使用的插件皆爲maven-jar-plugin。對於我要解決的問題,能夠考慮選擇使用第二種作法,由於它只須要執行一條mvn package命令就能同時獲得Server和Client的Jar包。具體的作法就是在插件的配置中,include各自的文件夾便可。配置以下:maven

<groupId>com.test.maven</groupId>
    <artifactId>testmaven</artifactId>
    <version>1.0-SNAPSHOT</version>

    <build>
        <sourceDirectory>src</sourceDirectory>
        <testSourceDirectory>testSrc</testSourceDirectory>

        <plugins>
            <plugin>
                <artifactId>maven-jar-plugin</artifactId>
                <executions>
                    <execution>
                        <id>server</id>
                        <goals><goal>jar</goal></goals>
                        <phase>package</phase>
                        <configuration>
                            <classifier>server</classifier>
                            <includes>
                                <include>**/server/**</include>
                            </includes>
                        </configuration>
                    </execution>
                    <execution>
                        <id>client</id>
                        <goals><goal>jar</goal></goals>
                        <phase>package</phase>
                        <configuration>
                            <classifier>client</classifier>
                            <includes>
                                <include>**/client/**</include>
                            </includes>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

經過這樣的配置,運行mvn package能夠生成三個包,其中testmaven-1.0-SNAPSHOT.jar同時包含了服務端和客戶端的類;而服務端和客戶端對應的Jar則爲testmaven-1.0-SNAPSHOT-server.jar和testmaven-1.0-SNAPSHOT-client.jar。工具

在前面的配置中,咱們並無爲server和client包定義本身的座標,而是沿用了統一的一個。這就意味着依賴這個包的其餘Module,可能沒法經過Dependency來精肯定位Server或Client。這對於部署來講,是沒有問題的,但卻沒法進行依賴管理;除非在依賴的時候,去依賴整個大的模塊。oop

要保證依賴管理,就意味着須要爲server和client分別指定各自的座標。看來須要另闢蹊徑。其實,Maven是支持在一個項目中創建多個子模塊的。咱們能夠考慮在項目中引入兩個子模塊,分別對應server和client,並在這兩個子模塊中創建本身的pom.xml文件。這在本質上是與Maven多模塊支持是相同的,惟一不一樣的是代碼結構。並且這種新建模塊並無影響原有的目錄結構,對於遺留系統而言,仍是能夠接受的。所以,咱們創建了以下圖所示的模塊結構:單元測試

在新的結構中,除了原有模塊外,我還引入了另外兩個新的模塊server和client,它們除了擁有本身的pom.xml文件,沒有其餘任何內容。而在原有模塊下,一樣定義了一個pom.xml文件,它將做爲整個項目的parent。定義以下:測試

<groupId>com.test.maven</groupId>
    <artifactId>testmaven</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging>

    <modules>
        <module>server</module>
        <module>client</module>
    </modules>

這裏定義的座標是整個項目的座標,同時指定了packaging的類型爲pom。在這個pom.xml文件中還包括了兩個子模塊,其中的值應該與模塊的名稱對應。接下來配置server模塊的pom.xml:ui

<parent>
        <groupId>com.test.maven</groupId>
        <artifactId>testmaven</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>testmaven-server</artifactId>

    <build>
        <sourceDirectory>../src</sourceDirectory>
        <testSourceDirectory>../testSrc</testSourceDirectory>

        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <includes>
                        <include>**/server/**</include>
                    </includes>
                </configuration>
            </plugin>
        </plugins>
    </build>

首先聲明瞭parent指向了主模塊的座標。接下來,聲明當前模塊的artifact id爲testmaven-server。這就爲server指定了獨立的座標。一旦部署後,在maven的Repository中會獲得這樣的文件:com/test/maven/testmaven-server/1.0-SNAPSHOT/testmaven-server-1.0-SNAPSHOT.jar。咱們就能夠在依賴中這樣聲明:插件

<dependencies>
        <dependency>
            <groupId>com.test.maven</groupId>
            <artifactId>testmaven-server</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

運行mvn package,oops……居然出現問題了。什麼問題呢?單元測試沒法經過。報告的錯誤爲:3d

Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.1:testCompile (default-testCompile) on project testmaven-server: Compilation failure: Compilation failure:
[ERROR] /Users/twer/learn/testmaven/testSrc/com/test/maven/client/HelloMavenTest.java:[11,9] cannot find symbol
[ERROR] symbol  : class HelloMaven
[ERROR] location: class com.test.maven.client.HelloMavenTest

仔細分析,原來是在執行編譯server包時,報告沒法編譯client包對應的測試類。怎麼會在編譯server包時,去編譯client包對應的測試呢?仔細觀察咱們的pom.xml文件,在maven-compiler-plugin插件中,咱們配置了對server文件的引入,這就意味着在編譯server包時,不會引入client文件夾下的全部文件(固然在這裏就是Java類文件)。可是,咱們並無在test-compile階段排除client對應的測試文件。這就致使client的測試沒法找到對應的實現類。找到根源,問題就好解決了,顯然咱們須要在test-compile階段排除client文件夾。因此,server模塊下正確的pom.xml配置爲:

<parent>
        <groupId>com.test.maven</groupId>
        <artifactId>testmaven</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>testmaven-server</artifactId>

    <build>
        <sourceDirectory>../src</sourceDirectory>
        <testSourceDirectory>../testSrc</testSourceDirectory>

        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <includes>
                        <include>**/server/**</include>
                    </includes>
                </configuration>
                <executions>
                    <execution>
                        <id>default-testCompile</id>
                        <phase>test-compile</phase>
                        <configuration>
                            <testExcludes>
                                <exclude>**/client/**</exclude>
                            </testExcludes>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

注意:我在IntelliJ配置execution下的configuration節時,碰到一個問題,那就是針對testExcludes配置節沒有智能提示。因爲其餘maven配置節在正確狀況下都有智能提示,於是讓我產生錯誤,認爲這個配置項不支持testExcludes,這讓我糾結了好半天。

對於client模塊而言,如法炮製,只是包含以及過濾的文件夾反轉了一個個兒而已。當咱們進行install甚至deploy時,在repository下的test/maven文件夾中,看到了三個文件夾,如圖所示:

其中的testmaven/1.0-SNAPSHOT文件夾下並無jar包,由於它對應的配置爲主模塊的配置,也就是parent配置。在這個配置中,咱們將packaging的類型設置爲pom了。

相關文章
相關標籤/搜索