☰
Maven settings.xml 完整配置手册(含私服、镜像、密码加密、profile 切换)
2026/10/10 16:29:47 网站建设 项目流程

Maven 用了这么多年,真正让我停下来认真读一遍 settings.xml 文档的契机,是去年公司换了一次 Nexus 私服地址。运维发了个新 settings.xml 让全员替换,结果我一拉代码——所有依赖全部 404。排查了半小时发现是新配置里 mirrorOf 写了 *,把项目 pom 里声明的第三方仓库也一并劫持到了新私服上,而新私服还没同步完。

这件事之后我意识到,settings.xml 这个文件大部分人只是"抄来能用就行",但对它的加载顺序、mirror 匹配规则、profile 激活机制其实一知半解。出了问题只能靠删文件重装来修。

这篇把 settings.xml 每个配置项讲清楚,重点放在镜像匹配规则、密码加密的完整操作流程、多环境 profile 切换这三个最容易出错的地方。所有示例基于 Maven 3.9.x,低版本(3.6+)行为一致。

文件位置与加载优先级

settings.xml 有两份,Maven 会合并加载:

级别路径作用域
Global${maven.home}/conf/settings.xml当前机器所有用户
User${user.home}/.m2/settings.xml当前用户

Windows 上 User 级别就是 C:\Users\<你>\.m2\settings.xml。

加载规则:User 级别优先。如果两边都定义了同一个 <server> 的 <id>,User 级别覆盖 Global。如果两边都定义了 <mirror>,则两份都会生效(不是覆盖关系),匹配时按声明顺序。

建议:日常开发只维护 User 级别的 ~/.m2/settings.xml,不要动 Maven 安装目录下的 conf。原因:升级 Maven 版本时 conf 会被覆盖,你的配置就丢了。

用命令确认 Maven 实际读的是哪个文件:

mvn --version # Maven home: D:\apache-maven-3.9.6 ← conf/settings.xml 在这下面 mvn help:effective-settings # 输出最终合并后的生效配置(最准)

基本结构

一份 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/> <interactiveMode/> <offline/> <pluginGroups/> <servers/> <mirrors/> <proxies/> <profiles/> <activeProfiles/> </settings>

下面逐个讲。

localRepository:本地仓库路径

<localRepository>D:/maven-repo</localRepository>

默认是 ~/.m2/repository。建议改到非系统盘,原因:

  • 一个中等 Java 项目跑几次 mvn clean install,仓库就能到 5~10 GB
  • 多项目累积下来 30~50 GB 很正常
  • C 盘满了会导致各种诡异问题(IDEA 索引失败、Git 操作报错等)

路径注意:Windows 上用正斜杠 / 或双反斜杠 \\,单反斜杠 \ 在 XML 里虽然大部分时候也能识别,但不保证所有 Maven 插件都兼容。

改完之后验证:

mvn help:evaluate -Dexpression=settings.localRepository -q -DforceStdout # 输出:D:/maven-repo

mirrors:镜像配置(重点)

基本语法

<mirrors> <mirror> <id>aliyun-public</id> <name>Aliyun Maven Mirror</name> <url>https://maven.aliyun.com/repository/public</url> <mirrorOf>central</mirrorOf> </mirror> </mirrors>

四个字段:

  • id:唯一标识,也是认证时关联 <server> 的 key(如果镜像需要登录)
  • name:描述,随便写
  • url:镜像地址
  • mirrorOf:匹配规则——决定哪些仓库的请求会被这个镜像拦截

mirrorOf 匹配规则(核心)

这是 settings.xml 里最容易出错的地方,我见过至少五种写法,行为完全不同:

mirrorOf 值含义使用场景
central只代理 Maven Central最安全,推荐
*代理所有仓库(包括 pom 里声明的第三方仓库)危险,容易劫持私服
external:*代理所有非 localhost、非 file:// 的仓库比 * 稍安全
repo1,repo2代理 id 为 repo1 或 repo2 的仓库精确控制
*,!company-nexus代理所有仓库,但排除id 为 company-nexus 的有私服时的推荐写法

血泪教训:如果你有公司私服,mirrorOf 千万不要用 *。否则 pom.xml 里声明的私服仓库请求会被镜像拦截,镜像上没有你公司的内部 jar,直接 404。

正确做法:

<mirror> <id>aliyun-public</id> <name>Aliyun Maven Mirror</name> <url>https://maven.aliyun.com/repository/public</url> <mirrorOf>central,!company-nexus</mirrorOf> </mirror>

或者更严格一点,只代理 central:

