Maven 聚合工程的幾個小細節

松哥原創的 Spring Boot 視頻教程已經殺青,感興趣的小夥伴戳這裏-->Spring Boot+Vue+微人事視頻教程


Maven 作爲我們 Java 開發領域最最基礎的工具,估計每個小夥伴每天都在用。但是對於新手而言,Maven 中有一些基本的概念似乎很容易搞混,例如繼承、聚合、集成等等。所以今天我們就來簡單聊聊這個話題。

爲什麼需要分模塊

松哥在 vhr(https://github.com/lenve/vhr) 項目中也用到了聚合工程,那麼爲什麼用聚合工程?直接一個工程分包不行嗎?

如果只是一個小 case,分包當然是可以的,但是如果項目越來越大,分包的弊端就會逐步展現出來:

  • pom.xml 中引用的依賴非常多以至於難以維護。
  • 同時在修改 dao 層的代碼,結果改錯了,你在 service 層編譯不通過,煩躁。
  • 項目越來越大,build 等待時間也越來越長。
  • 有一個新的項目想要複用你的 utils 工具包,結果你只能去拷貝代碼。

這樣高度耦和的代碼實際上並不符合我們的設計規範,所以我們需要對代碼進行拆分,做成不同的模塊。

模塊劃分

經過模塊劃分後,我們的 Maven 項目結構可能是這樣的:

└── vhr-parent
    ├── vhr-dao
    │   ├── pom.xml
    │   └── src
    │       ├── main
    │       │   ├── java
    │       │   └── resources
    │       └── test
    │           └── java
    ├── vhr-service
    │   ├── pom.xml
    │   └── src
    │       ├── main
    │       │   ├── java
    │       │   └── resources
    │       └── test
    │           └── java
    └── vhr-web
        ├── pom.xml
        └── src
            ├── main
            │   ├── java
            │   └── resources
            └── test
                └── java

先說下代碼組織形式,可以是父子形式,就像上面這樣,父工程是一個目錄,子工程的目錄在父工程目錄中。也可以是平鋪的形式,即父子工程在同一目錄下。兩種形式皆可,但是在配置上會略有差異,這個松哥後面會說,這裏我們先按照上面這種代碼組織形式來講。

service 依賴 dao,web 依賴 service,默認情況下,依賴是可以傳遞的,所以你在 web 中也可以使用 dao。一些特殊的情況下,如果我們不希望依賴傳遞,則可以使用 scope 節點進行配置。

一般來說,dao、service 都是打包成 jar、web 打包成 war,parent 的 packaging 類型則是一個 pom。

按照模塊劃分之後,上面我們所提到的問題,現在都解決了:

  • 首先,項目變大之後,build 等待時間並不會飛速增長,各個模塊都可以獨自 build。
  • 拆分之後,pom.xml 文件中的依賴也不再凌亂。
  • 如果其他項目有需要,可以方便的將某一個模塊提供給其他項目使用。
  • 依賴版本可以使用 depencencyManagement 節點進行統一管理。spring-boot 中的 parent 就是這麼幹的。

配置細節

首先我們來看下 vhr-parent 中的配置:

<?xml version="1.0" encoding="UTF-8"?>
<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>org.javaboy</groupId>
    <artifactId>vhr-parent</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>vhr-dao</module>
        <module>vhr-service</module>
        <module>vhr-web</module>
    </modules>
</project>

前面我們說過了,parent 的 packaging 節點要爲 pom,這個是固定的,只要有子模塊,packaging 就是 pom。

另外我們還在 parent 的 pom.xml 中配置了 modules,將其所包含的所有子模塊都列出來,當我們在 parent 處進行打包時,Maven 會自動梳理子模塊之間的依賴關係,整理出來一個 build 順序,然後進行編譯打包。

再來看看子模塊的 pom.xml 配置,以 vhr-service 爲例:

<?xml version="1.0" encoding="UTF-8"?>
<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">

    <parent>
        <artifactId>vhr-parent</artifactId>
        <groupId>org.javaboy</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>vhr-service</artifactId>
    <dependencies>
        <dependency>
            <groupId>org.javaboy</groupId>
            <artifactId>vhr-dao</artifactId>
            <version>${project.version}</version>
        </dependency>
    </dependencies>
</project>

子模塊中需要配置 parent,這樣 vhr-service 就繼承了 parent 的一切,包括依賴、插件、座標、版本號等等,有了來自 parent 的一大堆東西之後,vhr-service 自己的 pom.xml 中只需要簡單配置一下 artifactId 即可。依賴的版本號則可以通過 ${project.version} 變量引用項目的版本號。

這裏沒有配置 packaging,默認就是 jar,所以可以省略,如果是 web 模塊,則還需要配置 packaging 爲 war。

打包

聚合工程,聽名字就知道是很多工程聚在一起組成一個完整的項目,所以打包的時候,也是一起打包。直接在 parent 處執行 mvn package 命令,如下:

sang-3:vhr-parent sang$ mvn package
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO] 
[INFO] vhr-parent                                                         [pom]
[INFO] vhr-dao                                                            [jar]
[INFO] vhr-service                                                        [jar]
[INFO] vhr-web                                                            [jar]
[INFO] 
...
...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary for vhr-parent 1.0-SNAPSHOT:
[INFO] 
[INFO] vhr-parent ......................................... SUCCESS [  0.004 s]
[INFO] vhr-dao ............................................ SUCCESS [  0.867 s]
[INFO] vhr-service ........................................ SUCCESS [  0.038 s]
[INFO] vhr-web ............................................ SUCCESS [  0.025 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  1.021 s
[INFO] Finished at: 2021-01-26T12:50:42+08:00
[INFO] ------------------------------------------------------------------------

可以看到整個打包過程,各個聚合工程都被打包了。打包完成後,在不同模塊的目錄下都可以看到一個 target 目錄,裏邊就有剛剛打包好的 jar 或者 war。

如果你使用的是 IntelliJ IDEA,也可以在工具右側找到 Maven->LifeCycle->Package,雙擊進行打包。

目錄問題

在前面的案例中,我們的代碼結構使用了父子目錄的形式,但是在實際應用中,有的時候我們可能會採用平鋪的形式,像下面這樣:

└── vhr
    ├── parent
    │   ├── pom.xml
    ├── vhr-dao
    │   ├── pom.xml
    │   ├── src
    │   │   ├── main
    │   │   │   ├── java
    │   │   │   └── resources
    │   │   └── test
    │   │       └── java
    ├── vhr-service
    │   ├── pom.xml
    │   ├── src
    │   │   ├── main
    │   │   │   ├── java
    │   │   │   └── resources
    │   │   └── test
    │   │       └── java
    └── vhr-web
        ├── pom.xml
        ├── src
        │   ├── main
        │   │   ├── java
        │   │   └── resources
        │   └── test
        │       └── java

可以看到,parent 和各個子模塊處於同一目錄下,這個時候,無論是 parent 的 pom.xml 還是子模塊的 pom.xml,寫法都會和之前略有差異。

先來看 parent 的 pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<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>org.javaboy</groupId>
    <artifactId>parent</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>../vhr-dao</module>
        <module>../vhr-service</module>
        <module>../vhr-web</module>
    </modules>

</project>

由於 module 中定義的是子模塊名稱,所以,當子模塊和父模塊處於同一目錄下時,需要明確指出子模塊的位置,因此這裏用到了相對路徑。

同理,在子模塊中也需要明確指定父模塊的 pom.xml,以 vhr-service 爲例,如下:

<?xml version="1.0" encoding="UTF-8"?>
<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">

    <parent>
        <artifactId>parent</artifactId>
        <groupId>org.javaboy</groupId>
        <version>1.0-SNAPSHOT</version>
        <relativePath>../parent/pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>vhr-service</artifactId>
    <dependencies>
        <dependency>
            <groupId>org.javaboy</groupId>
            <artifactId>vhr-dao</artifactId>
            <version>${project.version}</version>
        </dependency>
    </dependencies>


</project>

可以看到,在 parnet 節點中多了 relativePath 用來指定父模塊的 pom.xml。




往期推薦
0 1

50+ 需求文檔免費下載!

0 2

Spring Security 教程合集

0 3

接了兩個私活,都是血汗錢

本文分享自微信公衆號 - 江南一點雨(a_javaboy)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章