為什么要用Maven多模塊
假設有這樣一個項目,很常見的Java Web應用。在這個應用中,我們分了幾層:
Dao
Service
Controller
對應的,在一個項目中,我們會看到一些包名:
org.xx.app.dao
org.xx.app.service
org.xx.app.web
org.xx.app.util
但隨着項目的進行,你可能會遇到如下問題:
- 這個應用可能需要有一個前台和一個后台管理端,你發現大部分dao,一些service,和大部分util是在兩個應用中可。
- pom.xml中的依賴列表越來越長以重用的,但是,由於目前只有一個項目,你不得不新建一個項目依賴這個WAR.
- build整個項目的時間越來越長,盡管你只是一直在web層工作,但你不得不build整個項目。
- 某個模塊,比如util,你只想讓一些經驗豐富的人來維護,可是,現在這種情況,每個開發者都能修改,這導致關鍵模塊的代碼質量不能達到你的要求。
我們會發現,其實這里實際上沒有遵守一個設計模式原則:“高內聚,低耦合”。雖然我們通過包名划分了層次,並且你還會說,這些包的依賴都是單向的,沒有包的環依賴。這很好,但還不夠,因為就構建層次來說,所有東西都被耦合在一起了。因此我們需要使用Maven划分模塊。
其實說白了,Maven多模塊就是為了更好的復用,所以,要求划分子模塊,為啥不單獨開工程,然后我們各自去引用呢?我認為有一下幾點:
- 每個工程單獨管理,必然要開單獨的倉庫,是否能及時更新,及時響應,不好管理;
- 每個單獨的工程,那就要單端部署和管理,如何部署,是否在同一台服務器上,是否支持遠程調用,增加了運營運維的成本,不好管理;
- 如果每個都單獨開工程,那么有一部分的基礎代碼就會重復,比如POJO,各種配置,這些就會冗余。
所以,為了內聚和簡單可以重復使用這個因素,就直接在一個大的工程里面,直接去開辟小的模塊,這樣就避免了上述的3個問題。
Maven子模塊和父模塊的設置
在有子模塊的maven工程之中,有子模塊和父模塊兩個角色,父模塊是root的角色,一般我們在父模塊之中不寫代碼,所以創建好父模塊的工程之后,就會刪除src文件夾,父模塊其實就是起一個管理的作用,管理插件,管理依賴,保證我們的子模塊工程,使用的jar包在版本上是沒有沖突的,而真正實現功能的部分是在子模塊,子模塊之間就能夠相互引用(單方面的),這就是基本的一個思想,按照這個想法,我們所要做的事情,其實已經一目了然,父模塊-管理,子模塊->干活,父子之間有層級關系。我們使用的是Maven管理,那就必然是關系到pom.xml文件,其中就牽扯到parent,packaging,modules,dependencyManagement,pluginManagement。
- parent
- 所有的工程,都不需要寫parent了,父工程可以寫,但是沒必要,子模塊,沒法寫,因為父工程只是一個殼子,沒有包名,所以子模塊父模塊都不需要寫了,SpringBoot工程默認會有spring-boot-starter-parent的parent,可以直接去掉。
- packaging
- 父工程變為pom, 子工程變成jar或者war,按照需求,一般是jar,方便引入使用。
- modules
- 父工程之中使用modules標簽,一個工程一個module,把子工程包裹起來。
- dependencyManagement
- 父工程之中使用,依賴管理,相當於一個版本的定義,子工程可以不去使用,使用自己的版本也是可以的。
- pluginManagement
- 父工程之中使用,插件管理,相當於一個版本的定義,子工程可以不去使用,使用自己的版本也是可以的。
按照這個操作后,項目的關系在IDEA之中的顯示是如左圖圖的,也就是,在父工程后面,會標注一個root,而右邊的圖,bootvue雖然在springboot2.x-integration的文件夾下面,但是沒有父子關系的。下面展示三個pom配置:

zgytest的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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<!-- 父模塊,parent可有可無 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>com.zgy.test</groupId>
<artifactId>zgytest</artifactId>
<packaging>pom</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>zgytest</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.3.0.RELEASE</spring-boot.version>
</properties>
<!-- 子模塊 -->
<modules>
<module>mine</module>
<module>hello</module>
</modules>
<!-- 依賴管理 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<!-- 插件管理 -->
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
hello的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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example.hello</groupId>
<artifactId>hello</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>hello</name>
<packaging>jar</packaging>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.3.0.RELEASE</spring-boot.version>
<spring-cloud-alibaba.version>2.2.1.RELEASE</spring-cloud-alibaba.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-dubbo</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.taobao.arthas</groupId>
<artifactId>arthas-spring-boot-starter</artifactId>
<version>3.3.7</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>
mine的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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example.dream</groupId>
<artifactId>mine</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>mine</name>
<packaging>jar</packaging>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.3.0.RELEASE</spring-boot.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!-- 引入hello模塊 -->
<dependency>
<groupId>com.example.hello</groupId>
<artifactId>hello</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>
Maven多模塊項目的創建
和普通的項目創建沒有區別,一般我們直接創建SpringBoot工程或者maven工程,按照上面的Maven子模塊和父模塊的設置的設置配置即可,關鍵點就是pom文件之中的parent,packaging,modules,dependencyManagement,pluginManagement這幾個標簽,其中parent,packaging,modules是必須處理的。詳細:Maven 多模塊父子工程 (含Spring Boot示例),https://www.cnblogs.com/flywang/p/8567175.html,https://blog.csdn.net/u013983628/article/details/89511553,https://blog.csdn.net/qq_42449963/article/details/105405247
Maven子模塊相互調用
其實,maven工程如果分成了子模塊父模塊的模式,父模塊只是一個殼子,相當於是進行了一個依賴,版本等的管理,更像是一個管家,而各個子模塊可以直接從父模塊獲取需要的依賴,這樣的話,就可以保證各個子模塊所獲取的依賴,沒有沖突,相同的依賴,不至於因為版本的不同而導致一些莫名其妙的問題發生,我們也知道,如果是子模塊之間,有依賴,那么我們就直接調用引入依賴即可,但是要確保這個時候是單向的,也就是要排除循環依賴的問題。比如,moduleA,moduleB,在moduleB之中依賴了moduleA,那么也就是要引入moduleA即可,如同下圖之中的mine工程,引入了hello模塊,和我們正常引入其他的jar包並沒有任何區別。
<dependency>
<groupId>com.example.hello</groupId>
<artifactId>hello</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>

Maven子模塊和父模塊工程的替代方式
這個其實我們從上面也可以看出來,模塊之間的調用和普通的引入第三方的jar包沒有任何區別,只不過從上述提出的三個方面:
- 每個工程單獨管理,必然要開單獨的倉庫,是否能及時更新,及時響應,不好管理;
- 每個單獨的工程,那就要單端部署和管理,如何部署,是否在同一台服務器上,是否支持遠程調用,增加了運營運維的成本,不好管理;
- 如果每個都單獨開工程,那么有一部分的基礎代碼就會重復,比如POJO,各種配置,這些就會冗余。
而言,這樣會比較麻煩,不管從運營,維護,以及更新,都會有一定的成本,所以就選擇了多層級的處理方式,但是實際上,還是單層級的項目最簡潔,這種方式代替也很簡單,就是一個模塊,當做是一個單獨的項目,如果有使用就去調用,至於是否支持遠程調用,那就要看是不是部署在同一個服務器上面,如果在同一台服務器,普通的方式就行,如果不在同一台服務器,那就要支持遠程調用,那就要用到RPC,Dubbo的方式了,更遠一步說,就是分布式架構和微服務的范疇了,所以從這個角度上面來說,微服務,就是從這個角度出發的。