<mirrorOf>central</mirrorOf>

多镜像配置

可以配多个 mirror,Maven 按声明顺序匹配,第一个命中的生效:

<mirrors> <!-- 优先走公司内网镜像(速度快、有内部包) --> <mirror> <id>company-mirror</id> <url>https://nexus.company.com/repository/maven-public/</url> <mirrorOf>central</mirrorOf> </mirror> <!-- 公司镜像挂了或在外网时的备选 --> <mirror> <id>aliyun-public</id> <url>https://maven.aliyun.com/repository/public</url> <mirrorOf>central</mirrorOf> </mirror> </mirrors>

注意:同一个 mirrorOf 目标如果声明了多个 mirror,Maven 3.9+ 的行为是只用第一个匹配的,不会 fallback 到第二个(这跟 Docker 的 registry-mirrors 不一样)。想要 failover 效果,需要用 Maven 3.9 的 <blocked> 配合或者在 CI 里用脚本切换。

常用国内镜像地址(2026 年 9 月仍可用)

<!-- 阿里云(推荐,稳定) --> <url>https://maven.aliyun.com/repository/public</url> <!-- 华为云 --> <url>https://repo.huaweicloud.com/repository/maven/</url> <!-- 腾讯云 --> <url>https://mirrors.cloud.tencent.com/nexus/repository/maven-public/</url>

阿里云还有个 Spring 专用仓库,拉 Spring 的 snapshot 版时需要单独配:

<mirror> <id>aliyun-spring</id> <url>https://maven.aliyun.com/repository/spring</url> <mirrorOf>spring-milestones,spring-snapshots</mirrorOf> </mirror>

servers:认证配置(含密码加密)

基本用法

当你要往私服 deploy jar,或者访问需要登录的仓库时,需要配认证:

<servers> <server> <id>company-nexus</id> <username>zhangsan</username> <password>MyP@ssw0rd</password> </server> </servers>

id 必须跟 pom.xml 里 <distributionManagement> 的仓库 id,或者 settings.xml 里 <mirror> / <repository> 的 id完全一致(区分大小写)。不一致的话认证不生效,报 401 Unauthorized。

明文密码的问题

上面那种写法,密码是明文存在 ~/.m2/settings.xml 里的。问题:

  • 你 dotfiles 如果同步到 GitHub,密码就泄露了
  • 团队共用一台构建机时,所有人都能看到你的密码
  • 安全审计过不了

密码加密完整流程

Maven 提供了两层加密:master password加密server password。

第一步:创建 master password

mvn --encrypt-master-password 'YourMasterP@ss123'

输出类似:{Qe+3kF5bJ7mN2pR8sT1uV4wX6yZ0aB3cD5eF7gH9iJ0=}

把这个输出写到 ~/.m2/settings-security.xml(没有就新建):

<settingsSecurity> <master>{Qe+3kF5bJ7mN2pR8sT1uV4wX6yZ0aB3cD5eF7gH9iJ0=}</master> </settingsSecurity>

第二步:用 master password 加密 server password

mvn --encrypt-password 'MyP@ssw0rd'

输出类似:{aB1cD2eF3gH4iJ5kL6mN7oP8qR9sT0uV1wX2yZ3aB4c=}

第三步:把加密后的密码写到 settings.xml

<server> <id>company-nexus</id> <username>zhangsan</username> <password>{aB1cD2eF3gH4iJ5kL6mN7oP8qR9sT0uV1wX2yZ3aB4c=}</password> </server>

验证:

mvn deploy -DaltDeploymentRepository=company-nexus::default::https://nexus.company.com/repository/maven-releases/

不报 401 就说明认证通过了。

注意事项:

  • settings-security.xml 的权限建议设为只有当前用户可读(Linux/Mac:chmod 600 ~/.m2/settings-security.xml)
  • master password 忘了的话,删掉 settings-security.xml 重来就行,它不影响已经加密的密码(只要你不改 master)
  • 如果改了 master password,之前所有 --encrypt-password 生成的密文都要重新生成
  • CI 环境(Jenkins/GitLab CI)通常用环境变量注入而不是 settings-security.xml,后面 profile 那节会讲

proxies:代理配置

公司内网需要走 HTTP 代理才能出外网时配这个:

<proxies> <proxy> <id>company-proxy</id> <active>true</active> <protocol>http</protocol> <host>proxy.company.com</host> <port>8080</port> <username>zhangsan</username> <password>{加密后的密码}</password> <nonProxyHosts>*.company.com|localhost|127.0.0.1|192.168.*</nonProxyHosts> </proxy> </proxies>

