搞懂 Maven:settings.xml 与 pom.xml 的关系与生效流程
写给被 Maven 配置绕晕的你。读完这篇,你会彻底明白
settings.xml和pom.xml各自管什么、怎么联动,以及那个神秘的mirror到底在干嘛。
目录
- 一、两个文件,各管一摊
- 二、settings.xml 里的三件宝
- 三、pom.xml 里的关键配置
- 四、它们怎么联动?合并 + 拦截
- 五、用一个完整流程串起来
- 六、常见疑问解答
- 七、一张图总结
一、两个文件,各管一摊
| 文件 | 作用范围 | 内容性质 | 谁维护 |
|---|---|---|---|
settings.xml | 本机全局(所有项目) | 环境相关:本地仓库路径、镜像、网络、JDK 默认值 | 开发者本人 |
pom.xml | 单个项目 | 项目相关:依赖、插件、构建流程、发布地址 | 项目代码仓库 |
优先级:pom.xml > settings.xml(同名配置,pom 覆盖 settings)。
一句话:settings 是“你自己的习惯”,pom 是“这个项目的说明书”。
二、settings.xml 里的三件宝
一份典型的 settings.xml 长这样(精简版):
<settings><!-- 宝贝1:本地仓库路径 --><localRepository>D:\apache-maven-3.8.4\res</localRepository><!-- 宝贝2:镜像路牌 --><mirrors><mirror><id>aliyunmaven</id><mirrorOf>*,!yum-public-repo</mirrorOf><url>https://maven.aliyun.com/repository/public</url></mirror></mirrors><!-- 宝贝3:配置袋子(profile) --><profiles><profile><id>default-profile</id><repositories>...</repositories><pluginRepositories>...</pluginRepositories><properties>...</properties></profile></profiles><activeProfiles><activeProfile>default-profile</activeProfile></activeProfiles></settings>宝贝1:localRepository(本地仓库路径)
决定 Maven 找到依赖后缓存在哪。每次找依赖都先查这里,命中就直接用,不再上网。
宝贝2:mirrors(镜像路牌)
这是 settings 里最容易绕晕的部分,单独拎出来讲。
它的核心是<mirrorOf>这一行,写的是**“拦截哪些请求”**。以*,!yum-public-repo为例:
| 符号 | 含义 |
|---|---|
* | 拦截所有仓库 |
!yum-public-repo | 除了id 叫yum-public-repo的仓库 |
, | “和”,连接两条规则 |
合起来读:拦截所有仓库请求,但放过yum-public-repo,让它直走原地址。
⚠️ 注意:!优先级高于*,就像老师说"所有同学都要考试,除了班长"——班长被豁免。
关键点:mirror 靠<id>识别仓库,不是看 url,不是看名字。pom 里的<repository><id>必须和<mirrorOf>里写的字一字不差,才能被精准拦截或豁免。
宝贝3:profiles(配置袋子)
profile 是一个带名字的配置包,里面能装三样东西:
| 装的东西 | 作用 |
|---|---|
<repositories> | 找依赖时要敲的门 |
<pluginRepositories> | 找插件时要敲的门(和找依赖是两套独立通道) |
<properties> | 变量值(如 JDK 版本) |
为什么要装进袋子?因为 profile 可以开关:
- 直接写在 settings 顶层 → 永远生效,关不掉
- 装进 profile → 可以用
<activeProfiles>选择"激活"或"不激活"
<activeProfiles><activeProfile>default-profile</activeProfile></activeProfiles>这段的意思是:“把名叫default-profile的袋子打开,里面的东西现在生效。”
profile 的真正价值在于按条件激活:比如内网/外网用不同仓库、dev/prod 用不同配置。但很多场景下,它就是"把几条全局配置打包命名,顺便给本机所有项目兜底"。
三、pom.xml 里的关键配置
和仓库相关的有 3 块:
1.<repositories>(找依赖的门清单)
<repositories><repository><id>yum-public-repo</id><name>yum public repo</name><url>http://repo.hwwt2.com/repository/maven-public/</url></repository><repository><id>alimaven</id><name>aliyun maven</name><url>https://maven.aliyun.com/repository/central/</url></repository></repositories>这是一张**"找依赖要敲的门"清单**,Maven 会按声明顺序从上往下敲门。
每个<repository>三件信息:
<id>:门的身份证号(给 mirror 识别用)<name>:描述(给人看的)<url>:实际地址(可能被 mirror 改写)
2.<pluginRepositories>(找插件的门清单)
pom 里没写,但因为 settings profile 补了一条,所以找插件也能走公司仓库。
为什么单独配?因为 Maven 把"找依赖"和"找插件"当两套独立系统,<repositories>只管依赖。
3.<distributionManagement>(发布地址)
<distributionManagement><repository><id>yum-arch-releases</id><url>http://repo.hwwt2.com/repository/arch_maven_release/</url></repository><snapshotRepository><id>yum-arch-snapshots</id><url>http://repo.hwwt2.com/repository/arch_maven_snapshot/</url></snapshotRepository></distributionManagement>mvn deploy时把产物推到这里:
- 版本号无
SNAPSHOT→ 推到releases - 版本号带
SNAPSHOT→ 推到snapshots
⚠️mirror 不拦截上传请求,只拦截下载。所以mvn deploy直连 url,需要认证的话要在 settings 的<servers>里配账号密码。
四、它们怎么联动?合并 + 拦截
整个流程是两步流水线:
第一步:合并出"待敲门清单"
Maven 把以下来源的<repositories>合并去重,按顺序形成最终清单:
① pom.xml 的 <repositories> ← 项目级 ② settings.xml 激活 profile 里的 <repositories> ← 全局级 ③ 超级 POM 的 central ← Maven 内置兜底合并规则:按<id>去重,然后按顺序拼接。
比如:
settings profile 里的: yum-public-repo pom 里的: yum-public-repo, alimaven 合并后: yum-public-repo(去重), alimaven, central<pluginRepositories>和<properties>同理合并。同名 property,优先级:pom > settings profile。
第二步:mirror 对清单逐个改道
拿到合并后的清单后,mirror 对每一扇门做改道检查:
yum-public-repo → 命中 ! 豁免 → 直走 repo.hwwt2.com alimaven → 命中 * → 改道 maven.aliyun.com/public central → 命中 * → 改道 maven.aliyun.com/publicmirror 只改<url>,不改顺序、不减门数。
两步的本质区别
| 做什么 | 改顺序吗 | 改数量吗 | |
|---|---|---|---|
| profile + pom 的 repositories | 决定清单里有谁、什么顺序 | ✅ | ✅ 可增减 |
| mirror | 对清单里每扇门重写 url | ❌ | ❌ 只改 url |
一句话:profile 和 pom 一起"造清单",mirror 只负责"改清单上每扇门的实际地址"。
五、用一个完整流程串起来
假设要找依赖com.google.guava:guava(本地没有):
1. Maven 启动 ├─ 读 settings.xml ├─ 看到 profile default-profile → 检查是否激活 └─ <activeProfiles> 里有它 → 激活!袋子打开 → 全局上下文多了:repositories=[yum-public-repo] 2. 读项目 pom.xml ├─ 解析 <dependencies>,知道要找 guava ├─ 解析 <repositories> = [yum-public-repo, alimaven] └─ 合并 settings profile 的 repositories → 最终清单: [yum-public-repo, alimaven, central] 3. mirror 逐个改道: ├─ yum-public-repo → 命中 ! 豁免 → 直走 repo.hwwt2.com ├─ alimaven → 命中 * → 改道 maven.aliyun.com/public └─ central → 命中 * → 改道 maven.aliyun.com/public 4. 按顺序敲门: ├─ 本地柜子找 guava → 没有 ├─ 敲 yum-public-repo(repo.hwwt2.com) │ → 公司 Nexus 是个 group,代理了 central+aliyun │ → 有!✓ 拿走,结束 └─ (后面的门都不敲了)如果是公司的包(如com.yumchina.*),阿里云根本没有,只有公司仓库有——这就是为什么要配两扇门且公司仓库排第一。
六、常见疑问解答
Q1:为什么公司包只能从公司仓库找?
不是 Maven "判断"出来这是公司的包,而是只有公司仓库才有这种包。阿里云对com.yumchina.*的请求会返回 404,Maven 才会回退到下一个仓库。
Q2:为什么 pom 和 settings 里都写了 yum-public-repo?不冲突吗?
不冲突。合并时按 id 去重,最终只生效一份。功能上等价,profile 里那份相当于"本机兜底"(万一 pom 忘了写)。
一般做法二选一:
- 写在 settings → 本机所有项目自动可用,改地址不用动 pom
- 写在 pom → 跟着代码走,换机器/换人也保证可构建(更推荐团队协作)
Q3:mirror 为什么不直接写进 pom?
因为 mirror 解决的是完全不同的痛点:
| 痛点 | 谁解决 |
|---|---|
| “要去哪些仓库” | profile/pom |
| “某个仓库太慢/挂了,换个等效地址” | mirror |
mirror 的精髓:不用改 pom(pom 可能是共享的、不能随便动),只在本机 settings 里贴一张路牌,就能让所有项目访问 central 时都自动走阿里云。让本机网络优化和项目声明解耦。
Q4:mvn deploy会走 mirror 吗?
不会。mirror 只拦截下载请求,不拦截上传。mvn deploy直连distributionManagement里写的 url。
Q5:为什么找插件也要单独配<pluginRepositories>?
因为 Maven 把"找依赖"和"找插件"当两套独立系统。<repositories>只管依赖,要找插件必须配<pluginRepositories>。但实际写的时候,两个内容往往一样。
七、一张图总结
┌──────────────────────────────────────────────────┐ │ settings.xml (本机环境) │ │ ├─ localRepository → 决定缓存目录 │ │ ├─ mirrors → 下载时拦截改道 │ │ ├─ profiles → 补充仓库 + JDK 默认 │ │ └─ servers → deploy 认证 │ └──────────────────────────────────────────────────┘ │ 优先级低于 pom ▼ ┌──────────────────────────────────────────────────┐ │ pom.xml (项目) │ │ ├─ dependencies → 声明要拉哪些包 │ │ ├─ repositories → 声明去哪些仓库拉 │ │ ├─ build/plugins → 编译测试打包 │ │ └─ distributionManagement → 发布到哪 │ └──────────────────────────────────────────────────┘ 流程: settings profile ┐ ├─→ 合并出"待敲门清单" pom repositories ┘ │ ▼ mirror 逐个改道 url │ ▼ 按顺序敲门找依赖核心结论
- settings 管"本机环境",pom 管"项目声明",打架时听 pom 的。
- profile 是带开关的配置袋子,激活后内容和 pom 合并。
- mirror 是最后一道改道关卡,只改 url 不动清单,让网络优化和项目声明解耦。
- 找依赖靠 id 识别,id 必须一字不差才能被 mirror 精准拦截或豁免。
- 发布(deploy)和下载是两套独立通道,mirror 只管下载。
记住一句话:settings 是"你自己的习惯",pom 是"这个项目的说明书",mirror 是"去选定的店时走哪条路"。三者接力,谁也不抢谁的活。