一、分模块设计与开发:让项目结构更清晰
1.1 为什么需要分模块?单模块开发的痛点
在小型项目中,单模块(所有代码放在一个工程)或许能满足需求,但项目规模扩大后会出现两大核心问题:
- 维护成本高:几十甚至上百个开发人员协作时,所有人操作同一个工程,代码冲突频繁,功能迭代时牵一发而动全身(比如改一个工具类可能影响多个业务模块)。
- 资源复用难:项目中的通用组件(如实体类、工具类、统一响应封装)无法单独抽离,其他项目想使用时,只能复制粘贴或依赖整个工程(导致冗余代码多、性能损耗大)。
1.2 分模块设计的核心思路
分模块设计的本质是 **"拆分" 与 "解耦"**:在项目设计阶段,将一个大工程按功能或结构拆分为多个独立子模块,每个模块专注于特定职责,模块间通过依赖关系协作。
例如:电商项目可拆分为 "商品模块""订单模块""购物车模块",同时抽离 "通用组件模块"(存放实体类、工具类),各业务模块只需依赖通用组件,无需重复编写代码。
1.3 3 种分模块拆分策略(附实战案例)
分模块没有固定标准,但需遵循 "高内聚、低耦合" 原则,常见有 3 种拆分策略:
|------------|-----------------------------------|--------------------|-----------------------------------------------------------------------------------------------------|
| 拆分策略 | 核心思路 | 适用场景 | 工程结构示例 |
| 按功能模块拆分 | 按业务功能划分,每个模块对应一个完整业务(如商品、订单、搜索) | 业务边界清晰的项目(如电商、CRM) | mall-common(通用组件)、mall-goods(商品)、mall-order(订单)、mall-search(搜索) |
| 按层拆分 | 按技术分层划分,每个模块对应一个技术层(如控制层、业务层、数据层) | 技术架构标准化的项目(如中台系统) | mall-common(通用)、mall-controller(控制层)、mall-service(业务层)、mall-mapper(数据层)、mall-pojo(实体类) |
| 功能 + 层混合拆分 | 先按功能拆分,再在每个功能模块内部分层 | 大型复杂项目(如多端共用的后端系统) | mall-common(通用)、mall-goods-controller(商品控制层)、mall-goods-service(商品业务层)、mall-order-controller(订单控制层) |
1.4 分模块开发实战步骤(以 "通用组件拆分" 为例)
以传统 Web 工程为例,将 "实体类" 和 "工具类" 拆分为独立模块,步骤如下:
步骤 1:拆分 "实体类模块"(xxx-pojo)
- 新建 Maven 模块,命名为xxx-pojo(如 tlias-pojo),用途:存放所有实体类(如 User、Order、分页结果 PageBean、统一响应 Result)。
- 在xxx-pojo的pom.xml中引入依赖(实体类常用依赖,如 Lombok、Spring 基础依赖):
|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| <dependencies> <!-- Lombok:简化实体类get/set方法 --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.34</version> </dependency> <!-- Spring基础依赖:支持@DateTimeFormat等注解 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <version>3.2.8</version> </dependency> </dependencies> |
- 将原工程中com.xxx.pojo包下的所有类复制到xxx-pojo的对应包路径下。
步骤 2:拆分 "工具类模块"(xxx-utils)
- 新建 Maven 模块,命名为xxx-utils,用途:存放通用工具类(如 JWT 工具、OSS 上传工具、日期工具)。
- 在xxx-utils的pom.xml中引入工具类依赖(如 JWT、阿里云 OSS):
|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| <dependencies> <!-- JWT:生成/解析令牌 --> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency> <!-- 阿里云OSS:文件上传 --> <dependency> <groupId>com.aliyun.oss</groupId> <artifactId>aliyun-sdk-oss</artifactId> <version>3.17.4</version> </dependency> <!-- 依赖实体类模块:工具类可能用到实体类 --> <dependency> <groupId>com.xxx</groupId> <artifactId>xxx-pojo</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies> |
- 将原工程中com.xxx.utils包下的工具类复制到xxx-utils的对应包路径下。
步骤 3:改造原业务模块(xxx-web-management)
- 删除原工程中已拆分的pojo和utils包(避免冲突)。
- 在原业务模块的pom.xml中引入拆分后的模块依赖:
|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| <dependencies> <!-- 依赖实体类模块 --> <dependency> <groupId>com.xxx</groupId> <artifactId>xxx-pojo</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <!-- 依赖工具类模块 --> <dependency> <groupId>com.xxx</groupId> <artifactId>xxx-utils</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies> |
1.5 分模块开发的核心注意事项
- 先设计后开发:分模块需在项目启动前规划好,避免先开发完再拆分(会导致大量代码迁移和依赖调整)。
- 模块边界清晰:每个模块的职责单一,避免出现 "一个模块既包含商品业务,又包含订单逻辑" 的情况。
- 依赖不循环:禁止出现 "模块 A 依赖模块 B,模块 B 又依赖模块 A" 的循环依赖(会导致 Maven 构建失败)。
二、继承与聚合:解决依赖混乱,实现一键构建
分模块后,会出现新问题:多个模块依赖重复(如每个模块都要引入 Lombok)、版本管理麻烦(改一个依赖版本需改所有模块)、构建时需手动按依赖顺序执行(如先构建 pojo,再构建 utils,最后构建业务模块)。Maven 的 "继承" 和 "聚合" 特性可完美解决这些问题。
2.1 继承:统一管理依赖,简化配置
2.1.1 继承的核心作用
- 简化依赖配置:将多个模块的公共依赖(如 Lombok、Spring Boot Starter)提取到 "父工程",子模块无需重复配置,自动继承父工程依赖。
- 统一版本管理:父工程定义依赖版本,子模块直接使用,避免版本不一致导致的兼容性问题。
2.1.2 继承的实现步骤(以 Spring Boot 项目为例)
Spring Boot 项目默认继承spring-boot-starter-parent,因此我们需要创建 "自定义父工程",让自定义父工程继承 Spring Boot 父工程,再让业务模块继承自定义父工程(支持多重继承)。
步骤 1:创建自定义父工程(xxx-parent)
- 新建 Maven 模块,命名为xxx-parent,打包方式必须为 pom(父工程仅用于依赖管理,不写业务代码,无需 src 目录,可删除)。
- 在父工程的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> <!-- 继承Spring Boot官方父工程 --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.2.8</version> <relativePath/> <!-- 从仓库查找父工程,不指定本地路径 --> </parent> <!-- 自定义父工程坐标 --> <groupId>com.xxx</groupId> <artifactId>xxx-parent</artifactId> <version>1.0-SNAPSHOT</version> <packaging>pom</packaging> <!-- 父工程打包方式必须为pom --> <!-- 1. 配置公共依赖(子模块自动继承) --> <dependencies> <!-- Lombok:所有模块通用 --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.34</version> </dependency> <!-- Spring基础依赖:所有模块通用 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> </dependencies> <!-- 2. 版本锁定(非公共依赖,仅统一版本,子模块需手动引入) --> <dependencyManagement> <dependencies> <!-- JWT:仅部分模块使用,统一版本 --> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency> <!-- 阿里云OSS:仅工具类模块使用,统一版本 --> <dependency> <groupId>com.aliyun.oss</groupId> <artifactId>aliyun-sdk-oss</artifactId> <version>3.17.4</version> </dependency> </dependencies> </dependencyManagement> <!-- 3. 自定义属性(集中管理版本号,便于修改) --> <properties> <maven.compiler.source>17</maven.compiler.source> <!-- JDK版本 --> <maven.compiler.target>17</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <!-- 依赖版本属性(可直接引用) --> <lombok.version>1.18.34</lombok.version> <jwt.version>0.9.1</jwt.version> </properties> </project> |
步骤 2:子模块继承父工程
在每个子模块(如 xxx-pojo、xxx-utils)的pom.xml中配置继承:
|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| <parent> <groupId>com.xxx</groupId> <artifactId>xxx-parent</artifactId> <version>1.0-SNAPSHOT</version> <!-- 父工程pom.xml的相对路径(若父工程与子模块平级,路径为../xxx-parent/pom.xml) --> <relativePath>../xxx-parent/pom.xml</relativePath> </parent> <!-- 子模块坐标:groupId可省略(继承父工程),仅需配置artifactId和version --> <artifactId>xxx-pojo</artifactId> <version>1.0-SNAPSHOT</version> |
步骤 3:子模块引入依赖(无需版本号)
若依赖已在父工程的dependencyManagement中锁定版本,子模块引入时无需写version:
|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| <!-- 子模块xxx-utils引入JWT依赖(版本由父工程统一管理) --> <dependencies> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> </dependency> </dependencies> |
2.1.3 关键知识点: dependencyManagement vs dependencies
很多人会混淆这两个标签,核心区别如下:
|---------|-------------------------|--------------------------|
| 特性 | <dependencies> | <dependencyManagement> |
| 作用 | 直接引入依赖,子模块自动继承 | 仅统一管理依赖版本,不自动引入依赖 |
| 子模块使用方式 | 无需额外配置,直接使用 | 需手动引入依赖,无需写 version |
| 适用场景 | 所有子模块都需要的公共依赖(如 Lombok) | 部分子模块需要的依赖(如 JWT、OSS) |
2.2 聚合:一键构建所有模块,无需手动排序
2.2.1 聚合的核心作用
分模块后,模块间有依赖关系(如 xxx-web 依赖 xxx-utils,xxx-utils 依赖 xxx-pojo),手动构建时需按 "pojo → utils → web" 的顺序执行,否则会因依赖缺失失败。
聚合 可将所有模块纳入 "聚合工程",执行一次构建命令(如mvn package),Maven 会自动按依赖顺序构建所有模块,实现 "一键构建"。
2.2.2 聚合的实现步骤
通常将 "父工程" 同时作为 "聚合工程"(无需额外创建聚合工程),步骤如下:
- 在父工程(xxx-parent)的pom.xml中配置modules标签,指定需要聚合的子模块:
|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| <!-- 聚合工程:指定子模块路径(相对路径) --> <modules> <!-- 若子模块与父工程平级,路径为../xxx-pojo --> <module>../xxx-pojo</module> <module>../xxx-utils</module> <module>../xxx-web-management</module> </modules> |
- 执行聚合构建:在父工程根目录执行 Maven 命令(如mvn clean package),Maven 会自动按依赖顺序构建所有子模块:
- 先构建xxx-pojo(无依赖)
- 再构建xxx-utils(依赖 xxx-pojo)
- 最后构建xxx-web-management(依赖 xxx-utils 和 xxx-pojo)
2.2.3 聚合的注意事项
- 聚合工程的打包方式必须为pom。
- modules中模块的顺序不影响构建顺序(Maven 会自动分析依赖关系)。
- 若需排除某个模块,只需从modules中删除该模块的配置即可。
2.3 继承与聚合的对比总结
|------|------------------------------|--------------------------------|
| 特性 | 继承(Inheritance) | 聚合(Aggregation) |
| 核心目标 | 简化依赖配置、统一版本 | 一键构建所有模块,自动排序 |
| 配置位置 | 子模块的 pom.xml 中(配置 parent 标签) | 聚合工程的 pom.xml 中(配置 modules 标签) |
| 感知关系 | 父工程无法感知哪些子模块继承了自己 | 聚合工程能感知所有被聚合的子模块 |
| 共同点 | 打包方式均为 pom,无业务代码,仅做配置管理 | |
实际开发中,父工程通常同时承担 "继承" 和 "聚合" 的角色(如 xxx-parent 既是父工程,也是聚合工程),这样既能统一管理依赖,又能一键构建所有模块。
三、私服:解决团队内部资源共享与同步
分模块开发后,模块如何在团队内部共享?比如 A 团队开发的xxx-utils模块,B 团队想使用,直接复制代码会导致版本不一致,上传到中央仓库又无权限(中央仓库仅接受开源项目)。Maven 私服可解决这一问题。
3.1 私服的核心概念与作用
3.1.1 什么是私服?
私服是架设在企业局域网内的 "私有 Maven 仓库",本质是 "中央仓库的代理"+"团队内部资源库":
- 代理中央仓库:当本地仓库没有依赖时,先从私服下载;私服没有则自动从中央仓库下载,缓存到私服,下次其他团队成员可直接从私服获取。
- 存储内部资源 :团队开发的模块(如xxx-utils)可上传到私服,其他团队通过依赖坐标直接引用,无需复制代码。
3.1.2 为什么需要私服?
- 资源共享安全可控 :内部模块无需上传公网,既能保护代码隐私,又能避免因代码复制导致的版本不一致(如 A 团队更新
xxx-utils
后,B 团队通过依赖更新即可同步,无需手动替换代码); - 提升依赖下载效率:局域网内下载速度远快于外网,尤其在团队成员多、外网不稳定时,可大幅减少依赖等待时间;
- 规避中央仓库权限限制:公网中央仓库仅允许特定机构上传资源,企业自研的私有模块无法上传,私服则支持团队自主管理上传权限。
3.1.3 依赖查找顺序
Maven 项目引入依赖时,会按以下优先级查找,直至获取依赖:
- 本地仓库 :优先查询开发者本地
.m2/repository
目录; - 私服仓库:本地仓库缺失时,自动请求企业私服;
- 中央仓库:私服无该依赖时,由私服代理下载并缓存,供后续使用。
注:企业中通常仅需搭建 1 台私服,开发人员无需自行搭建,掌握配置和使用即可。
3.2 私服仓库分类与版本规范
3.2.1 私服仓库类型
私服默认包含 3 类仓库,各司其职:
- RELEASE 仓库 :存储功能稳定、停止更新的 "发布版本" 模块(如
xxx-utils-1.0.RELEASE.jar
),用于生产环境; - SNAPSHOT 仓库 :存储处于开发中、需频繁迭代的 "快照版本" 模块(如
xxx-utils-1.0-SNAPSHOT.jar
),用于测试和开发环境; - Central 仓库:代理公网中央仓库,缓存第三方依赖,避免重复从外网获取。
3.2.2 项目版本规范
- RELEASE 版本 :版本号格式为
主版本号.次版本号.修订号
(如 1.0.0),表示功能成熟、无重大 bug,对应上传至 RELEASE 仓库; - SNAPSHOT 版本 :版本号末尾加
-SNAPSHOT
(如 1.0.0-SNAPSHOT),表示仍在迭代,对应上传至 SNAPSHOT 仓库。
3.3 私服资源上传与下载实战
3.3.1 核心配置步骤
实现私服资源的上传与下载,需完成 3 步配置,最终执行deploy
命令即可:
步骤 1:配置私服访问凭证(settings.xml)
在 Maven 安装目录的conf/settings.xml
中,添加私服的用户名和密码(由管理员提供,默认常为admin/admin
):
xml
<servers>
<!-- RELEASE仓库访问凭证 -->
<server>
<id>maven-releases</id>
<username>admin</username>
<password>admin</password>
</server>
<!-- SNAPSHOT仓库访问凭证 -->
<server>
<id>maven-snapshots</id>
<username>admin</username>
<password>admin</password>
</server>
</servers>
注:id
需与后续项目 pom.xml 中的仓库 ID 保持一致,否则无法匹配权限。
步骤 2:配置私服下载地址(settings.xml)
同样在settings.xml
中,配置镜像和仓库权限,让 Maven 优先从私服下载依赖:
xml
<!-- 镜像:所有依赖请求转发到私服 -->
<mirrors>
<mirror>
<id>maven-public</id>
<mirrorOf>*</mirrorOf>
<url>http://localhost:8081/repository/maven-public/</url> <!-- 私服公共仓库地址 -->
</mirror>
</mirrors>
<!-- 允许下载RELEASE和SNAPSHOT版本 -->
<profiles>
<profile>
<id>allow-snapshots</id>
<activation>
<activeByDefault>true</activeByDefault> <!-- 默认激活 -->
</activation>
<repositories>
<repository>
<id>maven-public</id>
<url>http://localhost:8081/repository/maven-public/</url>
<releases><enabled>true</enabled></releases>
<snapshots><enabled>true</enabled></snapshots>
</repository>
</repositories>
</profile>
</profiles>
步骤 3:配置项目上传地址(pom.xml)
在父工程 pom.xml 中,添加distributionManagement
标签,指定不同版本的上传路径:
xml
<distributionManagement>
<!-- RELEASE版本上传地址 -->
<repository>
<id>maven-releases</id> <!-- 与settings.xml中server的id一致 -->
<url>http://localhost:8081/repository/maven-releases/</url>
</repository>
<!-- SNAPSHOT版本上传地址 -->
<snapshotRepository>
<id>maven-snapshots</id> <!-- 与settings.xml中server的id一致 -->
<url>http://localhost:8081/repository/maven-snapshots/</url>
</snapshotRepository>
</distributionManagement>
3.3.2 执行上传与下载
- 资源上传 :在父工程根目录执行
mvn clean deploy
,Maven 会自动根据版本类型(RELEASE/SNAPSHOT)上传至对应仓库,可通过私服管理界面(如http://localhost:8081
)查看; - 资源下载:其他项目组需使用时,直接在 pom.xml 中引入依赖坐标,Maven 会按 "本地→私服→中央仓库" 顺序自动下载。
示例:引入私服中的xxx-utils
模块
xml
<dependency>
<groupId>com.xxx</groupId>
<artifactId>xxx-utils</artifactId>
<version>1.0-SNAPSHOT</version> <!-- 版本需与私服一致 -->
</dependency>
3.4 关键注意事项
- 地址与 ID 一致性 :确保
settings.xml
和pom.xml
中的私服地址、仓库 ID 完全匹配,否则会出现 "权限拒绝" 或 "连接失败"; - 版本规范使用:禁止将 SNAPSHOT 版本上传至 RELEASE 仓库,避免影响生产环境稳定性;
- 企业环境适配 :真实开发中,私服部署在远程服务器,需将配置中的
localhost
替换为实际 IP / 域名,并确保开发环境可访问该服务器; - 依赖冲突处理 :若私服存在多版本依赖,优先使用项目指定版本,或通过父工程
dependencyManagement
统一锁定版本。