3151 字
约 10 分钟
12
S01-B03 · Maven 安装、镜像与第一次构建

适用读者:JDK 已对齐(S01-B02),第一次跑 mvn 就撞上 Could not resolve dependencies 或各种插件报错的人。
前置:S01-B02。
实测环境:Ubuntu 24.04.1 LTS / OpenJDK 17.0.20.1 / Apache Maven 3.8.7 / 实测日期 2026-11-05。
版本基线:Maven 3.9.x(3.8.7 为实测版本,差异处单独标注);maven-compiler-plugin 3.13.0。
覆盖旧文:A-1398(Maven 使用说明与配置,101 点)、A-1374(IDEA 配置 Maven,71 点)。
新增扩展:E-S01-004(Maven 3.9/4.x 与 mvnw)、E-S01-005(settings.xml 与 mirrorOf)、E-S01-006(.mvn/maven.config)。
上一篇:S01-B02 · JDK 安装与多版本共存:JAVA_HOME 到底该指向谁
下一篇:S01-B04 · IDEA 基本功。


1. 一句话结论

Maven 就三件事:用「坐标」找到 jar,用「生命周期」把源码变成产物,用「本地仓库 + 仓库链」缓存一切。 依赖问题的 90% 出在"找 jar"这一环——而找 jar 的顺序是固定的,背下来就能自己排障。

      mvn package
          │
          ▼
 ① 读 pom.xml(坐标 + 依赖 + 插件)
          │
          ▼
 ② 解析依赖 → 查 本地仓库 ~/.m2/repository
          │             │
          │   命中 ────┴──► 直接用
          │   未命中 ──► ③ 远程仓库链
          ▼                    │
 ④ 执行生命周期             ├─ 镜像(mirror,会"劫持"仓库)
 validate→compile→test        └─ 中央仓库 / 私服
 →package→verify→install→deploy

2. 三个概念,先分清

概念 是什么 例子
坐标(GAV) 一个 jar 的唯一定位:groupId:artifactId:version org.apache.commons:commons-lang3:3.14.0
生命周期(lifecycle) 阶段序列,跑后面的阶段会自动跑前面的 compile → test → package → install → deploy
仓库(repository) 存放 jar 的地方。本地一份,远程若干份 ~/.m2/repository、repo.maven.apache.org、公司 Nexus

记住一个反直觉点:mvn package 不等于"只打包"。它会把 validate → compile → test → package 全跑一遍。想跳过测试:-DskipTests(编译测试但不跑)或 -Dmaven.test.skip=true(连测试编译都不做)。


3. 装 Maven:解压即用,但要配环境变量

Maven 是绿色包,下载解压即可(旧文 A-1398 说得对:它"就是文件,不需要安装")。

# Linux / macOS
curl -O https://dlcdn.apache.org/maven/maven-3/3.9.9/binaries/apache-maven-3.9.9-bin.tar.gz
sudo tar -xzf apache-maven-3.9.9-bin.tar.gz -C /opt
export PATH=/opt/apache-maven-3.9.9/bin:$PATH       # 写进 ~/.bashrc 或 ~/.zshrc
mvn -v
:: Windows:解压到 D:\tools\apache-maven-3.9.9
:: 新增 MAVEN_HOME=D:\tools\apache-maven-3.9.9
:: Path 里追加 %MAVEN_HOME%\bin
mvn -v

验证要看的不是"Maven 3.9.9"这一行,而是第三行的 Java version:

Apache Maven 3.8.7
Maven home: /usr/share/maven
Java version: 17.0.20.1, vendor: Ubuntu, runtime: /usr/lib/jvm/java-17-openjdk-amd64   ← 这一行

它和 java -version 不一致,说明 §6(B02)的三处对齐没做好。


4. 实测:从空本地仓库到第一个 jar

4.1 最小可运行项目

/tmp/b03
├── pom.xml
└── src/main/java/demo/App.java

pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0">
  <modelVersion>4.0.0</modelVersion>
  <groupId>demo</groupId>
  <artifactId>s01-b03</artifactId>
  <version>1.0.0</version>
  <properties>
    <maven.compiler.release>17</maven.compiler.release>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
  <dependencies>
    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-lang3</artifactId>
      <version>3.14.0</version>
    </dependency>
  </dependencies>
</project>

App.java:

package demo;
import org.apache.commons.lang3.StringUtils;

public class App {
    public static void main(String[] args) {
        System.out.println("blank? " + StringUtils.isBlank("   "));
    }
}

4.2 第一次构建:报错了,而且报得很有教育意义

mvn -B -Dmaven.repo.local=/tmp/b03/repo package

实测输出(截取):

[INFO] BUILD FAILURE
[INFO] Total time:  22.373 s
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.1:compile
        (default-compile) on project s01-b03: Compilation failure:
[ERROR] Source option 5 is no longer supported. Use 7 or later.
[ERROR] Target option 5 is no longer supported. Use 7 or later.

注意三个细节:

  1. maven-compiler-plugin:3.1 —— 这是 Maven 内置的"默认插件版本",非常老。
  2. 我明明写了 <maven.compiler.release>17</maven.compiler.release>,它却说"Source option 5"——因为 release 参数是 compiler 插件 3.6+ 才有的,3.1 不认这个属性,于是回落到默认的 1.5。
  3. 它连 javac 都没调通就失败了,跟 JDK 无关、跟网络无关——这类报错最容易被误判成"环境没装好"。

4.3 修法:锁插件版本(这是 Maven 项目的第一条纪律)

<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-compiler-plugin</artifactId>
      <version>3.13.0</version>
      <configuration><release>17</release></configuration>
    </plugin>
  </plugins>
</build>

实测结果:

[INFO] Building jar: /tmp/b03/target/s01-b03-1.0.0.jar
[INFO] BUILD SUCCESS
[INFO] Total time:  14.123 s
指标 数值(实测)
Maven 自报总耗时 14.1 s
墙钟耗时 16.5 s
本地仓库体积(从 0 开始) 14 MB
产物 jar 2.2 KB

为什么必须显式锁插件版本? Maven 的默认插件版本跟随 Maven 大版本,跨版本会变;一旦换机器/换 Maven,构建行为就可能悄悄变化。锁版本 = 构建可复现。

对 B02 的修正:上一篇文章我说"maven.compiler.release 是最佳写法",实测证明只写属性不够,必须同时锁 compiler 插件版本(≥3.6)。B02 已同步更新这一处。


5. 读懂依赖树:dependency:tree

mvn -B dependency:tree

实测输出:

[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ s01-b03 ---
[INFO] demo:s01-b03:jar:1.0.0
[INFO] \- org.apache.commons:commons-lang3:jar:3.14.0:compile

读法:

符号 含义
\- / +- / ` `
行尾 :compile / :test / :runtime / :provided scope,决定这个 jar 在哪些阶段可见、是否进产物
omitted for duplicate 同名不同版本的依赖被"调解"掉了(进阶见 S01-J07)

四个 scope 的实用判据:

scope 编译期 运行期 进 jar/war 典型用途
compile(默认) ✅ ✅ ✅ 业务依赖
provided ✅ ❌(容器提供) ❌ servlet-api、lombok
runtime ❌ ✅ ✅ JDBC 驱动
test 仅测试 仅测试 ❌ JUnit、Mockito

6. 三个开关:-o、-U、-X

6.1 -o(离线)——最能暴露真相的模式

mvn -B -o package          # 只允许用本地仓库

实测报错(换了未下载过的版本号):

[ERROR] Failed to execute goal on project s01-b03-offline: Could not resolve dependencies
        for project demo:s01-b03-offline:jar:1.0.0:
        Cannot access central (https://repo.maven.apache.org/maven2) in offline mode
        and the artifact org.apache.commons:commons-lang3:jar:3.13.0 has not been downloaded from it before.

这句报错值得背下来:它明确告诉你"哪个仓库、哪个坐标、为什么失败"。当别人说"我这儿能编译你那儿不能",第一件事就是让他 -o 跑一次——能把"网络问题"和"代码问题"一刀切开。

6.2 -U(强制更新快照)

依赖是 SNAPSHOT 版本、或怀疑本地缓存脏了时用。日常别加,会变慢。

6.3 -X(debug)

排查插件行为、看真实生效的 settings.xml 时用。输出很长,配合 grep 用。

mvn -X package 2>&1 | grep -E "Local repository|Using mirror|Reading global settings"

7. 镜像(mirror):最容易配错的一处

7.1 配置放在哪,谁覆盖谁

位置 作用域 优先级
~/.m2/settings.xml 当前用户 高(覆盖全局)
$MAVEN_HOME/conf/settings.xml 全机 低
pom.xml 的 <repositories> 单项目 最低(且会被 mirror "劫持",见下)

7.2 一个可用的镜像配置

<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0">
  <mirrors>
    <mirror>
      <id>aliyun</id>
      <url>https://maven.aliyun.com/repository/public</url>
      <mirrorOf>central</mirrorOf>
    </mirror>
  </mirrors>
</settings>

7.3 实测三组对照(同一台机器,空仓库,同一坐标)

组 配置 实测耗时 结果
A 默认(官方 Central) 38.4 s 成功
B 阿里云镜像 mirrorOf=central 75.5 s 成功
C 故意写坏 mirrorOf=* 指向不存在域名 — 失败,见下

别照抄"国内镜像一定更快"。同一条坐标,从我这台机器(海外网络)走官方 Central 用了 38 s,走阿里云用了 75 s。镜像快不快取决于你到镜像的网络,不取决于镜像的名气。 判断方法就是自己计时跑一次 dependency:get。

C 组的报错(很有代表性):

[ERROR] No plugin found for prefix 'dependency' in the current project and in the plugin groups
        [org.apache.maven.plugins, org.codehaus.mojo] available from the repositories
        [local (/tmp/mirrors/r3), broken (https://repo.example.invalid/maven2)]

读法:mirrorOf=* 把所有仓库(包括插件仓库)都指向了那个坏地址,于是连插件都找不到。这句话里 available from the repositories [...] 就是当时真实可用的仓库清单——排障时先看这一句。

7.4 两个常见配错

错误配置 后果 正确做法
mirrorOf=* 指向公网镜像 私服被绕过,公司内部构件永远拉不到 用 mirrorOf=central 或 mirrorOf=*,!internal(排除私服)
同时配 3 个镜像且都写 mirrorOf=central 只有第一个生效,其余静默忽略 一个仓库只留一个镜像,写清 <name>

8. 可复现构建:mvnw 与 .mvn/maven.config

8.1 wrapper:把 Maven 版本钉在仓库里

mvn -N wrapper:wrapper -Dmaven=3.9.9

实测生成:

mvnw                      # Unix 脚本
mvnw.cmd                  # Windows 脚本
.mvn/wrapper/maven-wrapper.properties   # 记录 distributionUrl

之后团队统一用 ./mvnw clean package:每个人用的 Maven 版本一致,不再出现"我这里能编译"。

8.2 .mvn/maven.config:把常用参数固化

# .mvn/maven.config(每行一个参数)
-Dmaven.repo.local=/data/m2
--offline

实测生效(mvn -X 里能看到这些参数被合并进命令行)。适合放:统一本地仓库路径、统一 -T 并行度、CI 上的 -B。

注意:maven.config 里放 --offline 是危险操作——会让所有需要新依赖的构建失败。它适合"验证可复现性"的临时场景,不适合长期提交。


9. 排障决策树:Could not resolve dependencies

报错 → 先看最后一行的 "Cannot access <repo>" / "has not been downloaded" / "Could not find artifact"
  │
  ├─ 提到 in offline mode ──────────► 去掉 -o,或确认该依赖是否真的被下载过
  ├─ 提到 Could not find artifact ─► 坐标写错(拼写/版本不存在)→ mvnrepository.com 核对
  ├─ 提到 Connection timed out / UnknownHost ─► 网络或镜像问题 → 用 §7.3 的方法计时验证
  ├─ 提到 401/403 ─────────────────► 私服认证(settings.xml 的 <servers>)
  └─ 什么都没提到,但 jar 明明在本地 ─► 本地仓库损坏 → 删掉该坐标目录重下;仍不行 -U

配合三条命令定位:

mvn -o  package          # 分离"网络 / 代码"
mvn -U  package          # 强制刷新
mvn -X  package | grep -E "Local repository|Using mirror"   # 看实际生效的配置

10. 自检清单

# 检查 命令 预期
1 Maven 与 JDK 一致 mvn -v 第三行 Java version 与 java -version 一致
2 能离线自证 mvn -o -q package(已构建过的项目) 成功;失败则说明依赖没落本地
3 插件被锁定 mvn help:effective-pom | grep -A2 compiler-plugin 有明确 <version>
4 依赖树可读 mvn dependency:tree 能看到直接依赖与传递依赖分层
5 产物体积合理 ls -lh target/*.jar 普通业务 jar 几 KB~几十 MB;异常巨大通常是打包了不该打的依赖
6 环境可复现 ./mvnw -v 版本等于 .mvn/wrapper/maven-wrapper.properties 里声明的版本

11. 旧文对照

项 旧文 本篇处理
Maven 是什么 A-1398:核心是"帮你管理 jar 包" 保留结论,补"坐标 / 生命周期 / 仓库链"三层模型
下载与安装 A-1398/A-1374:解压 + 配环境变量 保留;补三平台命令与 mvn -v 读法
本地仓库改路径 A-1374:在 Maven 目录下建 repository 保留,改为"用 settings.xml 的 <localRepository> 或 -Dmaven.repo.local"(更规范,且支持多项目隔离)
镜像配置 A-1398:阿里云镜像 保留配置,但补 §7.3 实测:镜像不一定更快;补 mirrorOf 语义与私服被绕过的坑
默认插件版本 两者都未涉及 新增 §4.2 实测失败案例(Source option 5)与锁版本纪律
dependency:tree / -o -U -X 未涉及 新增 §5、§6
mvnw / maven.config 未涉及 新增 §8(E-S01-004、E-S01-006)
IDEA 里配 Maven A-1374:项目级/全局 移入 B04 §工程集成,避免与 Maven 本体混讲

12. 自测

  1. mvn -o package 报 has not been downloaded from it before,说明什么?该怎么修?
  2. 为什么只写 <maven.compiler.release> 还不够?缺的那一块是什么?
  3. 公司有私服,settings.xml 里写了 mirrorOf=* 指向公网镜像,会发生什么?
  4. provided 与 compile 的区别,用一个真实依赖举例说明(提示:lombok、servlet-api)。
  5. .mvn/maven.config 里放 --offline 会带来什么风险?

13. 下一篇

S01-B04 · IDEA 基本功:界面、视图、外观与编码习惯

Maven 打通之后,回到 IDE:哪些设置属于"个人偏好"可以随便改,哪些属于"团队约定"必须统一?下一篇把旧文里 10 篇零散的设置教程(字体、主题、汉化、行号、Tab、导包、注释模板……)合成一篇可执行的配置流程,并用 .editorconfig 把"团队约定"落到仓库里。


本篇属于系列《S01 · 开发环境与工具链》。旧文来源:A-1398、A-1374;扩展点 E-S01-004/005/006。全部命令与耗时均为 2026-11-05 实测,仓库截面见 ../coverage.csv。

S01-B03 · Maven 安装、镜像与第一次构建
https://www.clxhxhhr.top/posts/5157/
作者
clxstart
发布于
2024-12-31
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。