
Maven 拉依赖慢几乎是每个在本地做 Java 开发的人都会撞上的问题。新拉一个项目第一次执行mvn clean package进度条像卡住了一样控制台不停输出Downloading from central动辄十几分钟乃至直接超时失败那种体验我到现在都记得。要解决这个问题最常规、最有效的办法就是给 Maven 配置国内镜像仓库。所谓镜像就是在我方网络环境下建立一个中央仓库的同步副本下载依赖时不再直接访问海外服务器而是走国内节点速度自然就上来了。这篇内容主要讲清楚镜像配置的前因后果以及真正可以抄作业的配置方案。从为什么慢、镜像怎么工作到配置文件在哪、mirror 节点怎么写、多镜像怎么搞再到配完以后常见的一堆坑我都会按自己实际用下来的经验顺序捋一遍。适合刚接触 Maven 的小白也适合那些配过镜像但总在某个边角问题上折腾半天的老手。1. 依赖下载慢的根源与镜像的工作方式1.1 卡点到底在哪请求都发去了海外Maven 默认的远程仓库只有一个就是官方中央仓库。官方仓本身是好的问题是它架设在海外网络环境下而我们的开发网络访问境外服务器时延迟高、丢包率也不低。下载一个小型依赖可能看不出什么但一个真实项目的依赖树往往有几百甚至上千个 jar 包每个 jar 还要连带下载 pom、校验和文件等累计起来的请求数量非常可观。我在帮朋友排查过一次一个 Spring Boot 聚合工程首次构建光是依赖就拉了近 400MB。从官方仓库拉速度经常掉到几十 KB/s中间还会整包超时重试。Maven 对失败的依赖下载有重试机制但每次重试都要重新握手、重新建立连接时间成本比想象中高得多。这不是单次请求慢的问题而是整个依赖获取链路在网络上被拖住了。而且还有一个容易被忽略的点构建工具在解析依赖时是按顺序检查仓库的不是所有 jar 并行下载。一旦碰到某个大体积依赖卡住后续依赖全部排队等待构建时间就会被无限拉长。因此替换仓库地址到国内节点等于把整个下载链路的地理位置搬到了自家门口效果几乎是立竿见影的。1.2 镜像到底做了什么请求转发与缓存同步镜像仓库的机制理解起来很简单你在 Maven 里声明一个 mirrorMaven 在对中央仓库发请求之前会先看有没有镜像配置有的话就直接把请求转向镜像地址。镜像服务器做的事情本质上就是定期把官方中央仓库的内容同步到自己的服务器上。你的下载请求到达镜像站后如果文件已经同步过就直接返回如果没有镜像会触发一次回源拉取。同步通常有延迟所以偶尔会出现某个刚发布的新版本在镜像上还找不到的情况这是正常现象等待同步即可。需要特别区分一下mirrors 和 repositories 是两个层面的东西。repositories 是告诉 Maven “我要用哪些仓库”mirrors 则是“当我访问某个仓库时实际走哪个地址”。镜像的优先级非常高只要 mirror 配置命中了目标仓库 idMaven 就不会再访问原始地址。理解这一点之后很多“配了不生效”“私服被劫持”之类的问题就有了解释。2. 动手之前先弄清配置文件和关键节点2.1 找对配置文件全局还是用户级Maven 的配置入口有两个安装目录下conf/settings.xml是全局配置对所有用户生效用户目录下~/.m2/settings.xml是用户级配置只对当前用户生效。如果两处都存在Maven 会把它们合并用户级配置覆盖全局配置。我的建议是优先配置用户级的~/.m2/settings.xml理由很实际不需要动系统安装目录也就不需要管理员权限而且换机器、换项目时只要把这一个文件拷贝走环境就跟着走了。很多人的 Maven 是解压版散落在某个不算规范的目录直接改conf/settings.xml也行但团队协作时容易被别人覆盖掉。还有一个高频坑尤其在使用集成开发环境时要注意IDE 内置了一套 Maven。比如 IntelliJ IDEA 默认使用的可能是它自带的 Maven而不是你在系统里单独安装的版本。如果你只改了系统 Maven 的配置文件而 IDE 里用的还是内置的那套那配置自然不生效。正确做法是在 IDE 的 Maven 设置页里把 Maven home 指向自己安装的目录同时确认 User settings file 路径指向~/.m2/settings.xml。2.2 settings.xml 里决定镜像生效的几个节点配置文件里和镜像直接相关的节点就一个mirrors。它下面可以有多个mirror每个mirror包含四段关键信息id镜像的唯一标识理论上随意但不能和其他仓库 id 重复。name镜像名称纯展示用。url镜像仓库的地址这是最关键的一段。mirrorOf声明这个镜像拦截哪些仓库支持通配符和排除规则。很多人配完不生效八成是mirrorOf写错了。它不是写着玩的它必须和你要拦截的仓库 id 匹配。官方中央仓库在 Maven 里默认的 id 就是central所以平时配置镜像用的就是mirrorOfcentral/mirrorOf。有的教程喜欢写mirrorOf*/mirrorOf意思是拦截所有仓库请求省事但也埋雷。如果项目里配置了公司内部的私有仓库*会把私有仓库也劫持到公共镜像上私有构件就会全部拉不到这个问题后面详细讲。3. 国内公共镜像的选择与取舍3.1 常见的几类镜像仓库对比国内可用的公共 Maven 镜像不少来源主要分两类一类是各大云厂商提供的公共仓库镜像一类是开源社区维护的镜像站。它们的共性是部署在国内节点带宽充足同步频率较高对绝大多数项目来说都够用。这里我没法直接给你写死某个具体截图上的地址因为镜像站偶尔会调整发布路径且不同服务商的仓库路径差异很大。一个通用规律是镜像地址通常以https://开头路径里会带maven或repository这样的标识比如常见的路径风格有/repository/maven-public/、/maven2/等。配置之前先去你选定的镜像站首页找到 Maven 仓库对应的 URL复制完整路径再填进去比凭记忆手写可靠得多。我把常见的几类镜像整理成了一张对照表方便你根据场景做选择镜像类型典型路径风格特点适合场景云厂商公共镜像/repository/maven-public/、/maven2/同步快、带宽大、稳定高个人开发、外网环境下的默认选择开源社区镜像/maven/更新频率尚可覆盖语言多轻量项目对版本及时性要求不高公司内网私有源/repository/maven-public/由内部团队维护可控性强团队协作、有私有构件、离线环境3.2 该不该同时配好几个镜像很多人在配置的时候有一种“多配几个更保险”的心态主页面上也不止一个镜像源就全部塞进mirrors列表里。这里要纠正一下Maven 对 mirror 的处理是顺序匹配而不是并发请求取最快结果。它会从上到下逐个找第一个能匹配当前仓库的 mirror然后使用它。如果第一个配置不稳定Maven 并不会自动切换到第二个去重新下载同一个依赖而是等请求失败后再试下一个这期间的超时等待反而更慢。所以我的结论是选一个质量最稳定的主镜像就够了不需要把三四个源全填进去。备用的需求可以通过 profile 切换来实现而不是靠多写 mirror 硬堆。至于私有源和公共源的关系也不适合用多个 mirror 去碰运气应该用仓库声明的方式做区分。4. 完整配置步骤与验证方法4.1 最小可用的镜像配置写法直接用文件覆盖的方式写一个最小配置?xml version1.0 encodingUTF-8? settings xmlnshttp://maven.apache.org/SETTINGS/1.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://maven.apache.org/SETTINGS/1.0.0 https://maven.apache.org/xsd/settings-1.0.0.xsd mirrors mirror idcn-mirror/id nameChina Mirror/name urlhttps://mirrors.cn-example.example/maven-public//url mirrorOfcentral/mirrorOf /mirror /mirrors /settings上面的url是示例地址实际使用时一定要去你选择的镜像站首页复制完整地址替换掉mirrors.cn-example.example这段及后续路径。不同云厂商的路径前缀不一样有的带repository有的直接就是根路径下照抄别人的地址很容易碰上 404。mirrorOf这里写成central也就是只拦截官方中央仓库的请求。这是我认为最不会出错的写法。外部私有仓库、其他公共仓库都不受影响以后项目里需要访问私有源时也不会有冲突。4.2 验证配置是否真正生效配置完成后不要急着跑完整构建先用一条命令验证mvn help:effective-settings这条命令会输出 Maven 合并所有配置之后的最终状态你要看的内容集中在mirrors节点确认里面显示的 URL 和你写的一致且mirrorOf是central。光看配置还不够再跑一次实际的依赖解析观察下载日志mvn dependency:resolve正常生效后日志里的Downloading from后面跟着的就不再是官方仓库地址而是你配置的镜像地址。这是最直观的验证方式。我第一次配镜像时日志里看起来还是官方仓库地址排查半天才发现是 IDE 用了内置 Maven压根没读我改的配置文件。另外本机已有的缓存可能会干扰判断如果某个依赖之前在默认仓库下已经下载成功Maven 就不会再发起下载请求也就看不到下载日志。此时可以先在项目里加一个新的、之前没用过的依赖再执行mvn dependency:resolve能更快看到新地址是否生效。4.3 快照依赖和 mirror 的关系快照版本带-SNAPSHOT后缀的版本在 Maven 里默认是关闭下载的除非仓库配置里显式开启。公共镜像一般会把中央仓库的快照内容也同步但很多项目并不依赖外部快照所以多数场景下不用关心。真正要注意的是如果你自己的组件发布到了私有仓库而私有仓库的快照需要走内部网络拉取此时你的 mirror 配置必须能够识别并跳过这个私有仓库。mirrorOf写成central就能实现这个效果这也是我坚持推荐central而不是*的核心原因之一。5. 进阶配置多镜像、环境切换与 mirrorOf 规则5.1 多镜像顺序与超时策略的正确用法先明确一个原则多 mirror 的初衷不是提高下载速度而是在主镜像不可用时提供降级通道。因此把最稳定的镜像写在前面备用镜像写在后面。举例如下mirrors mirror idprimary-mirror/id namePrimary Mirror/name urlhttps://mirrors.cn-example.example/maven-public//url mirrorOfcentral/mirrorOf /mirror mirror idbackup-mirror/id nameBackup Mirror/name urlhttps://mirrors.backup-example.example/repository/maven-public//url mirrorOfcentral/mirrorOf /mirror /mirrors这里有一个很容易踩的坑当第一个 mirror 连接超时时Maven 会等待超时结束后才能尝试下一个。如果你把备用镜像填在后面却把超时时间设得很长那备用镜像的优势基本体现不出来构建照样卡住。所以如果主镜像稳定性足够好我建议干脆不要配备用如果确实需要备用可以在调用 Maven 时增加超时参数控制失败切换的时间成本mvn clean package -Dmaven.wagon.http.connectionTimeout10000 -Dmaven.wagon.http.readTimeout30000这两个参数分别控制连接建立和读取数据的超时毫秒数实测下来能让失败切换快很多。有些项目用的是新版 Maven 自带的 resolver 传输参数名可能不一样但多数情况下这两个还是有效的。5.2 用 profile 区分内网与外网环境mirror 适合“所有项目统一走某个源”的简单场景。但真实团队环境往往复杂得多在家办公时希望走公共镜像回到公司内网又需要走内部仓库拉取私有构件。这种环境切换用 mirror 就不够灵活了改用 profile 是更合理的做法。在settings.xml里定义一个内网专用的 profile激活条件是activeByDefault然后通过命令行参数切换profiles profile idinternal/id repositories repository idinternal-repo/id urlhttp://nexus.internal.example/repository/maven-public//url releases enabledtrue/enabled /releases snapshots enabledtrue/enabled /snapshots /repository /repositories /profile /profiles activeProfiles activeProfileinternal/activeProfile /activeProfiles默认激活 internal走内网源需要切换公共源时用-P !internal关闭即可。这种方案的灵活性在于项目 POM 里还可以继续声明其他仓库而不受全局 mirror 影响构建行为的可解释性强很多。我在几家公司的实际项目里都用这个模式比来回改 mirror 省心得多。5.3 mirrorOf 完整规则会通配更要会排除mirrorOf的匹配规则比想象中灵活掌握之后可以精准控制镜像范围central只镜像中央仓库。*镜像所有仓库。external:*镜像除本地文件仓库外的所有外部仓库。repo1,repo2镜像指定的多个仓库。*,!internal镜像所有仓库但排除internal。最常用的就是第一种和最后一种。external:*配合公共镜像可以覆盖所有外部依赖来源适合纯公开开源项目的开发环境。但如果你同时维护私有索引就必须学会排除写法。比如公司 Nexus 仓库 id 是nexus-private那么可以这样mirror idcn-mirror/id nameChina Mirror/name urlhttps://mirrors.cn-example.example/maven-public//url mirrorOf*,!nexus-private/mirrorOf /mirror这样外网构件都走镜像私有构件还是从公司地址拉取两者互不干扰。6. 常见问题与排查实录6.1 配置了镜像但日志显示还在访问默认仓库这个问题的出现频率极高原因通常有三个。第一改的是全局配置但构建工具实际用的是内置 Maven 的配置前面提到过IDE 场景最典型。排查方法很简单在终端里直接执行mvn -v看 Maven 版本路径再确认 IDE 的 Maven home 指向一致。第二mirrorOf没写对。有人写了mirrorIdcentral/mirrorId这种不存在的节点或者把mirrorOf写成了别的仓库 id匹配不上自然不生效。检查时不光看 URL还要确认mirrorOf的值和日志里Downloading from后面的仓库标识一致。第三项目 POM 里显式声明了仓库且仓库 id 不是central。此时请求的是自定义仓库mirror 只拦了central请求就不会被接管。可以通过mvn help:effective-pom查看项目最终生效的仓库 id再调整mirrorOf。6.2 配置 mirror 后公司私服的依赖全部拉不到这基本就是mirrorOf写成*造成的。比如项目在 POM 里声明了公司私服id 是company-repo而全局 mirror 是mirrorOf *所有仓库请求都会被转向镜像地址。私服上的私有构件在镜像站上当然不存在构建就会报错。解决方法是把mirrorOf改为排除式写法*,!company-repo。还有一种更保守的做法干脆只写central让私服完全不受镜像影响。我在真实项目中普遍采用排除写法因为它兼顾了公共依赖的加速和私有依赖的直达。6.3 下载速度依旧很慢甚至总是超时镜像配好了速度还是有瓶颈就要分情况排查。先确认是否为某个大体积依赖首次下载首次下载无法避免全量传输速度再快也有物理上限。接着检查镜像站本身是不是高峰期拥堵。公共镜像在技术社区集中讨论的热门版本发布日经常有一波明显的下载高峰这时候换一个备用源试试能快速定位是不是源的问题。还有一个细节本机缓存目录可能已经存在损坏的半成品文件比如上次下载中断留下的.part文件这会导致 Maven 反复校验失败。排查思路是找到~/.m2/repository下对应依赖的目录删掉出问题的那一组目录重新下载。6.4 配了 repositories 却总感觉 mirror 在“抢”如果项目 POM 里声明了多个 repository而全局 mirror 用了external:*之类的宽匹配就会把 POM 里手动声明的仓库也镜像到公共源。这在某些场景下是好事在有些场景下是灾难。我遇到过一种情况项目 POM 里声明了一个第三方仓库地址在国外但访问极不稳定配了external:*之后把请求全部指向国内镜像反而变稳定了。而另一个场景下项目依赖的某个构件只有 POM 声明仓库里有镜像站根本不同步这时候宽匹配就导致依赖拉不到。所以 mirrorOf 的宽度要根据项目的实际依赖来源来决定不是越大越好。7. 写在后头的经验之谈Mirror 配置本身不难真正难的是理解 Maven 处理仓库请求的顺序和匹配逻辑。我自己最早配置时就是抄了一段mirrorOf为*的配置结果本地开发没问题一接公司项目就翻车私服的包全部拉不到。被这个问题折腾过之后我才开始仔细理清 mirror、repository、profile 三者的区别后面再也没在这类问题上踩过坑。如果你只想要一个稳妥的起步方案我的建议是一个公共镜像加mirrorOfcentral就能覆盖大半需求等以后有内网私有构件、多环境切换这类需求时再把 profile 和排除写法用起来一步一步演进比一开始就堆一堆配置更不容易出错。配置文件改完先跑help:effective-settings确认效果这个习惯能帮你过滤掉一大半配置不生效的问题值得坚持。