nonProxyHosts 用 | 分隔,支持 * 通配。公司内网的私服地址一定要加进去,否则访问私服也走代理,绕一圈不说还可能鉴权失败。

大部分开发者不需要配这个——只有你的机器不能直连外网、必须通过代理服务器出网时才用。如果配了代理但拉包还是超时,先确认 nonProxyHosts 是不是漏了私服域名。

profiles + activeProfiles:多环境切换

为什么需要 profile

典型场景:

  • 公司内网用 Nexus 私服,在家用阿里云公共镜像
  • 开发环境用 snapshot 仓库(实时更新),发布环境只走 release 仓库
  • 不同项目用不同的 JDK 版本或编译参数
  • CI 和本地用不同的仓库配置

基本结构

<profiles> <profile> <id>company</id> <activation> <!-- 激活条件 --> </activation> <repositories> <!-- 依赖仓库 --> </repositories> <pluginRepositories> <!-- 插件仓库 --> </pluginRepositories> <properties> <!-- 自定义属性 --> </properties> </profile> </profiles> <activeProfiles> <activeProfile>company</activeProfile> </activeProfiles>

实战示例:公司内网 + 外网两套配置

<profiles> <!-- 公司内网环境 --> <profile> <id>office</id> <repositories> <repository> <id>company-nexus</id> <url>https://nexus.company.com/repository/maven-public/</url> <releases><enabled>true</enabled></releases> <snapshots><enabled>true</enabled><updatePolicy>always</updatePolicy></snapshots> </repository> </repositories> <pluginRepositories> <pluginRepository> <id>company-nexus</id> <url>https://nexus.company.com/repository/maven-public/</url> </pluginRepository> </pluginRepositories> </profile> <!-- 外网/家庭环境 --> <profile> <id>home</id> <repositories> <repository> <id>aliyun</id> <url>https://maven.aliyun.com/repository/public</url> <releases><enabled>true</enabled></releases> <snapshots><enabled>false</enabled></snapshots> </repository> </repositories> </profile> </profiles> <!-- 默认激活公司环境 --> <activeProfiles> <activeProfile>office</activeProfile> </activeProfiles>

切换方式:

方式一:改 <activeProfiles> 里的值(永久切换)

方式二:命令行临时指定:

# 临时用 home profile 构建 mvn clean package -P home # 临时禁用 office profile mvn clean package -P !office

方式三:用激活条件自动切换(后面讲)

自动激活:基于文件或环境变量

<profile> <id>ci</id> <activation> <!-- 当环境变量 CI=true 时自动激活 --> <property> <name>env.CI</name> <value>true</value> </property> </activation> <properties> <maven.test.skip>true</maven.test.skip> </properties> </profile>
<profile> <id>jdk17</id> <activation> <!-- 当 JDK 版本为 17 时自动激活 --> <jdk>17</jdk> </activation> <properties> <maven.compiler.release>17</maven.compiler.release> </properties> </profile>
<profile> <id>has-settings-file</id> <activation> <!-- 当某个文件存在时自动激活 --> <file> <exists>${user.home}/.company-vpn-active</exists> </file> </activation> <!-- 只有连了 VPN(创建了标记文件)才走内网仓库 --> </profile>

最后这个 file 激活的玩法挺实用:连上 VPN 后 touch ~/.company-vpn-active,断开后 rm ~/.company-vpn-active,Maven 自动切换仓库源,不用手动改 settings.xml。

用 properties 统一版本号

profile 里可以定义 properties,然后在 pom.xml 里用 ${xxx} 引用:

<profile> <id>dev</id> <properties> <deploy.env>dev</deploy.env> <spring.profiles.active>dev</spring.profiles.active> </properties> </profile> <profile> <id>prod</id> <properties> <deploy.env>prod</deploy.env> <spring.profiles.active>prod</spring.profiles.active> </properties> </profile>

配合 maven-resources-plugin 的 filtering 功能,可以在打包时把 application.yml 里的占位符替换成对应环境的值。不过现在 Spring Boot 项目更推荐用 spring.profiles.active 运行时切换,而不是编译期替换——编译期替换意味着每个环境要单独打一次包,容易出错。

完整示例:我目前在用的 settings.xml

下面是一份整合了上面所有内容的完整文件,改改 URL 和账号就能直接用:

