企業 Maven 依賴管理層次結構設計

企業 Maven 依賴管理層次結構設計

準備工作

既然是企業使用,那麼一定要有企業的 Nexus 私服,通過私服可以加快公司內部常用第三方依賴的下載速度,最重要的還是可以將企業內部的項目 deploy 到私服供企業內部項目使用。

關於搭建 Nexus 以及簡單的配置等內容可以參考:

企業 Maven 依賴管理層次結構設計 - 附錄》中的 1 和 3 節內容。

本文是直接使用 Nexus,所以默認認爲已經存在合理的 Nexus 配置和可用的帳號密碼:

企業 Maven Mirror 地址:

http://localhost:8081/repository/maven-public/

帳號密碼:admin/123456

特別說明:maven-public 是一個 Nexus 組,其中可以配置包含哪些 Repository,默認包含 Maven Central 的代理,企業內部的 Releases 和 Snapshots。爲了加速還可以配置上 AliRepo(可以參考《企業 Maven 依賴管理層次結構設計 - 附錄》中第 2 節的內容)。

想要 deploy 到 Nexus 私服,還需要每個用戶(開發人員)在自己電腦上配置 Maven 的 settings.xml,關鍵的部分配置如下:

\<servers\>
    \<!-- 快照版用戶配置 --\>
    \<server\>
        \<id\>nexus-snapshots\</id\>
        \<username\>admin\</username\>
        \<password\>123456\</password\>
    \</server\>
    \<!-- 發佈版用戶配置 --\>
    \<server\>
        \<id\>nexus-releases\</id\>
        \<username\>admin\</username\>
        \<password\>123456\</password\>
    \</server\>
\</servers\>
\<!-- 所有 Maven 依賴下載都走公司私服 --\>
\<mirrors\>
    \<mirror\>
        \<id\>central\</id\>
        \<name\>central\</name\>
        \<url\>http://localhost:8081/repository/maven-public/\</url\>
        \<mirrorOf\>*\</mirrorOf\>
    \</mirror\>
\</mirrors\>

下面在第一部分最基礎的配置部分,會用到這裏的配置,只有兩者的 id 一致才能起到作用。

使用 IDEA 或 Eclipse 的 IDE 時,可能還需要指定上面配置好的 settings.xml。

Nexus 私服配置

這是最基礎的配置部分,這部分的作用就是控制所有 Maven 項目可以 deploy 到 Nexus 私服中,因此這部分幾乎從一開始就會固定下來,所以這裏可以直接使用固定版本號。

創建 company-nexus 目錄,在其中創建 pom.xml 文件,內容如下:

\<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" 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\>com.company\</groupId\>
    \<artifactId\>company-nexus\</artifactId\>
    \<version\>1\</version\>
    \<packaging\>pom\</packaging\>

    \<name\>company-nexus\</name\>
    \<description\>公司 Nexus 基礎配置\</description\>
    \<url\>http://localhost:8081/\</url\>

    \<!-- 發佈配置 --\>
    \<distributionManagement\>
        \<!-- 快照版倉庫 --\>
        \<snapshotRepository\>
            \<!-- 和 settings.xml 中的快照版 id 保持一致 --\>
            \<id\>nexus-snapshots\</id\>
            \<!-- Nexus 中快照版倉庫的地址 --\>
            \<url\>http://localhost:8081/repository/maven-snapshots/\</url\>
        \</snapshotRepository\>
        \<!-- 發佈版倉庫 --\>
        \<repository\>
            \<!-- 和 settings.xml 中的發佈版 id 保持一致 --\>
            \<id\>nexus-releases\</id\>
            \<!-- Nexus 中發佈版倉庫的地址 --\>
            \<url\>http://localhost:8081/repository/maven-releases/\</url\>
        \</repository\>
    \</distributionManagement\>
\</project\>

配置好後,可以直接執行 maven deploy 將上面的 pom 發佈到私服中,產生的部分日誌如下:

[INFO] Scanning for projects...
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building company-nexus 1
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- maven-install-plugin:2.4:install (default-install) @ company-nexus ---
[INFO] Installing F:\Liu\maven\company-nexus\pom.xml to F:\Liu\maven\repository\com\company\company-nexus\1\company-nexus-1.pom
[INFO] 
[INFO] --- maven-deploy-plugin:2.7:deploy (default-deploy) @ company-nexus ---
Uploading: http://localhost:8081/repository/maven-releases/com/company/company-nexus/1/company-nexus-1.pom
Uploaded: http://localhost:8081/repository/maven-releases/com/company/company-nexus/1/company-nexus-1.pom (2 KB at 9.6 KB/sec)
Downloading: http://localhost:8081/repository/maven-releases/com/company/company-nexus/maven-metadata.xml
[IJ]-1-METADATA_DOWNLOADED-[IJ]-path=F:\Liu\maven\repository\com\company\company-nexus\maven-metadata-nexus-releases.xml-[IJ]-artifactCoord=-[IJ]-error=Could not find metadata com.company:company-nexus/maven-metadata.xml in nexus-releases (http://localhost:8081/repository/maven-releases/)
Uploading: http://localhost:8081/repository/maven-releases/com/company/company-nexus/maven-metadata.xml
Uploaded: http://localhost:8081/repository/maven-releases/com/company/company-nexus/maven-metadata.xml (296 B at 2.8 KB/sec)
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.285 s
[INFO] Finished at: 2020-01-29T23:00:51+08:00
[INFO] Final Memory: 8M/40M
[INFO] ------------------------------------------------------------------------

因爲是 release 版本(不含 SNAPSHOT),所以可以在 Nexus 從 maven-releases 下看到:

簡單使用演示

有了最基礎配置後,以後其他項目只要想發佈到私服,就可以將這裏的 pom 設置爲 parent,例如:

\<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" 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\>
    \<parent\>
        \<groupId\>com.company\</groupId\>
        \<artifactId\>company-nexus\</artifactId\>
        \<version\>1\</version\>
    \</parent\>
    \<artifactId\>company-demo\</artifactId\>
    \<version\>1.0.0-SNAPSHOT\</version\>

    \<dependencies\>
        \<dependency\>
            \<groupId\>tk.mybatis\</groupId\>
            \<artifactId\>mapper\</artifactId\>
            \<version\>4.1.5\</version\>
        \</dependency\>
    \</dependencies\>
\</project\>

使用 parent 繼承時,一定要能區分 Maven 子模塊和 Parent 繼承的關係,詳細內容參考:

Maven 的聚合(多模塊)和 Parent 繼承

此時在該項目執行 maven deploy 也可以發佈到私服中。因爲是快照版,所以能從 maven-snapshots 下面看到該文件:

<version> 了。

三方庫依賴配置

這裏從最常見的 Spring 以及 Spring Boot 依賴入手,開始控制所有常用的第三方依賴(根據自己需要添加)。

得益於 Spring,尤其是 Spring Boot 對第三方集成提供的 starter。Spring Boot 對可能用到的第三方依賴提供了很好的版本控制,我們就依靠 Spring 提供的 bom 和 dependencies 來實現我們自己的基礎依賴。

這裏選擇 Spring 和 Spring Boot 的最新版本,這兩個依賴分別如下:

1. Spring Framework (Bill of Materials) » 5.2.3.RELEASE

2. 2.2.4.RELEASE

這兩個依賴分別管理了 21 個和 603 個依賴的版本,通過這兩個就能直接幫我們控制 624 個依賴的版本。除了這兩個基礎之外,我們還要添加需要用到的其他第三方依賴,在選擇依賴時,也要注意優先選擇對方提供的 bom 依賴,例如:

  1. Dubbo BOM » 2.7.5Dubbo Dependencies BOM » 2.7.5
  2. 使用 tk.mybatis 各個獨立依賴,可以選擇 Mapper All » 4.1.5

從 Spring 和 Dubbo 兩對依賴命名方式中,我們可以制定一個簡單的規則。二方庫依賴管理使用 xxx-bom 命名方式,對三方庫的依賴使用 xxx-dependencies 命名方式。因此這裏的三方庫可以命名爲 company-dependencies

company-dependencies 這個三方庫 依賴的項目可以獨立創建,也可以作爲前面 company-nexus 的子模塊進行創建,本文爲了最終分享(源碼)項目的簡單,使用子模塊來管理。

在 company-nexus 中添加子模塊 company-dependencies,這個三方庫在包含上述幾個依賴的情況下,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"\>
    \<parent\>
        \<artifactId\>company-nexus\</artifactId\>
        \<groupId\>com.company\</groupId\>
        \<version\>1\</version\>
    \</parent\>
    \<modelVersion\>4.0.0\</modelVersion\>

    \<artifactId\>company-dependencies\</artifactId\>
    \<version\>1.0.0-SNAPSHOT\</version\>
    \<packaging\>pom\</packaging\>

    \<properties\>
        \<spring.version\>5.2.3.RELEASE\</spring.version\>
        \<spring-boot.version\>2.2.4.RELEASE\</spring-boot.version\>
        \<dubbo.version\>2.7.5\</dubbo.version\>
        \<mapper.version\>4.1.5\</mapper.version\>
    \</properties\>

    \<dependencyManagement\>
        \<dependencies\>
            \<dependency\>
                \<groupId\>org.springframework\</groupId\>
                \<artifactId\>spring-framework-bom\</artifactId\>
                \<version\>${spring.version}\</version\>
                \<scope\>import\</scope\>
                \<type\>pom\</type\>
            \</dependency\>
            \<dependency\>
                \<groupId\>org.springframework.boot\</groupId\>
                \<artifactId\>spring-boot-dependencies\</artifactId\>
                \<version\>${spring-boot.version}\</version\>
                \<scope\>import\</scope\>
                \<type\>pom\</type\>
            \</dependency\>

            \<dependency\>
                \<groupId\>org.apache.dubbo\</groupId\>
                \<artifactId\>dubbo-bom\</artifactId\>
                \<version\>${dubbo.version}\</version\>
                \<scope\>import\</scope\>
                \<type\>pom\</type\>
            \</dependency\>
            \<dependency\>
                \<groupId\>org.apache.dubbo\</groupId\>
                \<artifactId\>dubbo-dependencies-bom\</artifactId\>
                \<version\>${dubbo.version}\</version\>
                \<scope\>import\</scope\>
                \<type\>pom\</type\>
            \</dependency\>

            \<dependency\>
                \<groupId\>tk.mybatis\</groupId\>
                \<artifactId\>mapper-all\</artifactId\>
                \<version\>${mapper.version}\</version\>
                \<scope\>import\</scope\>
                \<type\>pom\</type\>
            \</dependency\>

            \<!-- TODO 繼續添加其他三方庫 --\>
        \</dependencies\>
    \</dependencyManagement\>

\</project\>

這裏是一個簡單示例,真正使用時按照自己企業的需要添加三方庫,通過這個 pom.xml 我們就可以管理三方庫的版本了。當存在大量三方庫以及各種 bom 和 dependencies 時,我們還需要關注下面這個問題。

如何處理依賴版本衝突?

spring-boot-dependenciesdubbo-bom 中都有對 com.google.code.gson » gson 的依賴,Spring 使用了 2.8.6,Dubbo 使用了 2.8.5,我們最終使用的版本是哪個呢?

通過在 IDEA 中右鍵 Show Effective Pom 查看最終完整的 pom 時,可以看到使用的 Gson 是 2.8.6 的版本,通過調整 spring-boot-dependenciesdubbo-bom 的先後順序可以發現,版本有衝突時,配置在前的有效,如果你在管理依賴的最上面添加下面的依賴:

\<dependency\>
    \<groupId\>com.google.code.gson\</groupId\>
    \<artifactId\>gson\</artifactId\>
    \<version\>2.8.0\</version\>
\</dependency\>

最終使用的版本就是 2.8.0,因此當版本有衝突時,除了調整 bom 和 dependencies 順序外,你還可以直接在最上面指定該依賴的具體版本。

當我們把常用的三方庫版本控制後,通過 maven deploy 發佈到 Nexus 私服,然後我們自己的項目就可以基於 company-dependencies 來使用第三方依賴了。

使用的方式可以是作爲 parent 依賴,例如:

\<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" 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\>
    \<parent\>
        \<groupId\>com.company\</groupId\>
        \<artifactId\>company-dependencies\</artifactId\>
        \<version\>1.0.0-SNAPSHOT\</version\>
    \</parent\>
    \<artifactId\>company-demo\</artifactId\>
    \<version\>1.0.0-SNAPSHOT\</version\>

    \<dependencies\>
        \<dependency\>
            \<groupId\>tk.mybatis\</groupId\>
            \<artifactId\>mapper-core\</artifactId\>
        \</dependency\>
        \<dependency\>
            \<groupId\>tk.mybatis\</groupId\>
            \<artifactId\>mapper-generator\</artifactId\>
        \</dependency\>
    \</dependencies\>
\</project\>

還可以通過 <dependencyManagement> 來控制版本:

\<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" 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\>com.company\</groupId\>
    \<artifactId\>company-demo\</artifactId\>
    \<version\>1.0.0-SNAPSHOT\</version\>

    \<dependencyManagement\>
        \<dependencies\>
            \<dependency\>
                \<groupId\>com.company\</groupId\>
                \<artifactId\>company-dependencies\</artifactId\>
                \<version\>1.0.0-SNAPSHOT\</version\>
                \<scope\>import\</scope\>
                \<type\>pom\</type\>
            \</dependency\>
        \</dependencies\>
    \</dependencyManagement\>

    \<dependencies\>
        \<dependency\>
            \<groupId\>tk.mybatis\</groupId\>
            \<artifactId\>mapper-core\</artifactId\>
        \</dependency\>
        \<dependency\>
            \<groupId\>tk.mybatis\</groupId\>
            \<artifactId\>mapper-generator\</artifactId\>
        \</dependency\>
    \</dependencies\>
\</project\>

在企業中,如果想要達到強制使用版本控制,parent 方式更好,利用 parent 分層控制不同的依賴,雖然缺失了一定的靈活性,但能更好的將依賴版本管理傳遞下去,接下來看下一層,二方庫的依賴配置。

如何選擇 release 和 snapshot?

三方庫項目 company-dependencies 在初期不能完整添加所有第三方依賴時,可以先設置版本號爲快照版,等所有依賴穩定時發佈正式版,此時所有以此爲 parent 的項目都需要升級 parent 版本號。

項目不穩定時,如果使用了 release 版本,那麼每次添加第三方依賴都需要升級版本號,所有以此爲 parent 的項目也不可避免的要升級 parent 版本號。

二方庫依賴配置

在上面三方庫的基礎上,企業開發了大量的服務或模塊時,這些服務或模塊之間不可避免的會存在依賴關係,此時就需要管理二方庫依賴了。

假設有以下三個服務(模塊):

- company-common(模塊)
- company-user(服務)
    - company-user-api(子模塊)
    - company-user-service(子模塊)
    - company-user-controller(子模塊)
    - company-user-ui(子模塊)
- company-crm(服務)
    - company-crm-api(子模塊)
    - company-crm-service(子模塊)
    - company-crm-controller(子模塊)
    - company-crm-ui(子模塊)

項目工程結構僅用於演示,和真實情況不完全一致。

其中 user 和 crm 都依賴 common,crm 還依賴 user。當模塊少的時候如果直接寫死在各自的 pom.xml 文件中也沒什麼問題,但是如果有幾十個甚至上百個模塊時,模塊間的傳遞依賴都會導致當升級版本時無從下手,難道升級一個 A 模塊,還需要手動修改幾十個其他模塊嗎?

本文提供的方法,和手動修改幾十個其他模塊各有利弊,下面說說本文推薦的方式。

前面提到過二方庫-bom 後綴,因此這裏在 company-nexus 下面創建 company-bom 子模塊(和前面原因一樣,這裏是爲了最終的項目簡單,你也可以創建獨立項目,不使用子模塊)。對應的 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"\>
    \<parent\>
        \<artifactId\>company-nexus\</artifactId\>
        \<groupId\>com.company\</groupId\>
        \<version\>1\</version\>
    \</parent\>
    \<modelVersion\>4.0.0\</modelVersion\>

    \<artifactId\>company-bom\</artifactId\>
    \<version\>1-SNAPSHOT\</version\>
    \<packaging\>pom\</packaging\>

    \<properties\>
        \<common.version\>1.0.0-SNAPSHOT\</common.version\>
        \<crm.version\>1.0.0-SNAPSHOT\</crm.version\>
        \<user.version\>1.0.0-SNAPSHOT\</user.version\>
    \</properties\>

    \<dependencyManagement\>
        \<dependencies\>
            \<!-- common --\>
            \<dependency\>
                \<groupId\>com.company\</groupId\>
                \<artifactId\>company-common\</artifactId\>
                \<version\>${common.version}\</version\>
            \</dependency\>
            \<!-- crm --\>
            \<dependency\>
                \<groupId\>com.company\</groupId\>
                \<artifactId\>company-crm-api\</artifactId\>
                \<version\>${crm.version}\</version\>
            \</dependency\>
            \<dependency\>
                \<groupId\>com.company\</groupId\>
                \<artifactId\>company-crm-controller\</artifactId\>
                \<version\>${crm.version}\</version\>
            \</dependency\>
            \<dependency\>
                \<groupId\>com.company\</groupId\>
                \<artifactId\>company-crm-service\</artifactId\>
                \<version\>${crm.version}\</version\>
            \</dependency\>
            \<dependency\>
                \<groupId\>com.company\</groupId\>
                \<artifactId\>company-crm-ui\</artifactId\>
                \<version\>${crm.version}\</version\>
            \</dependency\>
            \<!-- user --\>
            \<dependency\>
                \<groupId\>com.company\</groupId\>
                \<artifactId\>company-user-api\</artifactId\>
                \<version\>${user.version}\</version\>
            \</dependency\>
            \<dependency\>
                \<groupId\>com.company\</groupId\>
                \<artifactId\>company-user-controller\</artifactId\>
                \<version\>${user.version}\</version\>
            \</dependency\>
            \<dependency\>
                \<groupId\>com.company\</groupId\>
                \<artifactId\>company-user-service\</artifactId\>
                \<version\>${user.version}\</version\>
            \</dependency\>
            \<dependency\>
                \<groupId\>com.company\</groupId\>
                \<artifactId\>company-user-ui\</artifactId\>
                \<version\>${user.version}\</version\>
            \</dependency\>
        \</dependencies\>
    \</dependencyManagement\>

\</project\>

在當前 bom 中,管理了所有企業內部服務(模塊)的依賴。

此時,如果 company-crm-service 依賴了 company-user-api,就可以通過上面的 bom 來管理版本。例如:

\<?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\>company-crm\</artifactId\>
        \<groupId\>com.company\</groupId\>
        \<version\>1.0.0-SNAPSHOT\</version\>
    \</parent\>
    \<modelVersion\>4.0.0\</modelVersion\>

    \<artifactId\>company-crm-service\</artifactId\>
    \<version\>1.0.0-SNAPSHOT\</version\>

    \<dependencyManagement\>
        \<dependencies\>
            \<dependency\>
                \<groupId\>com.company\</groupId\>
                \<artifactId\>company-bom\</artifactId\>
                \<version\>1-SNAPSHOT\</version\>
                \<scope\>import\</scope\>
                \<type\>pom\</type\>
            \</dependency\>
            \<dependency\>
                \<groupId\>com.company\</groupId\>
                \<artifactId\>company-dependencies\</artifactId\>
                \<version\>1.0.0-SNAPSHOT\</version\>
                \<scope\>import\</scope\>
                \<type\>pom\</type\>
            \</dependency\>
        \</dependencies\>
    \</dependencyManagement\>

    \<dependencies\>
        \<dependency\>
            \<groupId\>com.company\</groupId\>
            \<artifactId\>company-crm-api\</artifactId\>
            \<version\>${project.version}\</version\>
        \</dependency\>
        \<dependency\>
            \<groupId\>com.company\</groupId\>
            \<artifactId\>company-user-api\</artifactId\>
        \</dependency\>

        \<dependency\>
            \<groupId\>tk.mybatis\</groupId\>
            \<artifactId\>mapper-core\</artifactId\>
        \</dependency\>
        \<dependency\>
            \<groupId\>tk.mybatis\</groupId\>
            \<artifactId\>mapper-generator\</artifactId\>
        \</dependency\>
    \</dependencies\>

\</project\>

改成上面這種方式控制版本後,你可能也會發現一個問題,在 bom 中也管理了 company-crm 相關模塊的版本,company-crm 又用到了 company-bom,似乎產生了一個不好的循環依賴。在這種情況下,就需要一定的規則規定來限制往 company-bom 中添加依賴的時機。

在開發 company-crm 初期,bom 中還沒有(不能添加) CRM 的依賴版本控制,當 CRM 第一次 maven deploy 開始允許其他人使用時,才能添加到 bom 中,在這種順序下,即是應有的操作,也是正確的操作。

這裏簡單舉例只寫了一個 company-bom,實際上當存在幾十個上百個服務模塊時,還可以根據功能、產品、項目、部門、項目組等不同維度拆分成更細緻的 bom。上面這種方式可以按照這些維度對版本進行管理,而且簡化了多個項目之間傳遞依賴時的版本不一致,全局控制的版本更統一。當某個版本升級時,修改快照版的 bom 即可。

上面這種方式已經能解決大量依賴版本不一致的問題,但是還存在一些不可避免的缺陷,缺陷的原因還是上面提到過的循環依賴,想象下面這種情況:

當 bom 想發佈 release 版本時,所有服務模塊可能都是快照版,如果服務模塊發佈 release 版本,但是他引用的 bom 還是快照版(理論上 release 版本中不應該存在其他快照版依賴),這個循環依賴導致 bom 無法發佈真正的 release 版本。

針對上面這種情況,有兩種處理方式,既然必須進行取捨,而大量內部依賴時版本不統一的問題更愁人,因此可以選擇永遠使用快照版的 bom 版本,bom 永遠是快照版,有大的升級時可以升級版本,但是不會有 release 版本。bom 中控制的其他服務模塊可以根據需要發佈 release 版本。在這種情況下 bom 不可能頻繁升級小版本(x.y.z-SNAPSHOT 中的 y 和 z),因此版本號可以直接簡化爲 1-SNAPSHOT,升級後直接 2-SNAPSHOT,按照產品迭代更新來說,一年也不會出現太多大版本的升級。

另外一種處理方式就只是爲了所有都能發佈 release 版本,在使用上會有更大的限制。這種情況下,bom 中包含的服務模塊不能使用該 bom,那麼該 bom 就是爲了方便其他功能、產品、項目、部門、項目組等不同維度去使用的,比如下面的簡單例子:

A 部門開發了很多服務,爲了讓其他部門知道本部門所有服務版本號的依賴關係,可以創建一個 company-a-bom,包含了 A 部門提供的所有服務依賴,但是 A 部門內部不使用該 bom,部門內部的依賴還通過直接指定版本號使用。

上面這個例子是方便了其他部門的消費者,但是對本部門來說沒有明顯的好處,反而增加了一些工作量,如果公司就一個部門或很少的部門,這種方式用途就不明顯了。所以本文推薦前一種方式。

繼續引申到下一節,在上面的 company-crm-service 中,添加了 <dependencyManagement>,如下所示:

\<dependencyManagement\>
    \<dependencies\>
        \<dependency\>
            \<groupId\>com.company\</groupId\>
            \<artifactId\>company-bom\</artifactId\>
            \<version\>1-SNAPSHOT\</version\>
            \<scope\>import\</scope\>
            \<type\>pom\</type\>
        \</dependency\>
        \<dependency\>
            \<groupId\>com.company\</groupId\>
            \<artifactId\>company-dependencies\</artifactId\>
            \<version\>1.0.0-SNAPSHOT\</version\>
            \<scope\>import\</scope\>
            \<type\>pom\</type\>
        \</dependency\>
    \</dependencies\>
\</dependencyManagement\>

按照這種用法,api, controller, service, ui 可能需要配置上面的依賴管理,大量複製粘貼這段配置也會導致當增加 bom 或者修改 bom 版本時,所有子模塊都需要改動。而且 api、controller、service、ui 這種模塊可能都包含了相同的基礎依賴,每個服務的子模塊都要重複一遍,這都是隱藏的風險。爲了解決這個問題,我們在 company-bom 和 company-dependencies 基礎上在增加兩個層次來簡化配置。

子模塊依賴配置

到這裏時,我們有了以下幾個低層次的依賴配置:

  • company-nexus:私服基礎配置,方便 deploy 到 Nexus 私服
  • company-dependencies:第三方依賴管理,包含常見和特有的三方庫依賴
  • company-bom:二方庫依賴管理,企業內部服務依賴版本管理

上一節使用的時候需要每個項目都單獨配置 <dependencyManagement> 使用,在這裏爲了簡化這個操作,我們先增加一層封裝。這裏起名爲 company-parent,仍然作爲子模塊創建在 company-nexus 中。其中的 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"\>
    \<parent\>
        \<artifactId\>company-nexus\</artifactId\>
        \<groupId\>com.company\</groupId\>
        \<version\>1\</version\>
    \</parent\>
    \<modelVersion\>4.0.0\</modelVersion\>

    \<artifactId\>company-parent\</artifactId\>
    \<version\>1-SNAPSHOT\</version\>
    \<packaging\>pom\</packaging\>

    \<properties\>
        \<company-bom.version\>1-SNAPSHOT\</company-bom.version\>
        \<company-dependencies.version\>1.0.0-SNAPSHOT\</company-dependencies.version\>
    \</properties\>

    \<dependencyManagement\>
        \<dependencies\>
            \<dependency\>
                \<groupId\>com.company\</groupId\>
                \<artifactId\>company-bom\</artifactId\>
                \<version\>${company-bom.version}\</version\>
                \<scope\>import\</scope\>
                \<type\>pom\</type\>
            \</dependency\>
            \<dependency\>
                \<groupId\>com.company\</groupId\>
                \<artifactId\>company-dependencies\</artifactId\>
                \<version\>${company-dependencies.version}\</version\>
                \<scope\>import\</scope\>
                \<type\>pom\</type\>
            \</dependency\>
        \</dependencies\>
    \</dependencyManagement\>

\</project\>

簡單來說,這兒和前面的 company-crm-service 內容很類似,提取 company-parent 就是爲了將這部分配置單獨管理起來,這樣後續子模塊以這裏的 company-parent 作爲父依賴時就不需要再重複該內容了。這種方式已經解決了前面提到的第一個問題。還剩一個,如何解決 api, controller, service, ui 這種模塊可能都包含的相同的基礎依賴。解決的辦法就是針對 api, controller, service, ui 各封裝一層。在 company-parent 中依次添加下面 4 個子模塊:

company-parent-api:管理 api 層公共依賴

  • company-parent-controller:管理 controller 層公共依賴
  • company-parent-service:管理 service 層公共依賴
  • company-parent-ui:管理 ui 層公共依賴

由於不同企業子模塊拆分形式不同,這裏僅以 service 分層進行簡單的演示。

company-parent-service 對應的 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"\>
    \<parent\>
        \<artifactId\>company-parent\</artifactId\>
        \<groupId\>com.company\</groupId\>
        \<version\>1-SNAPSHOT\</version\>
    \</parent\>
    \<modelVersion\>4.0.0\</modelVersion\>

    \<artifactId\>company-parent-service\</artifactId\>
    \<packaging\>pom\</packaging\>

    \<dependencies\>
        \<!-- 日誌 依賴 --\>
        \<dependency\>
            \<groupId\>org.slf4j\</groupId\>
            \<artifactId\>slf4j-api\</artifactId\>
        \</dependency\>

        \<!-- Spring 依賴 --\>
        \<dependency\>
            \<groupId\>org.springframework\</groupId\>
            \<artifactId\>spring-context-support\</artifactId\>
        \</dependency\>
        \<dependency\>
            \<groupId\>org.springframework\</groupId\>
            \<artifactId\>spring-jdbc\</artifactId\>
        \</dependency\>
        \<dependency\>
            \<groupId\>org.springframework\</groupId\>
            \<artifactId\>spring-tx\</artifactId\>
        \</dependency\>
        \<dependency\>
            \<groupId\>org.springframework\</groupId\>
            \<artifactId\>spring-aop\</artifactId\>
        \</dependency\>
        \<dependency\>
            \<groupId\>org.springframework\</groupId\>
            \<artifactId\>spring-aspects\</artifactId\>
        \</dependency\>

        \<!-- Mapper 依賴 --\>
        \<dependency\>
            \<groupId\>tk.mybatis\</groupId\>
            \<artifactId\>mapper-core\</artifactId\>
        \</dependency\>
        \<dependency\>
            \<groupId\>tk.mybatis\</groupId\>
            \<artifactId\>mapper-base\</artifactId\>
        \</dependency\>
        \<dependency\>
            \<groupId\>tk.mybatis\</groupId\>
            \<artifactId\>mapper-extra\</artifactId\>
        \</dependency\>
        \<dependency\>
            \<groupId\>tk.mybatis\</groupId\>
            \<artifactId\>mapper-spring\</artifactId\>
        \</dependency\>

        \<!-- 數據庫驅動 --\>
        \<dependency\>
            \<groupId\>mysql\</groupId\>
            \<artifactId\>mysql-connector-java\</artifactId\>
        \</dependency\>

        \<!-- 數據庫連接池 --\>
        \<dependency\>
            \<groupId\>com.zaxxer\</groupId\>
            \<artifactId\>HikariCP\</artifactId\>
        \</dependency\>

        \<!-- Dubbo --\>
        \<dependency\>
            \<groupId\>org.apache.dubbo\</groupId\>
            \<artifactId\>dubbo-common\</artifactId\>
        \</dependency\>
        \<dependency\>
            \<groupId\>org.apache.dubbo\</groupId\>
            \<artifactId\>dubbo-config-spring\</artifactId\>
        \</dependency\>
        \<dependency\>
            \<groupId\>org.apache.dubbo\</groupId\>
            \<artifactId\>dubbo-remoting-netty\</artifactId\>
        \</dependency\>
        \<dependency\>
            \<groupId\>org.apache.dubbo\</groupId\>
            \<artifactId\>dubbo-rpc-dubbo\</artifactId\>
        \</dependency\>
        \<dependency\>
            \<groupId\>org.apache.dubbo\</groupId\>
            \<artifactId\>dubbo-registry-zookeeper\</artifactId\>
        \</dependency\>
        \<dependency\>
            \<groupId\>org.apache.dubbo\</groupId\>
            \<artifactId\>dubbo-serialization-hessian2\</artifactId\>
        \</dependency\>
        \<dependency\>
            \<groupId\>org.apache.dubbo\</groupId\>
            \<artifactId\>dubbo-metadata-report-zookeeper\</artifactId\>
        \</dependency\>
        \<dependency\>
            \<groupId\>org.apache.dubbo\</groupId\>
            \<artifactId\>dubbo-configcenter-zookeeper\</artifactId\>
        \</dependency\>

        \<!-- zookeeper --\>
        \<dependency\>
            \<groupId\>org.apache.zookeeper\</groupId\>
            \<artifactId\>zookeeper\</artifactId\>
        \</dependency\>
        \<dependency\>
            \<groupId\>org.apache.curator\</groupId\>
            \<artifactId\>curator-framework\</artifactId\>
        \</dependency\>
    \</dependencies\>

\</project\>

上面只是簡單的示範,真正應用時要根據自己企業的項目來設計。當有了這些子模塊對應的 parent 項目後,我們來改造前面的 company-crm-service,改造後的 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"\>
    \<parent\>
        \<artifactId\>company-parent-service\</artifactId\>
        \<groupId\>com.company\</groupId\>
        \<version\>1-SNAPSHOT\</version\>
    \</parent\>
    \<modelVersion\>4.0.0\</modelVersion\>

    \<artifactId\>company-crm-service\</artifactId\>
    \<version\>1.0.0-SNAPSHOT\</version\>

    \<dependencies\>
        \<dependency\>
            \<groupId\>com.company\</groupId\>
            \<artifactId\>company-crm-api\</artifactId\>
            \<version\>${project.version}\</version\>
        \</dependency\>
        \<dependency\>
            \<groupId\>com.company\</groupId\>
            \<artifactId\>company-user-api\</artifactId\>
        \</dependency\>
    \</dependencies\>

\</project\>

可以看到,改造後只需要添加 company-crm-service 自己特有的依賴即可,而且由於 company-bom 的存在,這裏依賴 company-user-api 時也不需要指定版本號。

將來 company-user-api 版本升級的時候,只需要在 company-bom 中更新版本,company-crm-service 重新構建即可應用到最新版本。如果 company-user-api 存在不兼容的 api 改動,company-crm-service 編譯時就會通過錯誤信息看出來,雖然無法正常編譯了,但是也儘可能快的把錯誤暴露了。

總結

到這裏時,我們有了以下幾個低層次的依賴配置:

company-nexus:私服基礎配置,方便 deploy 到 Nexus 私服

  • company-dependencies:第三方依賴管理,包含常見和特有的三方庫依賴
  • company-bom:二方庫依賴管理,企業內部服務依賴版本管理
  • company-parent:包含上面兩個依賴管理
    • company-parent-api:管理 api 層公共依賴
    • company-parent-controller:管理 controller 層公共依賴
    • company-parent-service:管理 service 層公共依賴
    • company-parent-ui:管理 ui 層公共依賴

有了上面這些標準化的項目工程結構時,不僅僅可以方便的管理各種庫的依賴,利用 Maven 原型還可以方便得快速生成新的項目。這些新的項目結構有特殊的 parent 繼承關係(不理解的一定要看 Maven 的聚合(多模塊)和 Parent 繼承),以提到過的 company-crm 項目爲例,該項目結構如下:

company-crm(服務)

  • company-crm-api(子模塊)
  • company-crm-service(子模塊)
  • company-crm-controller(子模塊)
  • company-crm-ui(子模塊)

下面挨個來看改造後的各個模塊的 pom.xml 可能是什麼樣子。

company-crm

\<?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\>company-nexus\</artifactId\>
        \<groupId\>com.company\</groupId\>
        \<version\>1\</version\>
    \</parent\>
    \<modelVersion\>4.0.0\</modelVersion\>

    \<artifactId\>company-crm\</artifactId\>
    \<version\>1.0.0-SNAPSHOT\</version\>
    \<packaging\>pom\</packaging\>

    \<modules\>
        \<module\>company-crm-api\</module\>
        \<module\>company-crm-service\</module\>
        \<module\>company-crm-controller\</module\>
        \<module\>company-crm-ui\</module\>
    \</modules\>
\</project\>

當前項目包含了 4 個子模塊,本身沒有代碼,因此 <parent> 使用 company-nexus 可以 deploy 就行。

company-crm-api

\<?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\>company-parent-api\</artifactId\>
        \<groupId\>com.company\</groupId\>
        \<version\>1-SNAPSHOT\</version\>
    \</parent\>
    \<modelVersion\>4.0.0\</modelVersion\>
    \<artifactId\>company-crm-api\</artifactId\>
    \<version\>1.0.0-SNAPSHOT\</version\>
\</project\>

注意這裏的 <parent> 是 company-parent-api,不是上面的 company-crm,所以在這個 pom.xml 中必須指定自己的 <version> 版本號,否則會和 <parent> 的 1-SNAPSHOT 一樣。

company-crm-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\>company-parent-service\</artifactId\>
        \<groupId\>com.company\</groupId\>
        \<version\>1-SNAPSHOT\</version\>
    \</parent\>
    \<modelVersion\>4.0.0\</modelVersion\>
    \<artifactId\>company-crm-service\</artifactId\>
    \<version\>1.0.0-SNAPSHOT\</version\>

    \<dependencies\>
        \<dependency\>
            \<groupId\>com.company\</groupId\>
            \<artifactId\>company-crm-api\</artifactId\>
            \<version\>${project.version}\</version\>
        \</dependency\>
        \<dependency\>
            \<groupId\>com.company\</groupId\>
            \<artifactId\>company-user-api\</artifactId\>
        \</dependency\>
    \</dependencies\>
\</project\>

這裏和上面的 api 類似,要注意版本號。一方庫 company-crm-api 的版本號使用 ${project.version}

company-crm-controller

\<?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\>company-parent-controller\</artifactId\>
        \<groupId\>com.company\</groupId\>
        \<version\>1-SNAPSHOT\</version\>
    \</parent\>
    \<modelVersion\>4.0.0\</modelVersion\>
    \<artifactId\>company-crm-controller\</artifactId\>
    \<version\>1.0.0-SNAPSHOT\</version\>
    \<dependencies\>
        \<dependency\>
            \<groupId\>com.company\</groupId\>
            \<artifactId\>company-crm-api\</artifactId\>
            \<version\>${project.version}\</version\>
        \</dependency\>
    \</dependencies\>
\</project\>

這裏和上面的 controller 類似。

company-crm-ui

\<?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\>company-parent-ui\</artifactId\>
        \<groupId\>com.company\</groupId\>
        \<version\>1-SNAPSHOT\</version\>
    \</parent\>
    \<modelVersion\>4.0.0\</modelVersion\>
    \<artifactId\>company-crm-ui\</artifactId\>
    \<version\>1.0.0-SNAPSHOT\</version\>
\</project\>

和上面的 api 類似。

以上就是本文的內容,Maven 依賴管理層次結構設計經過多年真正實踐,應用效果很好,但是能否更廣泛的應用就需要讀者結合自身情況來調整適應。對文中有更好想法或疑問的可以留言進行交流。


歡迎關注我的公衆號,回覆關鍵字“大禮包” ,將會有大禮相送!!! 祝各位面試成功!!!

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