Maven 多模块项目父子关系与 IDEA 导入异常排查记录
1. 问题背景
在学习 Spring AI 时,准备将多个 Demo 整合到一个 Maven 多模块工程中:
text
Spring-AI-demo
│
├── pom.xml # 父工程
│
├── Spring-ai-demo
│ └── pom.xml # 子模块
│
└── Spring-AI-ollama-Demo
└── pom.xml # 子模块
目标结构:
#mermaid-svg-RYdGQOM6TTOP8wLB{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-RYdGQOM6TTOP8wLB .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-RYdGQOM6TTOP8wLB .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-RYdGQOM6TTOP8wLB .error-icon{fill:#552222;}#mermaid-svg-RYdGQOM6TTOP8wLB .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-RYdGQOM6TTOP8wLB .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-RYdGQOM6TTOP8wLB .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-RYdGQOM6TTOP8wLB .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-RYdGQOM6TTOP8wLB .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-RYdGQOM6TTOP8wLB .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-RYdGQOM6TTOP8wLB .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-RYdGQOM6TTOP8wLB .marker{fill:#333333;stroke:#333333;}#mermaid-svg-RYdGQOM6TTOP8wLB .marker.cross{stroke:#333333;}#mermaid-svg-RYdGQOM6TTOP8wLB svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-RYdGQOM6TTOP8wLB p{margin:0;}#mermaid-svg-RYdGQOM6TTOP8wLB .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-RYdGQOM6TTOP8wLB .cluster-label text{fill:#333;}#mermaid-svg-RYdGQOM6TTOP8wLB .cluster-label span{color:#333;}#mermaid-svg-RYdGQOM6TTOP8wLB .cluster-label span p{background-color:transparent;}#mermaid-svg-RYdGQOM6TTOP8wLB .label text,#mermaid-svg-RYdGQOM6TTOP8wLB span{fill:#333;color:#333;}#mermaid-svg-RYdGQOM6TTOP8wLB .node rect,#mermaid-svg-RYdGQOM6TTOP8wLB .node circle,#mermaid-svg-RYdGQOM6TTOP8wLB .node ellipse,#mermaid-svg-RYdGQOM6TTOP8wLB .node polygon,#mermaid-svg-RYdGQOM6TTOP8wLB .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-RYdGQOM6TTOP8wLB .rough-node .label text,#mermaid-svg-RYdGQOM6TTOP8wLB .node .label text,#mermaid-svg-RYdGQOM6TTOP8wLB .image-shape .label,#mermaid-svg-RYdGQOM6TTOP8wLB .icon-shape .label{text-anchor:middle;}#mermaid-svg-RYdGQOM6TTOP8wLB .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-RYdGQOM6TTOP8wLB .rough-node .label,#mermaid-svg-RYdGQOM6TTOP8wLB .node .label,#mermaid-svg-RYdGQOM6TTOP8wLB .image-shape .label,#mermaid-svg-RYdGQOM6TTOP8wLB .icon-shape .label{text-align:center;}#mermaid-svg-RYdGQOM6TTOP8wLB .node.clickable{cursor:pointer;}#mermaid-svg-RYdGQOM6TTOP8wLB .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-RYdGQOM6TTOP8wLB .arrowheadPath{fill:#333333;}#mermaid-svg-RYdGQOM6TTOP8wLB .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-RYdGQOM6TTOP8wLB .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-RYdGQOM6TTOP8wLB .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-RYdGQOM6TTOP8wLB .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-RYdGQOM6TTOP8wLB .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-RYdGQOM6TTOP8wLB .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-RYdGQOM6TTOP8wLB .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-RYdGQOM6TTOP8wLB .cluster text{fill:#333;}#mermaid-svg-RYdGQOM6TTOP8wLB .cluster span{color:#333;}#mermaid-svg-RYdGQOM6TTOP8wLB div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-RYdGQOM6TTOP8wLB .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-RYdGQOM6TTOP8wLB rect.text{fill:none;stroke-width:0;}#mermaid-svg-RYdGQOM6TTOP8wLB .icon-shape,#mermaid-svg-RYdGQOM6TTOP8wLB .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-RYdGQOM6TTOP8wLB .icon-shape p,#mermaid-svg-RYdGQOM6TTOP8wLB .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-RYdGQOM6TTOP8wLB .icon-shape .label rect,#mermaid-svg-RYdGQOM6TTOP8wLB .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-RYdGQOM6TTOP8wLB .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-RYdGQOM6TTOP8wLB .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-RYdGQOM6TTOP8wLB :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Spring-AI-demo 父工程
packaging=pom
Spring-ai-demo 子模块
Spring-AI-ollama-Demo 子模块
但是 IDEA Maven 面板出现:
text
Spring-AI-demo (root)
spring-ai-demo
两个项目并列显示,而不是:
text
Spring-AI-demo
|
└── spring-ai-demo
因此怀疑 Maven 多模块配置异常。
2. 初步排查:检查 pom.xml
2.1 父工程 pom
父工程核心配置:
xml
<packaging>pom</packaging>
<modules>
<module>Spring-ai-demo</module>
</modules>
含义:
- 当前项目不是普通 Spring Boot 应用
- 它负责聚合管理子模块
- Maven 会根据 modules 查找子工程
2.2 子工程 pom
子工程:
xml
<parent>
<groupId>com.djm</groupId>
<artifactId>Spring-AI-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
含义:
子模块继承父工程。
最终关系:
#mermaid-svg-R9y5V7xlbozLL25B{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-R9y5V7xlbozLL25B .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-R9y5V7xlbozLL25B .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-R9y5V7xlbozLL25B .error-icon{fill:#552222;}#mermaid-svg-R9y5V7xlbozLL25B .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-R9y5V7xlbozLL25B .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-R9y5V7xlbozLL25B .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-R9y5V7xlbozLL25B .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-R9y5V7xlbozLL25B .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-R9y5V7xlbozLL25B .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-R9y5V7xlbozLL25B .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-R9y5V7xlbozLL25B .marker{fill:#333333;stroke:#333333;}#mermaid-svg-R9y5V7xlbozLL25B .marker.cross{stroke:#333333;}#mermaid-svg-R9y5V7xlbozLL25B svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-R9y5V7xlbozLL25B p{margin:0;}#mermaid-svg-R9y5V7xlbozLL25B .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-R9y5V7xlbozLL25B .cluster-label text{fill:#333;}#mermaid-svg-R9y5V7xlbozLL25B .cluster-label span{color:#333;}#mermaid-svg-R9y5V7xlbozLL25B .cluster-label span p{background-color:transparent;}#mermaid-svg-R9y5V7xlbozLL25B .label text,#mermaid-svg-R9y5V7xlbozLL25B span{fill:#333;color:#333;}#mermaid-svg-R9y5V7xlbozLL25B .node rect,#mermaid-svg-R9y5V7xlbozLL25B .node circle,#mermaid-svg-R9y5V7xlbozLL25B .node ellipse,#mermaid-svg-R9y5V7xlbozLL25B .node polygon,#mermaid-svg-R9y5V7xlbozLL25B .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-R9y5V7xlbozLL25B .rough-node .label text,#mermaid-svg-R9y5V7xlbozLL25B .node .label text,#mermaid-svg-R9y5V7xlbozLL25B .image-shape .label,#mermaid-svg-R9y5V7xlbozLL25B .icon-shape .label{text-anchor:middle;}#mermaid-svg-R9y5V7xlbozLL25B .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-R9y5V7xlbozLL25B .rough-node .label,#mermaid-svg-R9y5V7xlbozLL25B .node .label,#mermaid-svg-R9y5V7xlbozLL25B .image-shape .label,#mermaid-svg-R9y5V7xlbozLL25B .icon-shape .label{text-align:center;}#mermaid-svg-R9y5V7xlbozLL25B .node.clickable{cursor:pointer;}#mermaid-svg-R9y5V7xlbozLL25B .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-R9y5V7xlbozLL25B .arrowheadPath{fill:#333333;}#mermaid-svg-R9y5V7xlbozLL25B .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-R9y5V7xlbozLL25B .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-R9y5V7xlbozLL25B .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-R9y5V7xlbozLL25B .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-R9y5V7xlbozLL25B .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-R9y5V7xlbozLL25B .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-R9y5V7xlbozLL25B .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-R9y5V7xlbozLL25B .cluster text{fill:#333;}#mermaid-svg-R9y5V7xlbozLL25B .cluster span{color:#333;}#mermaid-svg-R9y5V7xlbozLL25B div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-R9y5V7xlbozLL25B .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-R9y5V7xlbozLL25B rect.text{fill:none;stroke-width:0;}#mermaid-svg-R9y5V7xlbozLL25B .icon-shape,#mermaid-svg-R9y5V7xlbozLL25B .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-R9y5V7xlbozLL25B .icon-shape p,#mermaid-svg-R9y5V7xlbozLL25B .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-R9y5V7xlbozLL25B .icon-shape .label rect,#mermaid-svg-R9y5V7xlbozLL25B .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-R9y5V7xlbozLL25B .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-R9y5V7xlbozLL25B .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-R9y5V7xlbozLL25B :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} spring-boot-starter-parent
Spring-AI-demo
Spring-ai-demo
3. 使用 Maven 命令验证
不要只相信 IDEA 界面。
进入父工程目录:
bash
mvn validate
输出:
text
Reactor Build Order:
Spring-AI-demo [pom]
Spring-ai-demo [jar]
说明:
#mermaid-svg-UpLZRNUm0NUWoVgH{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-UpLZRNUm0NUWoVgH .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-UpLZRNUm0NUWoVgH .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-UpLZRNUm0NUWoVgH .error-icon{fill:#552222;}#mermaid-svg-UpLZRNUm0NUWoVgH .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-UpLZRNUm0NUWoVgH .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-UpLZRNUm0NUWoVgH .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-UpLZRNUm0NUWoVgH .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-UpLZRNUm0NUWoVgH .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-UpLZRNUm0NUWoVgH .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-UpLZRNUm0NUWoVgH .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-UpLZRNUm0NUWoVgH .marker{fill:#333333;stroke:#333333;}#mermaid-svg-UpLZRNUm0NUWoVgH .marker.cross{stroke:#333333;}#mermaid-svg-UpLZRNUm0NUWoVgH svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-UpLZRNUm0NUWoVgH p{margin:0;}#mermaid-svg-UpLZRNUm0NUWoVgH .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-UpLZRNUm0NUWoVgH .cluster-label text{fill:#333;}#mermaid-svg-UpLZRNUm0NUWoVgH .cluster-label span{color:#333;}#mermaid-svg-UpLZRNUm0NUWoVgH .cluster-label span p{background-color:transparent;}#mermaid-svg-UpLZRNUm0NUWoVgH .label text,#mermaid-svg-UpLZRNUm0NUWoVgH span{fill:#333;color:#333;}#mermaid-svg-UpLZRNUm0NUWoVgH .node rect,#mermaid-svg-UpLZRNUm0NUWoVgH .node circle,#mermaid-svg-UpLZRNUm0NUWoVgH .node ellipse,#mermaid-svg-UpLZRNUm0NUWoVgH .node polygon,#mermaid-svg-UpLZRNUm0NUWoVgH .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-UpLZRNUm0NUWoVgH .rough-node .label text,#mermaid-svg-UpLZRNUm0NUWoVgH .node .label text,#mermaid-svg-UpLZRNUm0NUWoVgH .image-shape .label,#mermaid-svg-UpLZRNUm0NUWoVgH .icon-shape .label{text-anchor:middle;}#mermaid-svg-UpLZRNUm0NUWoVgH .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-UpLZRNUm0NUWoVgH .rough-node .label,#mermaid-svg-UpLZRNUm0NUWoVgH .node .label,#mermaid-svg-UpLZRNUm0NUWoVgH .image-shape .label,#mermaid-svg-UpLZRNUm0NUWoVgH .icon-shape .label{text-align:center;}#mermaid-svg-UpLZRNUm0NUWoVgH .node.clickable{cursor:pointer;}#mermaid-svg-UpLZRNUm0NUWoVgH .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-UpLZRNUm0NUWoVgH .arrowheadPath{fill:#333333;}#mermaid-svg-UpLZRNUm0NUWoVgH .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-UpLZRNUm0NUWoVgH .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-UpLZRNUm0NUWoVgH .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-UpLZRNUm0NUWoVgH .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-UpLZRNUm0NUWoVgH .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-UpLZRNUm0NUWoVgH .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-UpLZRNUm0NUWoVgH .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-UpLZRNUm0NUWoVgH .cluster text{fill:#333;}#mermaid-svg-UpLZRNUm0NUWoVgH .cluster span{color:#333;}#mermaid-svg-UpLZRNUm0NUWoVgH div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-UpLZRNUm0NUWoVgH .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-UpLZRNUm0NUWoVgH rect.text{fill:none;stroke-width:0;}#mermaid-svg-UpLZRNUm0NUWoVgH .icon-shape,#mermaid-svg-UpLZRNUm0NUWoVgH .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-UpLZRNUm0NUWoVgH .icon-shape p,#mermaid-svg-UpLZRNUm0NUWoVgH .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-UpLZRNUm0NUWoVgH .icon-shape .label rect,#mermaid-svg-UpLZRNUm0NUWoVgH .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-UpLZRNUm0NUWoVgH .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-UpLZRNUm0NUWoVgH .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-UpLZRNUm0NUWoVgH :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Maven Reactor
Spring-AI-demo
Spring-ai-demo
Maven 本身已经正确识别父子关系。
因此:
- pom 文件没有根本错误
- modules 配置正常
- parent 继承正常
4. 真正的问题:IDEA 项目模型
Maven 和 IDEA 使用不同的模型。
Maven 模型
由:
xml
<modules>
决定。
IDEA 模型
由:
text
.idea
*.iml
决定。
之前的问题:
#mermaid-svg-ZPdYnkAPQvRRTbSQ{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ZPdYnkAPQvRRTbSQ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ZPdYnkAPQvRRTbSQ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ZPdYnkAPQvRRTbSQ .error-icon{fill:#552222;}#mermaid-svg-ZPdYnkAPQvRRTbSQ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ZPdYnkAPQvRRTbSQ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ZPdYnkAPQvRRTbSQ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ZPdYnkAPQvRRTbSQ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ZPdYnkAPQvRRTbSQ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ZPdYnkAPQvRRTbSQ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ZPdYnkAPQvRRTbSQ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ZPdYnkAPQvRRTbSQ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ZPdYnkAPQvRRTbSQ .marker.cross{stroke:#333333;}#mermaid-svg-ZPdYnkAPQvRRTbSQ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ZPdYnkAPQvRRTbSQ p{margin:0;}#mermaid-svg-ZPdYnkAPQvRRTbSQ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ZPdYnkAPQvRRTbSQ .cluster-label text{fill:#333;}#mermaid-svg-ZPdYnkAPQvRRTbSQ .cluster-label span{color:#333;}#mermaid-svg-ZPdYnkAPQvRRTbSQ .cluster-label span p{background-color:transparent;}#mermaid-svg-ZPdYnkAPQvRRTbSQ .label text,#mermaid-svg-ZPdYnkAPQvRRTbSQ span{fill:#333;color:#333;}#mermaid-svg-ZPdYnkAPQvRRTbSQ .node rect,#mermaid-svg-ZPdYnkAPQvRRTbSQ .node circle,#mermaid-svg-ZPdYnkAPQvRRTbSQ .node ellipse,#mermaid-svg-ZPdYnkAPQvRRTbSQ .node polygon,#mermaid-svg-ZPdYnkAPQvRRTbSQ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ZPdYnkAPQvRRTbSQ .rough-node .label text,#mermaid-svg-ZPdYnkAPQvRRTbSQ .node .label text,#mermaid-svg-ZPdYnkAPQvRRTbSQ .image-shape .label,#mermaid-svg-ZPdYnkAPQvRRTbSQ .icon-shape .label{text-anchor:middle;}#mermaid-svg-ZPdYnkAPQvRRTbSQ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ZPdYnkAPQvRRTbSQ .rough-node .label,#mermaid-svg-ZPdYnkAPQvRRTbSQ .node .label,#mermaid-svg-ZPdYnkAPQvRRTbSQ .image-shape .label,#mermaid-svg-ZPdYnkAPQvRRTbSQ .icon-shape .label{text-align:center;}#mermaid-svg-ZPdYnkAPQvRRTbSQ .node.clickable{cursor:pointer;}#mermaid-svg-ZPdYnkAPQvRRTbSQ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ZPdYnkAPQvRRTbSQ .arrowheadPath{fill:#333333;}#mermaid-svg-ZPdYnkAPQvRRTbSQ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ZPdYnkAPQvRRTbSQ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ZPdYnkAPQvRRTbSQ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ZPdYnkAPQvRRTbSQ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ZPdYnkAPQvRRTbSQ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ZPdYnkAPQvRRTbSQ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ZPdYnkAPQvRRTbSQ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ZPdYnkAPQvRRTbSQ .cluster text{fill:#333;}#mermaid-svg-ZPdYnkAPQvRRTbSQ .cluster span{color:#333;}#mermaid-svg-ZPdYnkAPQvRRTbSQ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ZPdYnkAPQvRRTbSQ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ZPdYnkAPQvRRTbSQ rect.text{fill:none;stroke-width:0;}#mermaid-svg-ZPdYnkAPQvRRTbSQ .icon-shape,#mermaid-svg-ZPdYnkAPQvRRTbSQ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ZPdYnkAPQvRRTbSQ .icon-shape p,#mermaid-svg-ZPdYnkAPQvRRTbSQ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ZPdYnkAPQvRRTbSQ .icon-shape .label rect,#mermaid-svg-ZPdYnkAPQvRRTbSQ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ZPdYnkAPQvRRTbSQ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ZPdYnkAPQvRRTbSQ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ZPdYnkAPQvRRTbSQ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Maven
Spring-AI-demo
Spring-ai-demo
IDEA
Spring-AI-demo Module
Spring-ai-demo Module
也就是说:
Maven 认为:
一个父工程包含子模块
但是 IDEA 认为:
两个独立 Maven 项目
5. 为什么修改 pom 没有效果?
因为 IDEA 已经缓存了旧项目结构。
即使:
xml
<parent>
和:
xml
<modules>
修改正确。
IDEA 仍然读取旧的:
text
.idea/
*.iml
导致 Module 关系没有刷新。
6. 最终解决方案
Step 1:确认 Maven 配置正确
执行:
bash
mvn validate
确认 Reactor Build Order。
Step 2:删除 IDEA 项目缓存
删除:
text
.idea
*.iml
注意:
不要删除:
text
pom.xml
src
Step 3:重新导入父工程
不要打开子模块 pom。
正确方式:
text
Open
Spring-AI-demo/pom.xml
让 IDEA 根据 Maven Reactor 重新生成项目模型。
7. 最终正确结构
IDEA Maven 面板:
text
Spring-AI-demo (root)
│
├── Lifecycle
├── Plugins
│
└── Spring-ai-demo
|
├── Lifecycle
└── Plugins
Project Structure:
text
Modules
Spring-AI-demo
Spring-ai-demo
8. 本次问题总结
错误认知
认为:
修改 pom 后 IDEA 会自动理解新的父子关系。
实际上:
Maven 配置和 IDEA 工程模型是两个独立系统。
排查流程总结
以后遇到 Maven 多模块问题:
- 检查父 pom:
xml
<modules>
- 检查子 pom:
xml
<parent>
- 使用:
bash
mvn validate
确认 Maven Reactor。
- 如果 Maven 正常,IDEA 异常:
优先考虑:
- Maven Reload
- 删除
.idea - 删除
.iml - 重新从父 pom 导入
9. 经验总结
这次问题本质:
不是 Spring AI 问题。
不是依赖问题。
不是代码问题。
而是:
Maven 多模块结构、Maven Reactor 和 IDEA Module 模型之间的不一致。
理解这一点以后,面对企业项目中的:
text
xxx-project
├── xxx-common
├── xxx-user
├── xxx-order
└── xxx-gateway
这类大型 Maven 工程,可以快速判断项目结构和排查导入问题。