<?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>D:/maven-repo</localRepository> <!-- 镜像:只代理 central,排除私服 --> <mirrors> <mirror> <id>aliyun-public</id> <name>Aliyun Maven Public</name> <url>https://maven.aliyun.com/repository/public</url> <mirrorOf>central,!company-nexus</mirrorOf> </mirror> </mirrors> <!-- 认证 --> <servers> <server> <id>company-nexus</id> <username>zhangsan</username> <password>{aB1cD2eF3gH4iJ5kL6mN7oP8qR9sT0uV1wX2yZ3aB4c=}</password> </server> <!-- deploy 用的认证,id 必须和 pom 的 distributionManagement 一致 --> <server> <id>nexus-releases</id> <username>zhangsan</username> <password>{aB1cD2eF3gH4iJ5kL6mN7oP8qR9sT0uV1wX2yZ3aB4c=}</password> </server> <server> <id>nexus-snapshots</id> <username>zhangsan</username> <password>{aB1cD2eF3gH4iJ5kL6mN7oP8qR9sT0uV1wX2yZ3aB4c=}</password> </server> </servers> <!-- Profile:公司环境 --> <profiles> <profile> <id>company</id> <repositories> <repository> <id>company-nexus</id> <url>https://nexus.company.com/repository/maven-public/</url> <releases><enabled>true</enabled></releases> <snapshots> <enabled>true</enabled> <updatePolicy>interval:5</updatePolicy> </snapshots> </repository> </repositories> <pluginRepositories> <pluginRepository> <id>company-nexus</id> <url>https://nexus.company.com/repository/maven-public/</url> </pluginRepository> </pluginRepositories> </profile> </profiles> <activeProfiles> <activeProfile>company</activeProfile> </activeProfiles> </settings>

<updatePolicy>interval:5</updatePolicy> 表示 snapshot 版本每 5 分钟检查一次更新,不会每次构建都去拉。开发阶段频繁改 snapshot 时可以改成 always,稳定后改成 daily。

常见问题排查

拉包报 404 Not Found:

先确认这个包在 Maven Central 上存不存在(去 https://search.maven.org 搜)。如果 Central 上没有、只在公司私服上有,那多半是 mirrorOf=* 把私服请求劫持到了公共镜像。改成 mirrorOf=central,!私服id。

拉包报 401 Unauthorized:

<server> 的 id 和仓库的 id 不匹配。用 mvn help:effective-settings 看最终生效的 server 列表,再跟 mvn dependency:resolve -X 输出的仓库 id 对比。

snapshot 版本拉不到最新:

Maven 默认对 snapshot 的更新策略是 daily——一天只检查一次。强制刷新:

mvn clean package -U

-U 参数强制更新所有 snapshot 依赖。

IDEA 和命令行行为不一致:

IDEA 可能用了自己 bundled 的 Maven 和另一份 settings.xml。检查:Settings → Build Tools → Maven → Maven home path 和 User settings file,确保跟命令行用的是同一套。

mvn deploy 报 400 Bad Request:

往 release 仓库重复 deploy 同一个版本号会被拒绝(Nexus 默认策略)。要么升版本号,要么在 Nexus 上把对应仓库的 Deployment Policy 改成 Allow redeploy(不推荐,release 版本应该不可变)。

下载速度慢:

跑 mvn dependency:resolve -X,看日志里实际请求的 URL 是不是你配的镜像地址。如果不是,说明 mirror 没生效——检查 mirrorOf 是否匹配了目标仓库的 id。

仓库路径含 _remote.repositories 文件导致本地 jar 不生效:

本地仓库每个目录下有个 _remote.repositories 文件,记录了 jar 是从哪个远程仓库下载的。如果你手动把 jar 复制到本地仓库,Maven 可能因为它不认这个来源而忽略它。解法:删掉那个目录下的 _remote.repositories,或者用 mvn install:install-file 正规安装:

mvn install:install-file \ -Dfile=path/to/your.jar \ -DgroupId=com.example \ -DartifactId=your-lib \ -Dversion=1.0.0 \ -Dpackaging=jar

小结

settings.xml 配置的核心逻辑就三件事:从哪里拉包(mirrors + repositories)、用什么身份拉(servers + 加密)、什么条件下走哪套配置(profiles + activation)。

日常开发建议只维护一份 User 级别的 ~/.m2/settings.xml,用 profile 做环境切换而不是维护多份文件。密码一定要加密,settings-security.xml 不要提交到版本控制。

如果你现在的 settings.xml 是从别人那里抄来的,花十分钟跑一下 mvn help:effective-settings,看看实际生效的配置是不是你以为的那样——很多时候问题就出在"以为配了但其实没生效"。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询