Maven多模块项目正确结构的核心是父POM设<packaging>pom</packing>并声明<modules>,子模块通过<parent>继承且依赖由<dependencyManagement>统一版本管理,模块间依赖须单向向下、避免循环。

直接说结论:多模块项目不是靠“配置”出来的,而是靠 Maven/Gradle 的声明式结构 + IDEA 的自动识别共同完成的;手动在 Project Structure 里点来点去只能修修补补,甚至会破坏构建一致性。
父模块的 pom.xml 必须设为 packaging=pom
这是整个多模块结构的起点。IDEA 不会凭空认定哪个是父模块——它只看 pom.xml 里的 <packaging>pom</packaging>。如果漏了这行,哪怕你把模块拖得再整齐,Maven 命令(如 mvn clean install)也无法聚合构建,IDEA 的 Maven 工具窗口里也看不到子模块树。
- 父模块的
pom.xml中必须包含<modules>列表,每个<module>是子模块相对于父目录的路径(如<module>common-utils</module>),不能带.xml或斜杠结尾 - 子模块的
pom.xml必须有<parent>块,且<relativePath>默认为../pom.xml;如果父 POM 不在上一级,必须显式写对路径 - 不要在父模块里写
<dependencies>(业务依赖),那是<dependencyManagement>的位置;否则子模块会无条件继承,容易引发冲突
IDEA 导入时选 “Import project from external model” 而非 “Create project from existing sources”
这是最容易踩的坑。如果你用 “Create project from existing sources”,IDEA 会当成普通文件夹处理,自动生成 .iml 文件,但完全忽略 pom.xml 或 build.gradle.kts 的模块关系,后续你手动在 Project Structure 里加模块、调依赖,全是假把式——Maven/Gradle 构建时照样报错。
- 正确做法:关闭项目 → File → New → Project from Existing Sources → 选中父目录下的
pom.xml或settings.gradle.kts→ 勾选Import project from external model→ 选 Maven 或 Gradle - 导入后,检查 Maven 工具窗口(右侧边栏)是否展开出完整的模块树;如果没有,右键父项目 → Reload project
- Gradle 项目同理,但注意:如果用了
includeBuild或 composite build,需确保子项目根目录下有settings.gradle.kts,否则 IDEA 不识别为独立模块
Modules 标签页里的 Sources/Paths/Deps 只能微调,不能定义模块关系
很多人卡在这里:在 Project Structure → Modules 里反复设置 Sources 根、输出路径、Dependencies,以为这样就能“配好”多模块。其实这些只是 IDEA 编译期的辅助配置,不影响 Maven/Gradle 的实际依赖解析和打包行为。
-
Sources标签下标蓝的目录才是编译源码路径;标绿的是测试源,标橙的是构建产物(如target/classes),别手抖点成 Sources -
Paths里建议勾选Inherit project compile output path,避免每个模块单独设输出目录导致类路径混乱 -
Dependencies标签下看到的 jar 和模块,应与pom.xml中的<dependency>严格一致;如果手动加了一个 jar,但pom.xml没写,CI 构建时必然失败
运行/调试时选对模块的 main 类或 Spring Boot 启动类
多模块项目里,只有特定模块(比如 web-app)才含可执行入口。IDEA 默认可能找不到,或者跑错模块的 classpath。
- 右键某个
Application.java→ Run 'App.main()',IDEA 会自动创建一个 Run Configuration,其Use classpath of module会设为该类所在模块,这个值必须匹配 - 如果启动报
ClassNotFoundException,先检查该 Run Configuration 的Module下拉框是否选对了模块名(不是项目名,也不是父模块名) - Spring Boot 项目中,若
spring-boot-maven-plugin配在子模块里,那只有该模块能打可执行 jar;父模块打出来只是个空 pom,双击运行不了
真正麻烦的从来不是怎么点菜单,而是父子模块的 pom.xml 版本对齐、relativePath 路径写错、IDEA 缓存没刷新导致模块状态不一致——这些错误不会立刻报红,但会在 CI 构建或同事拉代码时突然爆发。
















