简介:面向 Java 开发者与 Maven 初学者的配置参考文件,重点梳理 settings.xml 在本地仓库、镜像加速、代理访问、服务器认证与多环境切换中的常见配置,帮助读者理解全局设置如何影响项目构建和依赖下载。压缩包内共 1 个 xml 文件,约 2KB,属于轻量级参考材料,内容以配置项说明和典型写法为主,便于对照修改自己的配置文件。已有 270 人学习,说明其对日常开发环境搭建具有一定参考价值。读者可借此理清镜像、代理、服务器、属性与多环境配置等模块的职责边界,掌握调整本地仓库路径、配置国内镜像、填写私有仓库账号密码、按环境激活配置等做法;也可把它当作排查依赖下载缓慢、代理不生效、配置被覆盖等问题的对照清单,适合需要快速建立配置认知并落到实际项目的开发者使用。
1. 同一份代码,两台机器两种结果
上个月帮人排查一个构建问题:同一份代码,同事机器上mvn clean package顺利通过,我这边从第三个依赖开始就报Could not resolve dependencies。换网络、重启 IDE、删本地仓库都试过,最后发现问题出在两份 Maven settings.xml 配置相互冲突:安装目录里的那份配置了内网镜像,用户目录里的那份又写了一个mirrorOf为*的备用镜像;两者叠加后,私服地址的请求全被分配到了公共仓库。这类问题几乎每个接手多环境项目的团队都会遇到。
这份资源围绕 Maven settings.xml 配置展开,内容包括文件位置、合并规则、mirrors、servers、profiles 三类核心配置块的写法,以及依赖解析链路的逐段拆解。它适合两类人:一类是刚接手项目、被依赖拉不下来卡住的新人;另一类是要为团队统一构建环境、维护 CI 脚本的老手。下面按从跑通到改写的顺序拆。
2. 加载链路与合并规则:一份 settings.xml 从磁盘到生效的全过程
Maven 有两套配置文件:pom.xml 描述项目本身,随代码提交;settings.xml 描述运行环境,跟着机器走。很多人把两者混着改,结果项目里能跑的配置换台机器就失效。要改对,先得知道 Maven 按什么顺序读文件、读完之后怎么合并。
2.1 三个层级的配置来源与优先级
启动时,Maven 会先确定两件事:全局配置在哪、用户配置在哪。默认情况下,全局配置在安装目录的conf/settings.xml,用户配置在~/.m2/settings.xml。这两份文件同时存在,而不是后者简单替换前者。
| 位置 | 谁维护 | 生效方式 | 常见误用 |
|---|---|---|---|
${maven.home}/conf/settings.xml | 安装包制作方、环境维护者 | 作为基础配置先被读入 | 在里面写个人账号密码 |
${user.home}/.m2/settings.xml | 开发者本人 | 在全局配置之上覆盖或追加 | 随手提交进 Git |
-s指定的路径 | CI 脚本、临时排查 | 整体替换用户级配置 | 以为只是追加 |
合并规则需要单独说明,它分成两类。一类是单值元素,比如localRepository、offline、interactiveMode,用户级配置里有值就用用户级的,没有才回落到全局。另一类是列表元素,比如mirrors、servers、proxies、pluginGroups、profiles、activeProfiles,两边的条目会被追加到一起,而不是谁覆盖谁。
提示:正因为它只是追加,两层配置里最好不要出现同一个
id。重复 id 的条目谁生效取决于合并顺序,排查时几乎看不出来,这是最容易埋雷的地方。
命令行参数还有两个容易混淆的地方。-s替换的是用户级文件,-gs替换的是全局文件。也就是说,如果你在 CI 里写了mvn -s ci-settings.xml,那么原来的~/.m2/settings.xml完全不会参与合并,但安装目录的全局配置仍然生效。很多人以为-s是「在原有基础上再补一份」,结果 CI 上的镜像列表和本地对不上,构建行为自然也不同。
2.2 目录结构与一份最小可用骨架
先看用户目录下的整体结构。这里路径不对、权限不对,后面配置写得再花也没用:
~/.m2/ ├── settings.xml # 用户级配置,主战场 ├── settings-security.xml # 主密码文件,密码加密时才会用到 └── repository/ # 本地仓库,默认位置一份能用的 settings.xml 不需要很长,把「仓库在哪里、走哪个镜像、认证信息是什么」三件事说清就够:
<?xml version="1.0" encoding="UTF-8"?> <settings xmlns="http://maven.apache.org/SETTINGS/1.2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.2.0 https://maven.apache.org/xsd/settings-1.2.0.xsd"> <!-- 本地仓库位置:磁盘紧张时改到数据盘,注意路径分隔符 --> <localRepository>/data/m2/repository</localRepository> <!-- 离线开关:内网且依赖已下全时可置 true,避免无谓的网络等待 --> <offline>false</offline> <!-- 镜像列表:拦截对远程仓库的请求,第一个匹配的生效 --> <mirrors> <mirror> <id>mirror-public</id> <name>内部公共镜像</name> <url>https://repo.example.internal/repository/public/</url> <!-- 只拦截所有非本地仓库的请求,不劫持 pom 里显式声明的私服 --> <mirrorOf>external:*,!repo-releases,!repo-snapshots</mirrorOf> </mirror> </mirrors> <!-- 认证信息:id 必须与 mirror 或 repository 的 id 严格一致 --> <servers> <server> <id>mirror-public</id> <username>${env.REPO_USER}</username> <password>${env.REPO_PASS}</password> </server> </servers> </settings>逐段解释。localRepository只有一个值,改到这里之后,命令行和 IDE 只要用同一份 settings 就会指向同一个目录;offline设成 true 时 Maven 完全不发起远程请求,缺包直接失败,适合在离线环境做发布前的校验。
mirrors里的mirrorOf是整个文件最关键的参数,值为external:*表示拦截所有非本机、非 file 协议的仓库请求,后面用!开头的 id 是排除项——被排除的仓库仍然走原始地址。这个写法能同时兼顾「公共依赖走镜像加速」和「私服依赖直连」两个需求。
servers里的id不能随便起,它既不是仓库地址也不是用户名,而是一个与mirror/repository的id对齐的标识符。对不上,Maven 就认为这次请求没有可用凭据,直接以匿名身份发出,最后表现成 401 或 403。
2.3 用 effective-settings 看合并后的真实结果
配置文件写完不等于理解了自己写的东西。想知道 Maven 眼里最终生效的配置是什么,最直接的办法是让它打印出来:
# 输出合并后的完整 settings,包含从全局继承的部分 mvn help:effective-settings # 临时验证一份候选配置,不影响本机已有文件 mvn help:effective-settings -s ./settings-draft.xml # 查看当前哪些 profile 被激活,用来排查"仓库没生效" mvn help:active-profileshelp:effective-settings的输出是合并结果,不是任何一份原始文件。它的价值在于把「继承来的」和「自己写的」放在一起看,镜像是不是被覆盖、localRepository最终指向哪里、activeProfiles里到底有哪几项,一目了然。我一般会把输出重定向到一个临时文件,用编辑器搜mirror和repository,比对着原始文件猜要快得多。
需要临时排查时可以加-X,启动日志的最前面几行会明确写出读了哪些 settings 文件:
mvn -X validate 2>&1 | head -n 30日志里通常能看到Reading user settings from ...和Reading global settings from ...两行。如果这两行指向的路径和你想的不是同一个,后面所有关于配置的讨论都是白费——先把这个路径修对,再谈内容。
还有一个隐蔽点:IDE 内置的 Maven 有一份自己的 settings 路径设置,默认可能不是~/.m2/settings.xml,也可能不是命令行用的那个 Maven 安装。命令行能构建、IDE 里满屏飘红,十有八九是这两条链路指向了不同文件。判断方法很简单,在 IDE 的 Maven 面板里看它显示的 settings 路径,和命令行-X日志里的路径对一遍。
3. 核心配置块拆解:mirrors、servers、profiles 三件套怎么写
settings.xml 的元素不少,但真正影响「拉不拉得到包」的只有三块:镜像决定请求发往哪里,认证决定对方认不认你,profile 决定除了地址之外还有哪些参数参与。剩下那些,多数是锦上添花。
3.1 mirrors:mirrorOf 的匹配顺序与通配符陷阱
mirrors的语义是「拦截」。Maven 在向某个远程仓库发起请求之前,会拿这个仓库的 id 去镜像列表里逐个比对mirrorOf,匹配上就用镜像的 URL 替换掉原地址。注意两个细节:一是按声明顺序取第一个匹配项,后面即使还有更精确的也不再考虑;二是匹配的是仓库 id,不是 URL。
<mirrors> <!-- 精确匹配放前面:只接管中央仓库 --> <mirror> <id>mirror-central</id> <url>https://repo.example.internal/repository/public/</url> <mirrorOf>central</mirrorOf> </mirror> <!-- 再放兜底:接管其余所有外部仓库,但放过两个私服 --> <mirror> <id>mirror-fallback</id> <url>https://repo.example.internal/repository/backup/</url> <mirrorOf>external:*,!repo-releases,!repo-snapshots</mirrorOf> </mirror> </mirrors>mirrorOf的取值有几种形态,写错一种就会连带影响整条链路:
| 取值 | 含义 | 适用场景 |
|---|---|---|
central | 只拦截 id 为 central 的仓库 | 只想加速公共依赖 |
* | 拦截所有仓库,包括 pom 里声明的私服 | 全内网离线环境,慎用 |
external:* | 拦截所有非本机、非 file 协议的仓库 | 通用兜底 |
repo-a,repo-b | 只拦截列出的 id | 多仓库精确控制 |
external:*,!repo-a | 兜底但排除 repo-a | 最常用的组合写法 |
*是最容易出错的一个。它连 pom.xml 里<repositories>声明的私服一起拦截,于是私服包被送到公共镜像去找,找得到是运气,找不到就是Could not find artifact ... in mirror-fallback。这个报错信息会把人往「依赖写错了」的方向带,实际是镜像抢了请求。
我在团队里推的写法固定是external:*,!<私服id>。既保留了兜底加速,又给私服留了直连通道。至于external:*里的 external 到底排除哪些,标准是:localhost、127.0.0.1以及file://协议的仓库不算外部,不会被拦截。做本地联调时这条规则很有用。
3.2 servers:认证信息绑定与密码加密
servers的结构简单,难点全在 id 对齐和密码管理。一个 server 条目只对 id 相同的 mirror 或 repository 生效,这个 id 是唯一的关联键:
<servers> <!-- 对应上面 mirror-public,供镜像认证使用 --> <server> <id>mirror-public</id> <username>deployer</username> <!-- 加密串以 {开头,由 mvn --encrypt-password 生成 --> <password>{kQ3sX1a9pB0vN7mR2tY6uI4oP8wE5rT1=}</password> </server> <!-- 对应 pom 里 repository 的 id,供发布使用 --> <server> <id>repo-releases</id> <username>${env.REPO_USER}</username> <password>${env.REPO_PASS}</password> </server> </servers>第一种写法用加密串,第二种用环境变量占位符。settings.xml 支持${env.XXX}和${user.home}这类属性插值,这一点常被忽略,但它是把配置安全地放进 CI 的关键——文件本身可以进版本库,凭据留在流水线的环境变量里。
加密串的生成需要两步:
# 第一步:生成主密码,写入 ~/.m2/settings-security.xml mvn --encrypt-master-password # 输出形如 {xxxxxx} 的串,手工保存到 settings-security.xml 的 <master> 节点 # 第二步:用主密码加密真实密码 mvn --encrypt-password # 交互输入明文密码,输出 {yyyyyy},粘贴进 server 的 <password>settings-security.xml的内容只有一个节点:
<settingsSecurity> <master>{主密码加密串}</master> </settingsSecurity>这里有个绕不开的问题:主密码文件本身是明文的,只是把「改密码要动 settings.xml」变成了「改密码要动两个文件」。它的真实价值在于,settings.xml 可以被复制、被同步、被误提交,而加密串离开主密码文件不可解。所以主密码文件必须放在用户目录、权限设成只有本人可读,绝不能跟 settings.xml 一起进仓库。
注意:改完密码忘了重新生成加密串,是认证类故障里最常见的来源。构建一直用旧凭据撞 401,报错信息里不会出现「你的密码过期了」这种提示,只会是干巴巴的 Unauthorized。
3.3 profiles + activeProfiles:仓库、属性、激活条件打包
profiles是 settings.xml 里唯一能把「仓库」「插件仓库」「属性」捆在一起的部分。它的典型用途是区分内网和外网两套环境,或者给不同项目组配不同的仓库地址。
<profiles> <profile> <id>corp</id> <!-- 激活条件:满足任意一个即生效,也可以用 activeByDefault --> <activation> <activeByDefault>false</activeByDefault> </activation> <properties> <!-- 自定义属性,可在 pom 里用 ${repo.env} 引用 --> <repo.env>corp</repo.env> </properties> <repositories> <repository> <id>repo-releases</id> <url>https://repo.example.internal/repository/releases/</url> <releases> <!-- 发布包每天检查一次更新,避免每次构建都打网络 --> <enabled>true</enabled> <updatePolicy>daily</updatePolicy> <checksumPolicy>warn</checksumPolicy> </releases> <snapshots> <enabled>false</enabled> </snapshots> </repository> <repository> <id>repo-snapshots</id> <url>https://repo.example.internal/repository/snapshots/</url> <releases> <enabled>false</enabled> </releases> <snapshots> <!-- 快照每次都检查,否则拉到的可能是几天前的包 --> <enabled>true</enabled> <updatePolicy>always</updatePolicy> </snapshots> </repository> </repositories> <pluginRepositories> <pluginRepository> <id>repo-releases</id> <url>https://repo.example.internal/repository/releases/</url> </pluginRepository> </pluginRepositories> </profile> </profiles> <activeProfiles> <activeProfile>corp</activeProfile> </activeProfiles>参数逐个说。updatePolicy有四种取值:always每次构建都检查、daily每天第一次检查、interval:X每 X 分钟检查一次、never从不检查。发布包用daily足够,快照包必须用always,否则同事刚推的 SNAPSHOT 你这边要等到第二天才能看见——这类「我明明推了」的争论,九成是这个参数没配对。
checksumPolicy控制校验和失败时的行为,warn只警告、fail直接失败、ignore忽略。内网仓库偶尔会有校验文件没同步过来的情况,用warn兼容性好一点;对外部依赖做安全审计的环境则应该设成fail。
releases和snapshots两个开关要成对写。只写<enabled>不写策略,Maven 会用默认的daily;两个都不写,默认是发布包启用、快照包禁用。很多「SNAPSHOT 依赖拉不到」的问题,根源就是 profile 里的 repository 没打开 snapshots。
activeProfiles是激活入口,可以和activation节点二选一。activeByDefault为 true 的 profile 在没有其他 profile 被显式激活时生效,一旦命令行用-P指定了别的 profile,它反而会失效——这个反直觉的规则我在文档里看过好几遍才记住。
3.4 proxies 与离线开关:内网构建的最后两块拼图
proxies处理的是「机器本身出不去网」的场景,和mirrors不是一回事:mirror 换的是仓库地址,proxy 换的是出口通道。
<proxies> <proxy> <id>http-proxy</id> <!-- 只对 http 生效时可写 http,https 走隧道时也写 http --> <active>true</active> <protocol>http</protocol> <host>proxy.example.internal</host> <port>8080</port> <!-- 内部域名不走代理,避免回环解析失败 --> <nonProxyHosts>*.example.internal|localhost|127.0.0.1</nonProxyHosts> </proxy> </proxies>nonProxyHosts用|分隔,支持*通配。内部仓库域名漏写进去,请求就会绕一圈公网再回来,通常是超时而不是报错,属于典型的「找不到原因就先等等」型故障。active设为 false 可以保留配置但临时禁用,做对比排查时很方便。
offline是单值元素,命令行用-o等价。它的语义是「不许发起任何远程请求」,缺包立刻失败。发布流水线上我会在最后打包阶段打开它,用来验证「本地仓库里的依赖是否完整」——如果离线能构建成功,说明依赖已经全部收集完成,产物是可复现的。
4. 依赖解析链路:一个坐标从声明到落盘的完整路径
配置写完,真正的考验是出问题时能不能定位。Maven 的依赖解析链路不算复杂,但每一段都有对应的日志和磁盘痕迹,读懂它们比反复改配置有效得多。
4.1 仓库查找顺序与日志逐行读法
解析一个坐标时,Maven 大致按下面的顺序走:
- 先看本地仓库
localRepository,路径下是否已有对应文件且元数据未过期; - 未命中则遍历当前生效的远程仓库列表,顺序上通常是 pom 里声明的
<repositories>与生效 profile 里的<repositories>合并后的结果; - 每个远程仓库在发请求前先过一遍
mirrors,匹配上就换成镜像地址; - 全部未命中则回落到内置的中央仓库,除非它也被镜像接管;
- 仍然找不到,构建失败并打印「在哪些仓库里找过」。
构建时看到的一段典型输出,逐行读下来信息量很大:
# 说明这一次请求最终发往哪里,已经过镜像替换 Downloading from mirror-public: https://repo.example.internal/repository/public/com/example/demo/1.2.0/demo-1.2.0.pom # 成功落盘 Downloaded from mirror-public: https://... (12 kB at 340 kB/s) # 认证失败:注意这里打印的是替换后的地址 [ERROR] Failed to execute goal ... : Could not transfer artifact com.example:demo:pom:1.2.0 from/to mirror-public (https://repo.example.internal/repository/public/): authentication failed for https://repo.example.internal/... # 找不到:会把搜过的仓库逐个列出,是定位问题最快的入口 [ERROR] The following artifacts could not be resolved: com.example:demo:jar:1.2.0 ...第一行里的mirror-public是镜像 id,不是原始仓库 id。这一点很关键:报错里出现的是镜像名,你要去mirrors里找它,而不是去 pom 里找。反过来,如果日志里出现的是私服 id 而请求却发到了公网地址,说明mirrorOf把它也接管了。
4.2 本地仓库布局:_remote.repositories 与 lastUpdated
本地仓库不是简单的一堆 jar,它记了来源。以com.example:demo:1.2.0为例:
~/.m2/repository/com/example/demo/1.2.0/ ├── demo-1.2.0.jar ├── demo-1.2.0.pom ├── _remote.repositories # 来源记录:这个包是从哪个仓库来的 ├── resolver-status.properties # 3.9 之后的解析状态 └── demo-1.2.0.jar.lastUpdated # 下载失败的时间戳,成功则不存在_remote.repositories的内容长这样:
# 格式:文件名>仓库id= demo-1.2.0.jar>repo-releases= demo-1.2.0.pom>repo-releases=Maven 用这个文件判断「当前这次解析,本地已有的包是不是来自允许的仓库」。如果仓库列表变了、包是从一个已经不在列表里的仓库下载的,它就可能拒绝复用,转而报「not available in the specified repository」。这个报错看起来像网络问题,实际是本地缓存和仓库定义对不上。
*.lastUpdated文件是失败缓存。同一个坐标在策略允许的间隔内不会重复尝试,你改了配置却看不见效果,很多时候就是被它挡住。删掉它,或者构建时加-U(强制检查更新),立刻能看到新行为。
还有一条历史包袱:如果从很老的版本迁过来,本地仓库里可能残留大量不带_remote.repositories的文件。这时可以启用「传统本地仓库」模式,忽略来源标记:
# 让解析器忽略 _remote.repositories 的来源校验 mvn -llr clean package-llr只是权宜之计,它把来源校验整体关掉,能解决眼前问题但掩盖了配置不一致。真正的修法是把仓库定义理清楚,让所有依赖来自一个明确、稳定的地址集合。
4.3 三条验证命令:dependency:tree、effective-pom、-X
配置改完,别急着跑全量构建,用下面三条命令分层验证,成本低、指向明确:
# 1) 看依赖树,确认有没有版本冲突、有没有解析到意外的版本 mvn dependency:tree -Dincludes=com.example:* # 2) 看合并后的 pom,确认 profile 里的仓库和属性确实进了项目 mvn help:effective-pom -Doutput=effective-pom.xml # 3) 看详细日志,确认请求实际发往了哪个地址 mvn -X dependency:resolve 2>&1 | grep -i "downloading from"dependency:tree的-Dincludes参数支持groupId:artifactId:version形式的通配,只输出关心的子树,比整棵树翻着看高效。看到omitted for conflict就说明有版本仲裁发生了,这时候要回头确认仲裁结果是不是你要的。
help:effective-pom输出的是合并后的结果,能直接验证「改 settings 里的 profile 到底有没有影响项目」。我见过的情况是 profile 写了但没激活,pom 里什么也没多出来,看这一条就能立刻定位。
grep "Downloading from"是我用得最多的一条。它把「你以为的地址」和「实际的地址」摆在同一个屏幕上,镜像配没配对、私服有没有被劫持,一行就清楚了。
5. 排查与避坑:五类典型翻车现场
下面这五条都是实际撞过的,按「现象 → 原因 → 解决」记录,方便对照。
5.1 认证与镜像相关的三类
第一条:加完镜像,私服依赖全 404
现象:配置镜像之前能拉到内部构件,配置之后报Could not find artifact com.example:xxx in mirror-public,同时公共依赖一切正常。
原因:mirrorOf写成了*,把 pom 里声明的私服仓库也拦截了,内部构件被送到公共镜像去找,自然找不到。
解决:改成external:*,!repo-releases,!repo-snapshots,把私服 id 显式排除。改完用mvn -X dependency:resolve | grep "Downloading from"确认私服请求确实回到了原地址。
第二条:认证一直失败,账号密码明明是对的
现象:authentication failed,但用同样的账号在浏览器里能登录仓库管理界面。
原因:servers里的id和mirror/repository的id不一致。Maven 按 id 关联凭据,对不上就当匿名请求发出。
解决:把两处 id 逐字符对齐,注意大小写和连字符。核对方法是在help:effective-settings的输出里搜索这个 id,看它是不是同时出现在mirrors和servers两个节点下。
第三条:密码换了,构建还在用旧密码
现象:运维那边已经重置了账号,本地构建仍然 401。
原因:settings.xml里存的是加密串,改密码后没有重新执行mvn --encrypt-password;或者主密码文件被换过,旧的加密串无法解密。
解决:两个都重新生成一遍,先--encrypt-master-password更新settings-security.xml,再--encrypt-password更新settings.xml。生成完用-X起一次构建确认能通过认证。
5.2 缓存与更新相关的两类
第四条:同事推的 SNAPSHOT,本地始终是旧的
现象:对方说已经发布了新版本,本地mvn clean package之后行为没变,本地仓库里的 jar 时间戳还是几天前的。
原因:快照仓库的updatePolicy是默认的daily,当天只检查一次;再加上*.lastUpdated的失败缓存,实际可能更久。
解决:短期用mvn -U强制检查更新;长期把快照仓库的updatePolicy改成always,发布包保持daily。彻底清理时删掉对应版本目录下的*.lastUpdated,再重新构建。
第五条:本地明明有包,却报 not available in the specified repository
现象:本地仓库里 jar 和 pom 都在,构建依然失败,报错里带not available in the specified repository。
原因:_remote.repositories记录的来源仓库 id 已经不在当前生效的仓库列表里(换过镜像、换过私服地址、从别人的机器拷了仓库),解析器认为这个包不可信。
解决:优先修配置,让仓库 id 稳定下来。应急时删掉该版本目录下的_remote.repositories,或者用-llr关掉来源校验。根治办法是团队统一仓库 id 命名,不要今天叫nexus明天叫mirror-public。
提示:这五条里,有四条的根因都落在「id 不一致」或「策略过严/过松」上,而不是网络。养成先看 id 对齐、再看
updatePolicy的顺序,排查会快很多。
6. 二次改造:把 settings.xml 做成可分发的团队模板
个人的 settings.xml 调通只是第一步,真正难的是让十台机器、三条流水线用同一套逻辑。我的做法是把它拆成「模板 + 变量」,模板进版本库,值从环境变量注入。
关键点是 settings.xml 支持${env.XXX}插值,所以凭据不需要写死在文件里:
<servers> <server> <!-- 仓库 id 统一为 repo-releases,全团队不許再改 --> <id>repo-releases</id> <username>${env.REPO_USER}</username> <password>${env.REPO_PASS}</password> </server> </servers> <mirrors> <mirror> <id>mirror-public</id> <!-- 镜像地址也走变量,测试环境和生产环境只换一个环境变量 --> <url>${env.REPO_MIRROR_URL}</url> <mirrorOf>external:*,!repo-releases,!repo-snapshots</mirrorOf> </mirror> </mirrors>配套的.mvn/maven.config可以把参数固化到项目里,这样每个人不用记命令:
# 放在项目根目录的 .mvn/maven.config,每行一个参数 -s ./build/settings-template.xml -Dstyle.color=always这两处配合起来的效果是:新人克隆代码后,只需要在 CI 或本机设置三个环境变量,构建行为就和老成员完全一致;镜像地址变更时改流水线变量,不用挨个通知改文件。
改造时有几个参数值得对照着定:
| 配置项 | 推荐值 | 理由 |
|---|---|---|
updatePolicy(releases) | daily | 发布包很少变,避免每次构建打网络 |
updatePolicy(snapshots) | always | 快照必须实时,不然天天扯皮 |
checksumPolicy | warn | 内网同步偶有延迟,硬失败影响面大 |
mirrorOf | external:*,!repo-releases,!repo-snapshots | 兜底加速且不劫持私服 |
| 本地仓库路径 | 通过-Dmaven.repo.local在 CI 指定 | 流水线每次用干净目录,排除缓存干扰 |
验证模板是否真的生效,我的固定动作是三步:先mvn help:effective-settings -s build/settings-template.xml看合并结果,再mvn -X dependency:resolve | grep "Downloading from"看实际请求地址,最后断开外网跑一次离线构建确认依赖完整。三步都过,这个模板才敢推到团队里。
踩过的坑是:一开始我把模板里的仓库 id 起得很随意,后来有同事本地已有的仓库缓存对不上,成批报「not available in the specified repository」,排查了一整个下午才发现只是 id 差了一个连字符。从那以后我每次改 settings 模板,都强制先在三台不同环境的机器上跑一遍上面那三步验证,再合并到主干。希望帮到你。
本文还有配套的精品资源,点击获取