多模块管理简单地理解就是一个 Java 工程项目中不止有一个 pom.xml 文件,会在不同的目录中有多个这样的文件,进而实现 Maven 的多模块管理
在多人使用Maven协作开发项目时,尤其是稍微上点规模的项目,每个RD的工作都细分到具体功能和模块,有些模块甚至还要单独部署。
我们假设有这样一个商城项目,包括以下几个模块:
其中shop和admin需要单独部署,dao、service、util你可能想要一些经验丰富的人来维护,如果使用一个应用来管理的话,所有的功能和模块都会耦合在一起,所有人都可以随意修改代码,这显然不是我们所期望的。
而且使用一个应用来管理的话,任何一个点的代码有变更,整个项目就需要重新build,使用模块化开发的另一个好处是如果dao的代码被修改,只需要重新build dao模块就可以了。web模块可以build成war,dao、service、util等可以build成jar,只需要配置好依赖关系,就可以实现模块间的解耦合。这样的设计才是遵循“高内聚,低耦合”设计原则的。
我们使用上面的例子进行演示,先进行合理的优化,我们希望dao和service作为通用的底层工具来使用,把它们合并成一个核心模块(core),build成core.jar,简单的Maven模块化项目结构如下:
---------- mall //顶级项目
|------ pom.xml //packaging = pom
|------ mall-util //通用工具
| |--- pom.xml //packaging = jar
|------ mall-core //核心模块
| |--- pom.xml //packaging = jar
|------ mall-web-api //接口模块
| |--- pom.xml //packaging = war
|------ mall-web-admin//管理后台
| |--- pom.xml //packaging = war
|------ mall-web-shop//商城前台
| |--- pom.xml //packaging = war
这些模块中api、admin、shop(war)均是可以单独部署的web应用,相互之间没有依赖关系,但都依赖于core模块,而core模块依赖于util模块。
模块拆分策略:推荐按照功能拆分,后期方便转换成微服务架构
然后删掉src(父项目必须遵循),只保留:.idea 文件夹 、项目 pom 文件、以及一个 *.iml 文件
注意: 因为父模块只做依赖管理,不需要编写代码,所以 src 文件夹可以直接删除。编写parent的pom.xml只是为了在各个模块中减少重复的配置。
在项目下创建子模块:
然后会发现module 的 pom 文件发生了变化:
新增了两段配置
<packaging>pompackaging>
<modules>
<module>module-utilmodule>
modules>
pom 是最简单的打包类型。不像jar和war,它生成的构件只有它本身。将 packaging 申明为 pom 则意味着没有代码需要测试或者编译,也没有资源需要处理。
由于我们使用了聚合,所以打包方式必须为pom,否则无法构建。
module的值是子模块相对于当前 POM 的路径。
<parent>
<groupId>com.wqlmgroupId>
<artifactId>moduleartifactId>
<version>1.0-SNAPSHOTversion>
parent>
<artifactId>module-utilartifactId>
<parent>
<groupId>com.wqlmgroupId>
<artifactId>moduleartifactId>
<version>1.0-SNAPSHOTversion>
parent>
声明了该模块继承自 com.wqlm:module:1.0-SNAPSHOT,其实这里面还省略了
由于 relativePath 默认是 …/pom.xml 而我们的子项目确实在父项目的下一级目录中,所以是可以不用填写的
Maven首先在当前构建项目的环境中查找父pom,然后项目所在的文件系统查找,然后是本地存储库,最后是远程repo。
artifactId
是子模块的组件id,由于继承了父pom,所以groupId、version 也可以不写,不写的话就默认继承自父pom
如上所示,在创建多个模块之后,可以在父pom中添加公共配置,然后所有的子模块都会继承这些配置。除此之外,还可以通用对子模块进行 编译、打包、安装… 操作
如果子模块间相互依赖,需要在 dependency
中引入要依赖的子模块,如图
上图中子模块 module-common:1.0-SNAPSHOT 依赖了 module-util:1.0-SNAPSHOT。
子模块间的相互依赖,需要管理好依赖项的版本号,负责容易依赖版本冲突。
简单来说就是把公共依赖及版本号在父 pom 中申明,子项目引入依赖时只需要指定 groupId、artifactId 不需要指定版本号
如下,先在父 pom 中申明依赖及版本号
再在子项目中引入依赖项,注意,不需要指定版本号,默认查找父pom中定义的版本号
以一个普通 Spring Boot 项目为例,首先放一张图,看一下整体项目完成后的结构
其中目录结构为
- detail-page
- detail-client
- detail-service
- detail-start
其中需要注意的是 pom.xml 的文件的配置,该配置决定了父子模块之间的关系
- detail-page:父模块
- detail-client:子模块,无依赖
- detail-service:子模块,依赖detail-client
- detail-start:子模块,依赖detail-service
注意:在依赖引用过程中,千万不可以出现循环依赖,比如 client 引用了 service,service 也引用了 client,如果出现这种情况 maven 在打包的时候会直接报错
<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>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.2.6.RELEASEversion>
parent>
<modelVersion>4.0.0modelVersion>
<groupId>com.drawcodegroupId>
<artifactId>detail-pageartifactId>
<version>0.0.1-SNAPSHOTversion>
<packaging>pompackaging>
<name>detail-pagename>
<properties>
<java.version>1.8java.version>
<maven.compiler.source>1.8maven.compiler.source>
<maven.compiler.target>1.8maven.compiler.target>
properties>
<modules>
<module>detail-clientmodule>
<module>detail-servicemodule>
<module>detail-startmodule>
modules>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
<version>2.2.6.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
<version>2.2.6.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
<exclusions>
<exclusion>
<groupId>org.junit.vintagegroupId>
<artifactId>junit-vintage-engineartifactId>
exclusion>
exclusions>
dependency>
<dependency>
<groupId>com.drawcodegroupId>
<artifactId>detail-serviceartifactId>
<version>${project.version}version>
dependency>
<dependency>
<groupId>com.drawcodegroupId>
<artifactId>detail-clientartifactId>
<version>${project.version}version>
dependency>
dependencies>
dependencyManagement>
<build>
<plugins>
plugins>
build>
project>
detail-start 的 pom.xml
<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>
<groupId>com.drawcodegroupId>
<artifactId>detail-pageartifactId>
<version>0.0.1-SNAPSHOTversion>
<relativePath>../pom.xmlrelativePath>
parent>
<modelVersion>4.0.0modelVersion>
<artifactId>detail-startartifactId>
<packaging>jarpackaging>
<name>detail-startname>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
dependency>
<dependency>
<groupId>com.drawcodegroupId>
<artifactId>detail-serviceartifactId>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
project>
detail-service 的 pom.xml
<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>
<groupId>com.drawcodegroupId>
<artifactId>detail-pageartifactId>
<version>0.0.1-SNAPSHOTversion>
<relativePath>../pom.xmlrelativePath>
parent>
<modelVersion>4.0.0modelVersion>
<artifactId>detail-serviceartifactId>
<packaging>jarpackaging>
<name>detail-servicename>
<dependencies>
<dependency>
<groupId>com.drawcodegroupId>
<artifactId>detail-clientartifactId>
dependency>
dependencies>
project>
detail-start 的 pom.xml
因为 detail-start 没有任何依赖所以比较简单
<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>
<groupId>com.drawcodegroupId>
<artifactId>detail-pageartifactId>
<version>0.0.1-SNAPSHOTversion>
<relativePath>../pom.xmlrelativePath>
parent>
<modelVersion>4.0.0modelVersion>
<artifactId>detail-clientartifactId>
<packaging>jarpackaging>
<name>detail-clientname>
<dependencies>
dependencies>
<build>
build>
project>
其中建议除了各个子模块单独使用的包之外,其他的都要在父模块下的 pom.xml 中配置包信息,这样便于包的版本控制
项目内部存在了包的依赖之后,不同模块之间的代码即可进行使用,比如 detail-service 依赖 detail-client,那么 detail-client 中的 Test2 就可以被 detail-service 使用了
但是反过来 detail-client 不可以使用 detail-service 中的类,因为依赖是单向的关系
启动指令如下
$ mvn clean install && mvn spring-boot:run -pl detail-start
其中 spring-boot:run 可以使用就是因为 spring-boot-maven-plugin 的存在