Module的概念
熟悉Java开发的对于Maven的模块应该并不陌生,它使用一个名为 POM(Project Object Model)的 XML 文件来描述项目结构和配置信息。一般创建单体项目,都是直接创建一个Maven的project,最终是一个Module,如下图:
同时一个项目可以包含多个模块(module),每个模块都是一个独立的 Maven 项目,拥有自己的 POM 文件,最终是以集成Module的形式呈现,如下:
Maven 多Module的基本结构
cola-demo-project/
├── pom.xml
└── cola-module/
├── module-a/
│ ├── pom.xml
│ └── src/
├── module-b/
│ ├── pom.xml
│ └── src/
└── module-c/
├── pom.xml
└── src/
采用集成多个Module的项目代码结构主要体现在Pom配置文件中
父项目的职责
父项目仅有一个pom文件,无任何源代码。父项目的主要职责集中在pom配置文件中。主要的作用有:
- 依赖管理:定义所有子模块共享的依赖,避免在每个子模块中重复相同的依赖配置。
- 模块管理:通过
modules
元素列出所有子模块的目录名称,Maven 会自动寻找这些目录下的pom.xml
文件。 - 插件管理:定义共享的插件配置,子模块可以继承或覆盖这些配置。
- 构建配置:设置项目的构建配置,如源代码和编译版本的设置。
依赖管理
父 pom.xml
使用 <dependencyManagement>
标签来管理所有子模块的依赖版本,确保项目中依赖的一致性。子模块可以在自己的 pom.xml
中声明依赖,而不需要指定版本,Maven 会从父 pom.xml
中获取相应的版本。
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.apache.maven.ci</groupId>
<artifactId>child2</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
如果子模块需要使用child2这个依赖,因为父项目中已经定义了该依赖,则子模块只需在 <dependencies>
中声明依赖的坐标(即 groupId
和 artifactId
),而不需要指定版本号。Maven 会自动使用父项目中定义的版本。
如果子模块需要使用一个与父项目不同的版本,则子模块必须在 <dependencies>
中明确指定所需的版本号。这将覆盖父项目中定义的版本。
<dependencyManagement>
的作用是提供一个集中管理依赖版本的地方,从而避免在每个子模块中重复相同的版本声明,并确保项目中依赖的一致性。子模块可以继承并使用这些管理的依赖,除非有特殊需要使用不同版本。
模块管理
在多模块项目中,项目的描述、组织信息、开发者信息等这些内容统一管理在父项目中,父项目的 pom.xml
配置了 packaging
为 pom
,这表明父项目本身不会产生一个可部署的构件,而是用来聚合子模块的构建配置。
<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>com.mycompany.myapp</groupId>
<artifactId>my-app-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<!-- 子模块列表 -->
<modules>
<module>my-app-util</module>
<module>my-app-dao</module>
<module>my-app-service</module>
<module>my-app-web</module>
</modules>
<!-- 依赖管理,子模块可以继承这些依赖版本 -->
<dependencyManagement>
……
</dependencyManagement>
<!-- 插件管理,子模块可以继承或引用这些插件配置 -->
<pluginManagement>
……
</pluginManagement>
<!-- 构建配置,可以包含共用的构建逻辑或扩展插件配置 -->
<build>……</build>
</project>
插件管理
父项目的 pom.xml
可以使用 <pluginManagement>
来统一配置插件。
<pluginManagement>
<plugins>
<!-- Maven 编译插件 -->
<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>
</configuration>
</plugin>
<!-- 其他插件 -->
</plugins>
</pluginManagement>
构建管理
在父项目的 pom.xml
中,通过 <build>
标签定义了通用的构建配置。
<build>
<pluginManagement> <!-- 插件管理配置 -->
<!-- 插件配置 -->
</pluginManagement>
<plugins> <!-- 插件配置 -->
<!-- 可以在这里添加一些所有子模块共用的插件 -->
</plugins>
<finalName>${project.artifactId}-${project.version}</finalName>
</build>
多Module的项目组织
其实不单是多Module,单Module的项目也存在项目规划的问题,所谓项目规划是指,对于module的拆分,对于单Module则是包一级别的划分,对于多Module则是module或者说组件一级的的划分,不论是单Module还是多module主要的划分原则主要有两个:
- 基于技术维度的划分
- 基于业务维度的划分
基于技术维度的项目结构
所谓技术维度的划分是指架构视角的划分,例如MVC架构,我的一个多Module项目的子项目是按技术分层来划分的,则会产生Controller、Service、Dao、Util四个Module:
<!-- 父项目基本信息 -->
<groupId>com.mycompany.myapp</groupId>
<artifactId>my-app-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<!-- 子模块列表 -->
<modules>
<module>my-app-util</module>
<module>my-app-dao</module>
<module>my-app-service</module>
<module>my-app-controller</module>
</modules>
此种划分的优势在于,在项目开发中偏向于技术流,能最大程度的保证架构设计的实现,技术可以统一管理开发,极大的降低技术开发成本,模块间的依赖可以在一开始就确定,并且借助maven的依赖限定死,即基于技术理论和架构设计,就规定好各个子模块间的依赖关系。
缺点是对于业务不够友好,不利于业务架构的灵活扩展,随着业务的膨胀,每一个module的代码会爆炸式的增长。因此更适用于业务相对简单、体量相对较小的项目。
基于此种划分,比较成熟的方案是,横向上是以module为单位的技术流划分,统一且明确技术架构边界,纵向上以命名规范、统一为基础,以包的形式来完成业务层面的划分。
基于业务维度的项目结构
基于业务维度的划分则更偏向于业务,每个module面向的不同的业务领域,整个项目就是集成各个领域间的业务关系。
<!-- 父项目基本信息 -->
<groupId>com.mycompany.myapp</groupId>
<artifactId>my-app-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<!-- 子模块列表 -->
<modules>
<module>my-app-order</module>
<module>my-app-account</module>
<module>my-app-pay</module>
<module>my-app-product</module>
</modules>
每个module都是“麻雀虽小、五脏俱全”,每个module都是完整的架构体系,以web项目为例,每个module内部都包含各自的Controller、Service、Dao,技术则内缩到单module内包级别来体现。module视角上则是复杂业务的关系维护。
基于业务维度的项目结构,每个module独立管理开发,面向专业业务,聚焦于业务内容的开发,保证了业务的独立性和扩展性,随着业务的扩展,可以直接新增module,每个module具备完整的技术流,独立演进开发。
缺点就是业务的划分会渗透到技术,依赖关系会变得十分复杂,使用此种方式,需要有较为完善的业务模型,明确的业务体系;适合业务场景复杂,体量较大的项目。