1. 为什么需要新建Module模块
在Spring项目开发中,随着业务复杂度提升,把所有代码堆在一个模块里会越来越难以维护。我经历过一个电商项目,初期所有功能都写在主模块中,到了后期要修改用户模块时,不得不重新编译部署整个项目,每次改动都提心吊胆。
模块化开发的核心价值在于:
- 解耦隔离:支付模块崩溃不会影响商品浏览
- 独立编译:修改订单模块只需编译该模块
- 团队协作:不同小组负责不同模块
- 依赖清晰:通过pom.xml明确定义模块关系
经验:当项目超过20个Service类时,就该考虑拆分模块了。我曾接手过一个包含87个Service的单模块项目,光是理清依赖关系就花了三周时间。
2. 创建Module的两种标准姿势
2.1 在现有项目中新建模块(推荐)
这是企业项目中最常见的做法,以我最近开发的物流系统为例:
bash复制# 项目结构
parent-project/
├── pom.xml
├── order-module/
├── user-module/
└── new-module/ # 新建的模块
具体操作:
- 右键项目 → New → Module
- 选择Maven → 输入模块名(建议小写+中划线)
- 关键配置项:
- GroupId: 保持与父项目一致
- ArtifactId: 模块名称(如payment-service)
- Version: 继承父pom版本
踩坑记录:有次手滑把ArtifactId写成驼峰命名,导致Jenkins构建失败。Maven官方建议使用小写字母和中划线组合。
2.2 创建独立模块后引入
适合需要复用现有模块的场景,比如我们公司的消息通知模块:
xml复制<!-- 在父pom中声明 -->
<modules>
<module>../common-notification</module>
</modules>
注意事项:
- 需要手动添加relativePath
- 模块版本号需要单独管理
- 跨项目引用时建议发布到Nexus私服
3. 模块化配置的黄金法则
3.1 POM文件配置模板
这是经过20+项目验证的配置模板:
xml复制<!-- 子模块pom示例 -->
<parent>
<groupId>com.company</groupId>
<artifactId>parent-project</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>inventory-module</artifactId>
<packaging>jar</packaging>
<dependencies>
<!-- 内部模块依赖 -->
<dependency>
<groupId>com.company</groupId>
<artifactId>common-utils</artifactId>
<version>${project.version}</version>
</dependency>
<!-- 外部依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
关键点:
- packaging类型通常用jar(web模块可用war)
- 内部模块依赖用${project.version}保持版本一致
- 父pom中要声明dependencyManagement
3.2 模块间依赖管理
推荐使用接口隔离原则:
- 定义api模块(包含DTO和FeignClient)
- 实现模块依赖api模块
- 其他模块只依赖api模块
java复制// 在order-api模块中
public interface OrderService {
OrderDTO getOrder(Long id);
}
// 在order-service模块实现
@Service
public class OrderServiceImpl implements OrderService {
// 具体实现
}
4. 实战中的七个必知必会
4.1 热部署配置
在子模块的application.properties中添加:
properties复制# 开启热部署
spring.devtools.restart.enabled=true
# 排除静态资源
spring.devtools.restart.exclude=static/**,templates/**
需要同时在父pom中添加devtools依赖:
xml复制<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
4.2 多模块启动技巧
在IDEA中配置复合启动:
- 新建Compound Configuration
- 添加多个模块的Application
- 设置启动顺序(先启动依赖模块)
4.3 测试代码组织
建议采用这种结构:
code复制src/
├── main/
└── test/
├── java/
│ └── com.company.module
└── resources/
├── application-test.properties
└── test-data.sql
在父pom中配置测试资源过滤:
xml复制<build>
<testResources>
<testResource>
<directory>src/test/resources</directory>
<filtering>true</filtering>
</testResource>
</testResources>
</build>
5. 常见问题排雷指南
5.1 类找不到问题
典型报错:
code复制java.lang.ClassNotFoundException: com.company.module.ClassX
排查步骤:
- 检查依赖模块是否已install
- 执行mvn clean install -pl moduleA -am
- 查看target/classes下是否存在该类
5.2 循环依赖问题
使用mvn dependency:tree检测:
bash复制mvn dependency:tree -Dincludes=com.company
解决方案:
- 提取公共代码到新模块
- 使用事件驱动(Spring Event)
- 改为接口依赖
5.3 配置加载顺序
Spring Boot多模块配置加载顺序:
- 父模块的application.yml
- 子模块的application.yml
- 激活的profile配置
- 命令行参数
建议在子模块中明确覆盖父模块配置:
yaml复制# 子模块配置
spring:
datasource:
url: jdbc:mysql://localhost:3306/module_db
6. 高级模块化技巧
6.1 条件化模块加载
使用@ConditionalOnProperty:
java复制@Configuration
@ConditionalOnProperty(name = "module.feature.enabled", havingValue = "true")
public class FeatureAutoConfiguration {
// 配置类
}
然后在application.properties中控制:
properties复制module.feature.enabled=true
6.2 动态依赖管理
在父pom中使用profiles:
xml复制<profiles>
<profile>
<id>with-redis</id>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
</dependencies>
</profile>
</profiles>
激活命令:
bash复制mvn install -Pwith-redis
6.3 多模块Docker构建
推荐使用docker-compose编排:
yaml复制version: '3'
services:
order-service:
build: ./order-module
depends_on:
- redis
user-service:
build: ./user-module
每个模块Dockerfile示例:
dockerfile复制FROM openjdk:17-jdk
COPY target/module.jar /app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
7. 企业级最佳实践
经过多个百万级用户项目验证的方案:
-
模块命名规范
- 业务模块:biz-trade, biz-user
- 通用模块:common-utils, common-dao
- 基础组件:component-cache, component-mq
-
版本管理策略
- 父pom用
. . - 子模块继承父版本
- 修改版本号后执行:
bash复制mvn versions:set -DnewVersion=1.1.0
- 父pom用
-
CI/CD集成
yaml复制# GitLab CI示例 build_module: stage: build only: changes: - "order-module/**" script: - mvn clean install -pl order-module -am -
接口文档聚合
使用SpringDoc OpenAPI配置:java复制@Bean public OpenApiResource openApiResource() { return new OpenApiResource("order-module", "/v3/api-docs/order"); }
最近在金融项目中采用这套方案后,编译时间从原来的8分钟降低到2分钟,各团队开发效率提升40%以上。关键是要在项目初期就规划好模块结构,后期重构成本会非常高。