1. 这个报错到底在说什么:先看懂Maven的“依赖寻址”规则
如果你在命令行里执行 mvn clean package,最终看到一排类似的输出:
code复制[ERROR] Failed to execute goal on project demo-service: Could not resolve dependencies for project com.example:demo-service:jar:1.0.0: The following artifacts could not be resolved: org.apache.commons:commons-lang3:jar:3.12.0 (central): Failure to transfer org.apache.commons:commons-lang3:jar:3.12.0 from https://repo.maven.apache.org/maven2 was cached in the local repository. Resolution will not be reattempted until the update interval of central has elapsed or updates are forced.
这一大段英文,实际上只传达了三层意思:
- Maven 在解析
org.apache.commons:commons-lang3:3.12.0这个依赖的时候失败了。 - 它尝试从某个远程仓库下载,但没有成功。
- 本地仓库里缓存了一个“失败标记”,在指定的更新间隔内,Maven 不会再次尝试下载。
很多人第一反应是“网络断了”,于是拼命检查网线。但根据我这几年带项目、帮同事排错的经历,这个报错的原因远不止网络一种。真正要解决它,你先得搞清楚 Maven 的依赖寻址流程。
Maven 找依赖,永远是 本地仓库优先,找不到才去远程仓库拉。本地仓库默认在用户目录下的 .m2/repository 文件夹里,例如 Windows 下是 C:\Users\你的用户名\.m2\repository,Linux/macOS 下是 ~/.m2/repository。
它的寻址逻辑其实非常“死板”:根据依赖的 groupId、artifactId、version 生成一个相对路径,然后去这个路径下找对应的 jar 文件。比如上面那个依赖,Maven 会去找 ~/.m2/repository/org/apache/commons/commons-lang3/3.12.0/commons-lang3-3.12.0.jar。
如果这个 jar 不存在,它就根据 settings.xml 里配置的镜像地址去远程仓库下载。下载成功,jar 放入本地仓库,后续编译打包都从本地读;下载失败,它会在 jar 旁边生成一个 .lastUpdated 后缀的标记文件,记录这次失败。下次构建时,Maven 发现这个标记文件还没过期,就直接拒绝再次下载。
注意:很多人看到
.lastUpdated文件不知道是什么,其实那是 Maven 的“失败记忆”。删掉它,Maven 才会重新尝试下载。这个后面我会专门讲。
所以,遇到 The following artifacts could not be resolved,你脑子里要立刻浮现这张“地图”:本地仓库有没有 → 远程仓库能不能连 → 下载的文件对不对 → 坐标到底存不存在。这条链路捋清楚,排查效率会高得多。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 最常见的“元凶”:网络、镜像与仓库配置
我统计过自己项目里遇到的这个报错,超过六成是 网络和仓库配置 问题。尤其是公司内部网络环境、刚入职新项目、或者换了一台新电脑的时候,最容易踩坑。
2.1 中央仓库被墙,换镜像最省事
Maven 默认的中央仓库地址是 https://repo.maven.apache.org/maven2。如果你的网络访问这个地址很慢或者干脆超时,依赖下载就会失败,然后报出文章开头那条错误。
解决方案是配置国内镜像。目前用得最多的是阿里云镜像,修改 ~/.m2/settings.xml,在 <mirrors> 节点里加这么一段:
xml复制<mirror>
<id>aliyunmaven</id>
<mirrorOf>central</mirrorOf>
<name>阿里云公共仓库</name>
<url>https://maven.aliyun.com/repository/public</url>
</mirror>
注意 <mirrorOf>central</mirrorOf> 表示这个镜像只拦截中央仓库的请求,不影响你配置的其他仓库。我见过有人图省事,写成 <mirrorOf>*</mirrorOf>,结果私服里的内部依赖也全被镜像拦走,导致更奇怪的报错。
2.2 私服配置的坑:id 对不上,认证全白搭
如果你是公司项目,通常会有内部的 Nexus 或 Artifactory 私服。这时候在 pom.xml 或 settings.xml 里配置 <repository>:
xml复制<repository>
<id>internal-repo</id>
<url>http://nexus.company.com/repository/maven-public/</url>
</repository>
同时要在 settings.xml 里配置服务器认证信息:
xml复制<server>
<id>internal-repo</id>
<username>yourname</username>
<password>yourpassword</password>
</server>
这里最坑的一点:<server> 里的 id 必须和 <repository> 里的 id 完全一致。Maven 靠这个 id 匹配认证信息,字母大小写、拼写有一点差异,它都不会带上凭据去访问私服。私服返回 401 或 403,Maven 就会报依赖无法解析。
我帮一个同事排查过这个问题,他在 pom.xml 里写的 id 是 nexus,settings.xml 里写的 id 是 Nexus,就这么一个大小写差异,折腾了两个小时。
2.3 多个镜像重叠导致“莫名其妙”的失败
如果你在 settings.xml 里配置了多个镜像,又没有做好 mirrorOf 的区分,它们会互相干扰。比如一个镜像拦截了 *,另一个镜像想拦截 central,结果第二个永远不生效。更糟的情况是,某个镜像本身不稳定,它把请求接过去,但下载下来的文件是损坏的,Maven 校验 checksum 失败,也会报依赖解析错误。
我的建议是:梳理清楚镜像规则。常规做法是只保留一个 mirrorOf 为 central 的镜像,公司有私服的话,再单独配置 repository,让私服处理走私服,中央仓库走镜像,两者互不干涉。
2.4 一个容易忽略的细节:settings.xml 生效位置
很多新手在 IDE 里改了 settings.xml,却发现不生效。这是因为 Maven 有用户级 settings 和全局 settings 两层。全局的在 Maven 安装目录下的 conf/settings.xml,用户级的是 ~/.m2/settings.xml。两者都存在时,用户级的配置覆盖全局配置。
如果你在 IDE 里配置了自定义的 Maven 路径,但它指向的 settings.xml 不是你正在改的那个文件,那你的修改自然不起作用。排查的时候,先用命令行执行 mvn help:effective-settings 看实际生效的配置是什么,再动手改文件。
3. 坐标正确但依然报错:传递依赖、版本冲突与快照陷阱
网络和仓库都确认没问题,错误还是报,那就要把注意力从“环境和配置”转移到“坐标本身”上。Maven 报错信息里会列出具体的 groupId:artifactId:version,很多时候问题就藏在这个坐标里。
3.1 版本号根本不存在
最常见的是版本号写错。比如 commons-lang3 根本没有 3.10 这个版本,实际最新的是 3.12.0。你一旦写了不存在的版本,无论怎么换镜像、怎么清理,都下载不到——因为远程仓库里压根没有这个东西。
判断方法很简单:在浏览器或命令行里直接访问仓库地址,看看那个版本的 jar 是否存在。比如:
code复制https://repo.maven.apache.org/maven2/org/apache/commons/commons-lang3/3.10/commons-lang3-3.10.jar
如果返回 404,那就是版本不存在,去 Maven 中央仓库 查一下可用的版本号,改掉依赖声明。
3.2 版本冲突:谁偷偷引用了不存在的版本?
这是排查最耗时的情况。你以为自己的依赖没问题,但 A 库内部引用了 B 库的某个版本,而那个版本在你访问的仓库里不存在,或者被标记为 optional、被 <exclusions> 排除了,最终就会导致解析失败。
要定位这个问题,用依赖树分析命令:
bash复制mvn dependency:tree -Dverbose
输出会展示所有依赖的完整传递关系。重点看报错那个坐标是从哪条路径引入的,找到它,你就能判断是修改根依赖的版本,还是直接在 pom.xml 里对这个传递依赖做 <exclusion>。
3.3 快照版本的“过期缓存”问题
如果你用到的依赖是公司内部发布的快照版本,比如 1.0.0-SNAPSHOT,它有一个特点:版本号末尾带 -SNAPSHOT,意味着这个版本会持续更新。Maven 默认对快照版本有一个更新策略,例如每天检查一次,或者每次构建检查。
问题来了:如果某个快照在私服上已经被重新发布过,但你本地仓库里缓存的是旧版,或者缓存了一条失败的下载记录,Maven 会在“更新间隔”内一直用旧的失败信息,导致你反复构建都报同样的错。
解决方法是在构建命令后面加 -U 参数,强制检查更新:
bash复制mvn clean package -U
这个参数的作用是告诉 Maven:“别管本地缓存了,强制去远程仓库重新拉取快照。”
我自己的习惯是:只要涉及快照依赖的构建,一律带上 -U,省得被缓存坑。
3.4 scope 和 optional 的隐性影响
还有一种不太容易被发现的坑:某个依赖被声明为 <scope>provided</scope> 或 <scope>test</scope>,在特定打包阶段不会被引入。如果你的代码里直接引用了这个依赖的类,编译期可能正常,但打包时如果另一个模块需要它,而它又没有进入最终的依赖集合,Maven 解析就会失败。
判断思路:检查报错坐标的 scope 和 optional 属性,确认它们是否和你当前的构建生命周期匹配。比如 provided 的依赖,在 package 阶段通常不打包进去,但如果其他模块需要解析它,就不能随便删。
4. 实战排查链路:从命令行把问题“揪”出来
很多同学一遇到这个报错,就在 IDE 里反复点 “Refresh”、“Reimport”,或者把 .m2/repository 整个删掉重来。这些做法不是不行,但效率太低。我建议按下面的排查链路走一遍,通常几分钟就能定位根因。
4.1 第一步:开启调试日志,看 Maven 到底访问了谁
执行:
bash复制mvn clean package -X
-X 是 debug 模式,会输出海量日志。不要被刷屏吓到,用搜索功能直接找报错的关键词,或者找 Downloading、Downloaded 开头的日志。重点看:
- 它尝试访问了哪个仓库地址?
- 是不是你预期的镜像或私服?
- 有没有 401、403、404、Connection timed out 之类的关键状态?
比如你看到日志里写 Downloading from central: https://repo.maven.apache.org/maven2/...,但你觉得应该走私服,那就说明镜像或仓库的匹配规则有问题。
4.2 第二步:手动验证仓库地址可达性
用浏览器或者 curl 访问日志里出现的 URL,看看能不能正常下载:
bash复制curl -I https://maven.aliyun.com/repository/public/org/apache/commons/commons-lang3/3.12.0/commons-lang3-3.12.0.jar
如果返回 200,说明仓库本身没问题,问题可能出在本地缓存或认证。如果返回超时或 5xx,说明仓库不可达,检查网络、代理或仓库服务状态。
4.3 第三步:检查 .lastUpdated 文件,清掉失败记忆
进入本地仓库对应依赖的目录,看看有没有 .lastUpdated 后缀的文件:
bash复制find ~/.m2/repository -name "*.lastUpdated" -type f
比如某个依赖路径下出现了 commons-lang3-3.12.0.jar.lastUpdated,说明之前有一次失败的下载记录被缓存了。Maven 默认在一段时间内不会重新尝试下载,这就会导致“明明网络已经恢复正常,但还是报同样的错”。
最直接的处理方式是删掉这些标记文件,或者干脆删掉整个依赖目录,让 Maven 重新下载:
bash复制rm -rf ~/.m2/repository/org/apache/commons/commons-lang3/3.12.0
构建时再加 -U 强制更新。注意,不要一上来就删整个 .m2/repository,那样会把所有依赖都重新下载一遍,耗时很长,而且小概率引发其他问题。
4.4 第四步:用 dependency:get 单独验证某个依赖
如果你不确定某个坐标到底能不能下载成功,可以用 Maven 的 dependency:get 命令直接测试:
bash复制mvn dependency:get -Dartifact=org.apache.commons:commons-lang3:3.12.0
这个命令会脱离项目,单独拉取指定的依赖。成功则说明坐标和环境都没问题,失败则错误信息更聚焦,方便继续排查。
4.5 排查链路对照表
| 现象 | 可能原因 | 优先操作 |
|---|---|---|
错误信息里出现 central,且访问地址是中央仓库 |
镜像未配置或镜像失效 | 配置镜像,或检查镜像地址 |
| 错误信息里出现 401/403 | 私服认证失败 | 检查 server 的 id 是否匹配,密码是否正确 |
| 出现 404 | 坐标不存在或路径不对 | 查 Maven 中央仓库,确认版本号 |
Connection timed out |
网络不通,或防火墙拦截 | 检查网络、代理、仓库地址 |
本地有 .lastUpdated 文件 |
历史失败缓存 | 删除标记文件,加 -U 重新构建 |
5. 万不得已的“重拳”:清理本地仓库与依赖仲裁
按上面链路排查完,大部分问题都能解决。但如果遇到特别顽固的情况,比如本地仓库的 jar 文件损坏、某些依赖版本被恶意污染,你可能需要“动手术”。
5.1 针对单个依赖的精准清理
不要动整个仓库,先精准删除出问题的依赖目录。路径规则就是前面提到的坐标映射:
bash复制rm -rf ~/.m2/repository/org/springframework/boot/spring-boot-starter-parent/2.7.0
删完后再构建,Maven 会重新从远程拉取。这种方式对“jar 包损坏但 Maven 没识别出来”的情况特别有效。
5.2 我为什么不建议直接删整个 .m2/repository
网上很多教程遇到依赖问题就让人把整个 .m2 删掉。我特别不推荐,原因有三个:
- 重新下载所有依赖可能要几十分钟,浪费时间。
- 如果某些依赖只在公司私服上存在,而私服暂时不可用,删完连之前的“能用版本”都没了。
- 删除过程中如果有 IDE 正在使用该仓库,你还会遇到文件锁定、项目彻底无法构建的尴尬。
除非你是刚搭好的开发环境,总共就那么几个依赖,否则不要用这种“核弹级”方案。你可以考虑把原来的仓库改名备份,而不是删除:
bash复制mv ~/.m2/repository ~/.m2/repository_backup
这样如果新仓库还是有问题,你随时可以改回来,不至于把自己逼到绝路。
5.3 手动下载并安装 jar 到本地仓库
有些特殊依赖,远程仓库确实没有,但你能通过其他途径拿到 jar 文件。这时候可以用 Maven 的 install-file 命令手动安装:
bash复制mvn install:install-file -Dfile=path/to/your.jar -DgroupId=com.example -DartifactId=your-lib -Dversion=1.0.0 -Dpackaging=jar
安装完成后,这个 jar 就存在于本地仓库了,Maven 解析时就能直接命中。这个方法在引入内部专用包、老版本遗留 jar 时非常实用。
不过要提醒一句:手动安装的 jar 只对你自己有效,其他同事仍然会报错。如果这个依赖是团队共用的,应该上传到公司私服,而不是让大家各自手动安装。
5.4 调整版本号,用“能用的”替代“想要的”
有些报错,不是依赖不存在,而是你选择的版本和项目里其他依赖存在不兼容的传递关系,Maven 根本解析不出一个合理的版本集合。比如 spring-boot-starter-parent 父 POM 里锁定了某个依赖版本,你的子模块又单独声明了另一个版本,两边冲突。
碰到这种情况,我通常先执行 mvn dependency:tree 看冲突路径,然后决定是改用父 POM 建议的版本,还是对某个传递依赖做 <exclusion>。优先选择“跟随父 POM 的版本管理”,因为 Spring Boot 这样的框架已经针对版本组合做了完整测试,你强行覆盖反而容易引入新问题。
6. 多模块项目里的特殊场景:reactor 解析与父 POM
如果你是在一个多模块项目里遇到 The following artifacts could not be resolved,情况会稍微复杂一些。因为 Maven 在处理多模块构建时,有一个“reactor”机制:它会在内存里尝试解析模块之间的依赖,而不是每个模块都直接从远程仓库下载。
6.1 报错 Non-resolvable import POM 是怎么回事
很多项目会有一个“父 POM”,子模块通过 <parent> 引用它。父 POM 本身可能又引入了 BOM(Bill of Materials)文件,用来统一管理依赖版本。如果父 POM 或 BOM 没能被解析成功,就会出现 Non-resolvable import POM: The following artifacts could not be resolved 这类错误。
常见原因有两个:
- 父 POM 没有先安装到本地仓库,直接对子模块单独执行
mvn package,Maven 在本地仓库和远程仓库都找不到父 POM。 - 父 POM 本身依赖了某个自定义 BOM,而这个 BOM 引用了不存在的属性或版本变量。
6.2 秘诀:先 install 父模块,或用 -pl/-am 组合
多模块项目构建时,最稳妥的命令是在项目根目录执行:
bash复制mvn clean install -pl your-module -am
其中 -pl 指定你要构建的模块,-am(also make)表示同时构建它依赖的其他模块。这样 Maven 会先把依赖链上的模块都编译打包好,并写入本地仓库,然后再构建你要的模块。
如果你直接进到子模块目录里单独执行 mvn package,Maven 默认找不到同项目的其他模块,只能去远程仓库找,而远程仓库里又不可能有你本地尚未发布的模块,于是报错。
6.3 父 POM 版本变量的坑
还有一种情况比较隐蔽:父 POM 里用属性(property)定义版本号,比如:
xml复制<properties>
<my.dependency.version>1.0.0</my.dependency.version>
</properties>
但如果子模块在解析时,这个属性没有被正确继承或者名字拼错了,Maven 会把这个变量当成一个“字面值”去寻址,最终请求一个类似 1.0.0-${my.dependency.version} 的路径,结果当然是 404。
遇到这种报错,检查父 POM 的属性定义和子模块的引用之间是否完全一致。IDE 的自动提示有时候会忽略这一点,手写时尤其容易出错。
7. 依赖解析少出幺蛾子的几个习惯:团队协作与日常维护
文章最后,我想分享几个能让你和团队少踩这个坑的习惯。Maven 这个报错看着吓人,但真正花时间的地方往往不是修复本身,而是“为什么我改了还是不对”“为什么同事没问题就我有问题”这类环境差异。
7.1 统一团队的 settings.xml
我见过很多团队,代码库管理得井井有条,但每个开发者的 Maven 配置完全靠个人自觉。结果就是 A 同事的机器上配了公司私服,B 同事没配,C 同事的镜像地址抄了一个别人的博客,版本还对不上。一旦新同事入职,第一件事就是和这个报错搏斗。
建议把一份验证过的 settings.xml 放进项目仓库的 docs 目录或者团队 Wiki 里,包含:
- 私服地址和对应的 server 认证信息(账号密码用占位符,让每个人填自己的)
- 镜像配置
- 本地仓库路径
同时约定:优先使用开发环境统一的 Maven 版本,避免高版本和低版本之间解析行为差异导致的怪问题。
7.2 别用 LATEST 和 RELEASE
Maven 支持用 LATEST 和 RELEASE 作为版本号,表示“自动选择最新版”。这听起来很方便,但实际是“定时炸弹”:每次构建时,Maven 都要去远程仓库检查最新版本,一旦某个版本被删掉或仓库故障,解析就会失败。更糟糕的是,不同时间构建出来的产物版本不一致,隔一段时间你再打包,发现依赖的 API 悄悄变了,代码直接编译失败。
所以,所有依赖版本都应该在 pom.xml 里显式写死,或者统一由父 POM / BOM 管理。锁定版本是依赖管理的基本素养。
7.3 构建时习惯性带上 -U
凡是涉及内部快照依赖,或者刚从私服拉取过新版本,构建命令我都建议加 -U。虽然会多花一点检查更新的事件,但能避免很多“缓存过期”导致的诡异报错。尤其是在 CI(持续集成)环境里,每次构建都应该是从干净状态出发的,-U 能保证及时同步快照变更。
我在本地开发机上的习惯是:日常增量编译不加 -U,但发布前、打包前、切换分支后,一定会用 mvn clean package -U 来一次“干净构建”。
7.4 项目里引入 Maven Wrapper
如果你的项目值得维护,可以考虑引入 Maven Wrapper(mvnw),它把 Maven 的版本固定下来,团队成员不必各自安装 Maven,只要执行 ./mvnw 就会自动使用项目指定的 Maven 版本。这能直接消除“我本机 Maven 版本和 CI 不一致”这类问题。
我经历过一次非常耗时的排查:项目在 CI 上用 Maven 3.8 构建正常,本地用 Maven 3.5 一直报错,后来发现是 Maven 3.5 对某个依赖解析的 bug 导致的。引入 Maven Wrapper 之后,这类问题彻底消失。
7.5 遇到问题先记录,再动手
最后一个小习惯:当你花了很多时间解决一个依赖解析问题,我强烈建议把错误信息、根因、解决步骤记到团队文档里。Maven 的报错往往大同小异,但每个团队的私服、镜像、依赖组合都不一样。你今天踩过的坑,未来某个新同事或者三个月后的自己,大概率还会再踩一次。留一份排查笔记,比等别人再问你一遍要高效得多。
我个人踩过最深的坑是私服仓库图省事把镜像和私有仓库混在一个 <repositories> 里,导致每次构建都从错误的仓库源拉取依赖最终解析失败。后来我把仓库职责拆分开,内部依赖走私服,第三方依赖走镜像,这个报错就很少再出现在我的构建日志里了。如果你的项目也遇到类似问题,不妨先从“仓库源是不是干净、职责是不是清晰”这个角度检查一遍。
