欢迎光临
我们一直在努力

Java项目管家-Maven

Maven 学习笔记

Maven 是 Java 项目中常用的项目管理与构建工具。它通过统一的项目结构和 pom.xml 配置文件,管理编译、测试、打包以及第三方依赖,使项目能够使用一组固定命令完成可重复构建。

这篇笔记以一个简单的命令行任务管理器为贯穿案例,重点掌握 pom.xml、项目结构、依赖管理、构建生命周期和常用命令。完成学习后,可以独立创建一个基础 Maven 项目,执行测试并生成可运行的 JAR 文件。

先验知识:能够阅读基础 Java 代码,了解 JDK、JAR 和命令行的基本概念即可。

示例基线

本文示例统一采用 Maven 3.9.16、JDK 17、JUnit Jupiter 5.14.4。Maven 4 尚未作为本文的学习基线,避免混用不同版本的配置方式。

目录

  • 1. 应用背景
  • 2. Maven 简介
  • 3. POM 项目配置
  • 4. Maven 项目结构
  • 5. 依赖管理
  • 6. 构建生命周期
  • 7. 常用 Maven 命令
  • 8. 完整项目案例
  • 9. 常见问题与排查
  • 10. 总结与速查

1. 应用背景

一个普通 Java 项目通常需要完成以下工作:

  • 按正确顺序编译多个 Java 文件;
  • 引入 JUnit、数据库驱动等第三方类库;
  • 区分正式代码、测试代码和资源文件;
  • 执行自动化测试;
  • 将编译结果打包为 JAR 或 WAR;
  • 保证不同开发者和服务器使用相同的构建流程。
  • 如果完全依靠手工操作,开发者需要自己下载 JAR、维护类路径、组织编译命令并处理依赖版本冲突。项目规模增大后,这些操作容易出错,也难以在其他电脑上重复执行。

    Maven 将这些信息写入项目根目录的 pom.xml,再通过统一命令执行构建。例如:

    # 在包含 pom.xml 的项目根目录执行
    mvn clean package

    这条命令会清理旧结果、编译源码、执行测试并生成项目制品。对普通 Java 项目而言,制品通常是 JAR 文件。

    Maven 在工程中的位置可以概括为:

    Java 源代码与资源文件

    pom.xml 描述项目、依赖和构建规则

    Maven 按生命周期调用不同插件

    编译结果、测试报告和 JAR 文件进入 target 目录

    Maven 不替代 JDK。JDK 提供 Java 编译器和运行环境,Maven 负责组织并调用这些工具。IDE 可以集成 Maven,但项目是否能够正确构建,最终仍应以 Maven 命令的执行结果为准。

    2. Maven 简介

    Maven 是面向项目的构建与依赖管理工具。它读取 pom.xml,根据其中声明的项目信息、依赖和插件配置,完成编译、测试、打包、安装和发布等任务。

    Maven 的核心特点包括:

    • 约定优于配置:默认从 src/main/java 读取正式源码,从 src/test/java 读取测试源码,将构建结果写入 target;
    • 声明式配置:开发者主要描述“项目需要什么”,而不是逐条编写所有底层编译命令;
    • 依赖自动解析:根据依赖坐标从仓库下载 JAR,并处理传递依赖;
    • 标准构建流程:不同项目可以使用 mvn test、mvn package、mvn verify 等统一命令;
    • 插件扩展:编译、测试、打包等实际工作由 Maven 插件完成。

    2.1 Maven 的输入、处理与输出

    阶段主要内容
    输入 pom.xml、Java 源码、测试代码、资源文件
    处理 解析项目模型、下载依赖、执行插件、依次完成生命周期阶段
    输出 .class 文件、测试报告、JAR/WAR、安装到本地仓库的制品

    2.2 Maven、JDK、IDE 和仓库的关系

    对象作用与 Maven 的关系
    JDK 编译和运行 Java 程序 Maven 调用 JDK 中的 javac 等工具
    IDE 编辑、调试和运行代码 IDE 可以读取并同步 Maven 项目
    pom.xml 描述当前项目 Maven 的核心输入文件
    Maven 插件 执行编译、测试、打包等任务 Maven 生命周期通过插件目标完成实际工作
    Maven 仓库 保存项目依赖和构建制品 Maven 从仓库解析依赖,并可向仓库安装或发布制品

    判断项目是否真正可构建

    IDE 中能够运行不代表 Maven 构建一定成功。提交代码前应在项目根目录执行 mvn clean verify。

    3. POM 项目配置

    POM 是 Project Object Model 的缩写,即项目对象模型。pom.xml 是 Maven 项目的核心配置文件,通常位于项目根目录。执行 Maven 命令时,Maven 默认在当前目录寻找该文件。

    本笔记使用的案例项目名为 task-manager,目标是创建一个简单的命令行任务管理器。下面的 POM 同时配置了:

    • 项目坐标;
    • Java 17 编译版本;
    • JUnit 测试依赖;
    • 编译插件;
    • 测试插件;
    • 可执行 JAR 的主类。

    文件位置:task-manager/pom.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <!– Maven 项目的核心配置文件,文件名必须为 pom.xml –>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
    https://maven.apache.org/xsd/maven-4.0.0.xsd">

    <!– 当前 POM 使用的模型版本,不是 Maven 软件版本 –>
    <modelVersion>4.0.0</modelVersion>

    <!– groupId、artifactId、version 共同唯一标识一个项目制品 –>
    <!– 1. groupId 代表项目归属,遵循反向域名小写规范,用来区分团队/组织。
    2. artifactId 是当前模块名称,小写并用横杠分隔单词,同一组织内保持唯一。
    3. version 为项目版本,采用 主.次.补丁 三段数字格式,SNAPSHOT代表未发布快照版本。–>
    <groupId>com.example</groupId>
    <artifactId>task-manager</artifactId>
    <version>1.0.0</version>

    <!– jar 表示项目最终打包为 JAR;不写时默认也是 jar–>

    <packaging>jar</packaging>

    <properties>
    <!–集中保存可复用值
    1.编译基础配置:指定项目使用 JDK26 编译,所有源码文件编码统一为 UTF-8,解决版本不兼容、中文乱码问题。
    2.依赖版本变量:把 JUnit 单元测试框架版本存入变量,后续引入测试依赖时直接引用,方便统一升级。
    3.Maven 插件版本变量:集中定义编译、单元测试打包、Jar 包打包等核心插件的版本,修改版本只需改动此处,无需多处修改。 –>

    <!– 使用 JDK 26 的 API 和字节码级别编译项目 –>
    <maven.compiler.release>26</maven.compiler.release>

    <!– 统一源码和资源文件编码,避免不同系统出现中文乱码 –>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

    <!– 集中维护依赖与插件版本,后续升级只修改一处 –>
    <junit.version>5.14.4</junit.version>
    <maven.compiler.plugin.version>3.15.0</maven.compiler.plugin.version>
    <maven.surefire.plugin.version>3.5.4</maven.surefire.plugin.version>
    <maven.jar.plugin.version>3.5.1</maven.jar.plugin.version>
    </properties>

    <dependencies>
    <!– `<dependencies>` 是 Maven 的依赖管理标签,用于声明项目需要引入的第三方工具类库 –>
    <!– JUnit Jupiter 仅用于编译和执行测试,不进入正式运行环境 –>
    <dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter</artifactId>
    <version>${junit.version}</version>
    <scope>test</scope>
    </dependency>
    </dependencies>

    <build>
    <!–Maven 的 `<build>` 标签用来配置项目构建全过程,包含编译、资源文件复制、单元测试、打包、输出目录、插件等所有构建相关规则。
    – maven-compiler-plugin:编译 Java 源码;
    – maven-surefire-plugin:自动执行 JUnit 单元测试;
    – maven-jar-plugin:打包可执行 Jar 包,内置主类配置支持命令行直接运行程序
    – exec-maven-plugin:程序快速运行,执行 `mvn exec:java` 命令时,直接运行主类,无需先打包 jar,开发调试时快速启动程序。
    –>
    <plugins>
    <!– 明确指定 Java 编译插件版本,避免依赖 Maven 默认版本 –>
    <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>${maven.compiler.plugin.version}</version>
    </plugin>

    <!– 在 test 阶段发现并执行 JUnit 测试 –>
    <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>${maven.surefire.plugin.version}</version>
    </plugin>

    <!– 在 JAR 清单中写入主类,使生成的 JAR 可以通过 java -jar 运行 –>
    <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jar-plugin</artifactId>
    <version>${maven.jar.plugin.version}</version>
    <configuration>
    <archive>
    <manifest>
    <mainClass>com.example.task.App</mainClass>
    </manifest>
    </archive>
    </configuration>
    </plugin>
    </plugins>
    </build>

    </project>

    3.1 项目坐标

    Maven 使用坐标定位项目和依赖,最核心的三个字段是:

    字段当前值含义
    groupId com.example 项目所属组织,通常使用反向域名
    artifactId task-manager 当前项目或模块名称
    version 1.0.0 当前制品版本

    三者组合后得到:

    com.example:task-manager:1.0.0

    Maven 构建完成后,JAR 文件名默认由 artifactId-version 组成:

    task-manager-1.0.0.jar

    开发中的不稳定版本常使用 1.0.0-SNAPSHOT。正式发布时通常去掉 SNAPSHOT,使用固定版本号。

    3.2 属性与变量引用

    properties 用于集中保存可复用值。POM 中通过 ${属性名} 引用属性:

    <!– 在 properties 中集中定义版本 –>
    <junit.version>5.14.4</junit.version>

    <!– 在 dependency 中引用该版本 –>
    <version>${junit.version}</version>

    这样做可以减少重复,并使后续升级更明确。

    maven.compiler.release 同时约束 Java 语言级别、生成的字节码版本以及可使用的标准库 API。它比只设置 source 和 target 更适合现代 JDK 项目。

    3.3 依赖与插件

    dependencies 和 build/plugins 容易混淆:

    • 依赖供项目代码使用,例如 JUnit、数据库驱动、日志库;
    • 插件在构建过程中执行任务,例如编译、测试、打包、生成文档。

    本项目中的 JUnit 是测试依赖,maven-compiler-plugin、maven-surefire-plugin 和 maven-jar-plugin 是构建插件。

    3.4 POM 的读取与验证

    保存 pom.xml 后,在项目根目录执行:

    # 检查 Maven 是否能够读取并解析当前 POM
    mvn validate

    正常情况下,终端最后出现:

    BUILD SUCCESS

    查看 Maven 合并默认配置、父 POM 和当前 POM 后得到的最终配置:

    # 输出 Maven 实际使用的完整 POM
    mvn help:effective-pom

    pom.xml 与 settings.xml

    pom.xml 属于项目,应提交到版本库;settings.xml 属于用户或构建机器,常用于配置镜像、私服和凭据,通常位于用户目录的 .m2/settings.xml,不应把密码直接写入项目 POM。

    4. Maven 项目结构

    Maven 默认使用固定目录约定,因此通常不需要在 POM 中逐项配置源码位置。

    本案例采用以下结构:

    task-manager/
    ├─ pom.xml # Maven 项目配置
    ├─ src/
    │ ├─ main/
    │ │ ├─ java/
    │ │ │ └─ com/example/task/
    │ │ │ ├─ App.java # 程序入口
    │ │ │ └─ TaskService.java # 任务业务逻辑
    │ │ └─ resources/ # 正式环境资源文件
    │ └─ test/
    │ ├─ java/
    │ │ └─ com/example/task/
    │ │ └─ TaskServiceTest.java # 单元测试
    │ └─ resources/ # 测试资源文件
    └─ target/ # Maven 自动生成的构建结果

    各目录的默认用途如下:

    路径用途是否手动维护
    pom.xml 项目、依赖和构建配置
    src/main/java 正式 Java 源码
    src/main/resources 配置、模板等正式资源
    src/test/java 测试源码
    src/test/resources 测试所需资源
    target/classes 正式源码编译结果
    target/test-classes 测试源码编译结果
    target/surefire-reports 单元测试报告
    target/*.jar 打包后的 JAR

    Java 文件的 package 必须与 src/main/java 或 src/test/java 后面的目录保持一致。例如:

    // 文件路径:src/main/java/com/example/task/App.java
    package com.example.task;

    不要手动修改 target

    target 是 Maven 自动生成目录,执行 mvn clean 后会被删除。源码和配置必须保存在 src 与 pom.xml 中。

    5. 依赖管理

    依赖管理解决的是“项目需要哪些外部类库、使用哪个版本、在什么阶段使用,以及从哪里取得”的问题。

    5.1 依赖坐标与仓库

    一个常见依赖声明包含:

    <!– 声明一个仅用于测试的 JUnit 依赖 –>
    <dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter</artifactId>
    <version>5.14.4</version>
    <scope>test</scope>
    </dependency>

    Maven 解析依赖时通常按照以下过程工作:

    读取 pom.xml 中的依赖坐标

    检查本地仓库是否已经存在

    不存在时访问远程仓库

    下载依赖及其元数据到本地仓库

    根据 scope 加入对应阶段的类路径

    常见仓库包括:

    • 本地仓库:当前电脑缓存的依赖,默认位于用户目录的 .m2/repository;
    • 中央仓库:Maven 默认使用的公共远程仓库;
    • 私有仓库:企业内部使用的 Nexus、Artifactory 等仓库。

    本地仓库不是项目的 lib 目录。不要把 .m2/repository 整体复制到项目中,也不要手动修改其中的文件。

    5.2 依赖范围

    scope 决定依赖在哪些阶段可用,以及是否传递给下游项目。

    scope编译主代码编译/运行测试运行正式程序常见用途
    compile 普通业务依赖,默认值
    runtime 只在运行时需要的实现,例如部分数据库驱动
    test JUnit、Mockito 等测试工具
    provided 由运行环境提供 Servlet API 等容器提供的类库
    import 不直接形成类路径 不直接形成类路径 不直接形成类路径 在 dependencyManagement 中导入 BOM
    system 取决于配置 取决于配置 取决于配置 直接引用本机文件,不推荐使用

    未填写 scope 时默认为 compile。

    5.3 传递依赖

    如果项目依赖 A,而 A 又依赖 B,Maven 通常会自动把 B 作为传递依赖加入项目。开发者不必手工声明每一层依赖。

    查看完整依赖树:

    # 在项目根目录查看直接依赖和传递依赖
    mvn dependency:tree

    输出会显示依赖层级、版本和范围。出现类冲突或版本不符合预期时,应优先执行这条命令。

    如果某个传递依赖不应进入项目,可以排除:

    <dependency>
    <groupId>com.example</groupId>
    <artifactId>example-library</artifactId>
    <version>1.0.0</version>
    <exclusions>
    <!– 排除 example-library 间接引入的旧日志实现 –>
    <exclusion>
    <groupId>commons-logging</groupId>
    <artifactId>commons-logging</artifactId>
    </exclusion>
    </exclusions>
    </dependency>

    排除后,如果项目仍然需要该类库,应在 dependencies 中显式声明合适版本。

    5.4 版本冲突与依赖调解

    同一个依赖通过多条路径进入项目时,可能出现多个版本。Maven 会进行依赖调解,通常优先选择依赖树中距离当前项目更近的版本;路径深度相同时,声明顺序也可能影响结果。

    工程中不要依赖偶然的调解结果。更稳妥的做法是:

  • 使用 mvn dependency:tree 找到冲突路径;
  • 在当前项目中显式声明需要的版本;
  • 多模块项目在父 POM 的 dependencyManagement 中统一版本;
  • 必要时排除不需要的传递依赖;
  • 执行完整测试确认升级没有破坏兼容性。
  • 5.5 dependencies 与 dependencyManagement

    dependencies 会真正把依赖加入当前模块;dependencyManagement 只提供统一的版本和默认配置,本身不会让依赖进入类路径。

    <dependencyManagement>
    <dependencies>
    <!– 这里只统一版本,不会自动将 JUnit 加入当前模块 –>
    <dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter</artifactId>
    <version>5.14.4</version>
    </dependency>
    </dependencies>
    </dependencyManagement>

    <dependencies>
    <!– 当前模块仍需显式声明依赖,但可以省略 version –>
    <dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter</artifactId>
    <scope>test</scope>
    </dependency>
    </dependencies>

    单模块小项目可以先使用 properties 集中维护版本。进入多模块项目后,再重点学习父 POM、dependencyManagement 和 BOM。

    6. 构建生命周期

    Maven 构建由三个概念组成:生命周期、阶段和插件目标。

    • 生命周期(Lifecycle):完整构建过程的总体顺序;
    • 阶段(Phase):生命周期中的一个步骤,例如 compile、test、package;
    • 目标(Goal):插件执行的具体任务,例如 compiler:compile、surefire:test。

    它们的关系是:

    生命周期规定阶段顺序

    阶段绑定一个或多个插件目标

    插件目标执行实际工作

    6.1 三个内置生命周期

    生命周期主要作用常用阶段
    default 编译、测试、打包、安装和发布项目 compile、test、package、verify、install、deploy
    clean 删除旧构建结果 clean
    site 生成项目站点和报告 site

    普通开发最常使用 clean 和 default。

    6.2 默认生命周期的关键阶段

    默认生命周期包含多个阶段,初学阶段重点掌握以下顺序:

    validate

    compile

    test

    package

    verify

    install

    deploy

    阶段作用主要结果
    validate 检查项目结构和配置是否可继续构建 POM 被解析
    compile 编译 src/main/java target/classes
    test 编译并运行单元测试 target/surefire-reports
    package 根据 packaging 生成 JAR 或 WAR target/*.jar 或 target/*.war
    verify 执行打包后的质量检查 构建通过或失败
    install 将制品写入本地仓库 .m2/repository 中出现当前制品
    deploy 将制品发布到远程仓库 团队或服务器可使用该制品

    执行某个阶段时,Maven 会先执行它之前的所有阶段。例如:

    # 会依次执行 validate、compile、test 和 package 等前置阶段
    mvn package

    因此,不需要先后手动执行 mvn compile、mvn test、mvn package。

    6.3 阶段与插件目标的区别

    以下两条命令含义不同:

    # 执行默认生命周期直到 test 阶段
    mvn test

    # 直接调用 Surefire 插件的 test 目标
    mvn surefire:test

    日常构建优先执行生命周期阶段,因为它会保证前置步骤按顺序完成。直接调用插件目标通常用于诊断、工具操作或明确知道前置条件已经满足的场景。

    7. 常用 Maven 命令

    Maven 命令的基本格式是:

    mvn [全局选项] [生命周期阶段或插件目标] [-D属性名=属性值]

    例如:

    # -U 是 Maven 全局选项,clean 和 verify 是生命周期阶段,skipTests 是传给插件的属性
    mvn -U clean verify -DskipTests

    其中:

    • 生命周期阶段使用单个名称,例如 compile、test、package;
    • 插件目标使用 插件前缀:目标,例如 dependency:tree、help:effective-pom;
    • 全局选项控制 Maven 本次运行方式,例如 -X、-o、-P、-pl;
    • -D 属性用于向 POM 或插件传入参数,例如 -DskipTests。

    除创建新项目等特殊命令外,以下命令一般都在包含 pom.xml 的项目根目录执行。

    优先使用 Maven Wrapper

    如果项目根目录包含 mvnw、mvnw.cmd 和 .mvn,应优先使用 Wrapper,以保证团队使用同一 Maven 版本:Windows PowerShell 执行 .\\mvnw.cmd clean verify,Linux/macOS 执行 ./mvnw clean verify。下文的 mvn 均可替换为对应的 Wrapper 命令。

    7.1 帮助与环境检查

    Maven 版本与命令帮助

    # 查看 Maven 版本、Maven 安装目录、Java 版本、Java 路径和操作系统信息
    mvn -v

    # 与 mvn -v 作用相同
    mvn –version

    # 查看 Maven 支持的全部命令行选项
    mvn -h

    # 显示 Maven 版本后继续执行构建,适合构建日志留档
    mvn -V clean verify

    排查构建环境时首先执行 mvn -v,重点检查:

    • Maven 版本是否符合项目要求;
    • Java version 是否正确;
    • Java home 是否指向预期 JDK;
    • Maven 是否使用了错误的用户目录或操作系统环境。
    查看系统与环境属性

    # 显示 Maven 能读取到的 Java 系统属性和环境变量
    mvn help:system

    该命令适合检查 JAVA_HOME、用户目录、文件编码和操作系统属性。输出可能包含较多环境信息,不要将包含敏感变量的完整结果直接公开。

    查看最终 POM

    # 查看继承、属性替换和 Profile 激活后的最终 POM
    mvn help:effective-pom

    # 在最终 POM 中标注每项配置来自哪个 POM,排查覆盖关系时更有用
    mvn help:effective-pom -Dverbose

    # 将最终 POM 输出到文件,便于搜索和对比
    mvn help:effective-pom -Doutput=effective-pom.xml

    当当前 pom.xml 中没有某项配置,但 Maven 实际使用了该配置时,通常是它来自父 POM、超级 POM、激活的 Profile 或插件默认值。

    查看最终 Maven 设置

    # 查看全局 settings.xml 与用户 settings.xml 合并后的实际配置
    mvn help:effective-settings

    # 将结果写入文件,便于检查镜像、私服和本地仓库配置
    mvn help:effective-settings -Doutput=effective-settings.xml

    该命令常用于排查:

    • Maven 实际使用了哪个镜像;
    • 私服地址是否生效;
    • 本地仓库是否改过位置;
    • 哪些服务器凭据配置被读取。
    查看 Profile

    # 查看当前构建已经激活的 Profile
    mvn help:active-profiles

    # 查看当前项目中全部可用 Profile,包括未激活的 Profile
    mvn help:all-profiles

    查询 Maven 表达式

    # 读取当前项目版本号
    mvn help:evaluate -Dexpression=project.version -q -DforceStdout

    # 读取最终构建目录,默认通常为项目根目录下的 target
    mvn help:evaluate -Dexpression=project.build.directory -q -DforceStdout

    # 读取 Maven 实际使用的本地仓库路径
    mvn help:evaluate -Dexpression=settings.localRepository -q -DforceStdout

    -q 减少普通日志,-DforceStdout 强制把表达式结果输出到终端,适合脚本读取。

    查看插件和目标参数

    # 查看 Compiler 插件支持的目标和参数
    mvn help:describe -Dplugin=org.apache.maven.plugins:maven-compiler-plugin -Ddetail

    # 只查看 Compiler 插件 compile 目标的详细参数
    mvn help:describe `
    -Dplugin=org.apache.maven.plugins:maven-compiler-plugin `
    -Dgoal=compile `
    -Ddetail

    当不知道某个插件参数如何填写时,优先使用 help:describe,不要凭印象猜测参数名。

    7.2 生命周期基本命令

    Maven 执行某个生命周期阶段时,会先执行该阶段之前的所有阶段。因此,通常只需要执行希望到达的最后阶段。

    清理生命周期

    # 删除当前项目的 target 目录和旧构建结果
    mvn clean

    clean 不会删除源码、pom.xml 或整个本地仓库,只清理当前项目生成的构建目录。

    默认生命周期高频阶段
    命令主要作用是否执行测试主要结果
    mvn validate 验证 POM 和项目基本信息 确认项目模型可被解析
    mvn compile 编译正式源码 target/classes
    mvn test-compile 编译正式源码和测试源码 不运行 target/classes、target/test-classes
    mvn test 编译并运行单元测试 target/surefire-reports
    mvn package 测试后生成 JAR/WAR target/*.jar 或 target/*.war
    mvn verify 完成打包并执行额外质量验证 完整验证结果
    mvn install 将当前制品安装到本地仓库 .m2/repository 中出现当前制品
    mvn deploy 将制品发布到远程仓库 团队私服或发布仓库中出现制品

    对应命令如下:

    # 只验证项目模型,不编译代码
    mvn validate

    # 编译 src/main/java 下的正式源码
    mvn compile

    # 编译正式代码和 src/test/java 下的测试代码,但不执行测试
    mvn test-compile

    # 编译并执行单元测试
    mvn test

    # 编译、测试并生成 JAR 或 WAR
    mvn package

    # 完成 package 之前的阶段,并执行项目配置的额外检查
    mvn verify

    # 完成构建并安装到当前电脑的 Maven 本地仓库
    mvn install

    # 完成构建并发布到 pom.xml 或 settings.xml 配置的远程仓库
    mvn deploy

    日常应如何选择

    只检查单元测试使用 mvn test;需要构建文件使用 mvn package;提交代码前优先使用 mvn verify;只有其他本地项目需要引用当前项目时才使用 mvn install;mvn deploy 通常由发布流程或持续集成服务器执行。

    经常在日志中出现的中间阶段
    阶段作用
    initialize 初始化构建状态和属性
    generate-sources 生成需要参与编译的源码
    process-sources 处理正式源码
    generate-resources 生成资源文件
    process-resources 将正式资源复制或处理到输出目录
    process-classes 对编译后的类文件继续处理
    generate-test-sources 生成测试源码
    process-test-resources 处理测试资源
    prepare-package 在正式打包前进行准备
    integration-test 执行集成测试相关任务
    verify 检查集成测试或其他质量规则的结果

    这些阶段通常由插件绑定后自动执行。除调试插件绑定外,不需要逐个手动运行。

    不要把 integration-test 当作完整集成测试命令

    某些项目会在 pre-integration-test 启动测试环境,在 post-integration-test 关闭环境。只执行 mvn integration-test 可能无法完成清理,应执行 mvn verify 让后续阶段一起完成。

    站点生命周期

    # 根据项目报告配置生成 Maven Site
    mvn site

    # 将生成的站点发布到配置的目标位置
    mvn site-deploy

    # 先清理,再生成站点
    mvn clean site

    生成的站点通常位于 target/site。普通业务项目不一定配置站点,因此这组命令的使用频率低于 test、package 和 verify。

    7.3 高频构建组合

    实际开发通常会组合 clean 与默认生命周期阶段。

    # 清理旧结果后重新编译正式代码
    mvn clean compile

    # 清理旧结果后执行全部单元测试
    mvn clean test

    # 清理、测试并生成 JAR 或 WAR
    mvn clean package

    # 清理并完成完整验证,提交代码前推荐使用
    mvn clean verify

    # 清理、完整构建并安装到本地仓库
    mvn clean install

    # 清理、完整构建并发布到远程仓库
    mvn clean deploy

    clean 与 package 属于不同生命周期,因此可以放在同一条命令中顺序执行。

    不执行 clean 的情况

    # 在现有 target 基础上继续构建,速度可能更快
    mvn test

    # 直接重新打包,不先删除 target
    mvn package

    Maven 本身不是真正的增量编译系统。修改插件配置、生成代码规则、资源文件或分支后,如果结果异常,应重新执行 mvn clean verify 排除旧文件干扰。

    7.4 测试命令

    以下命令适用于使用 Maven Surefire Plugin 执行的单元测试。

    执行全部单元测试

    # 编译并执行全部单元测试
    mvn test

    执行指定测试类

    # 只执行 TaskServiceTest
    mvn test -Dtest=TaskServiceTest

    # 使用完整类名执行指定测试
    mvn test -Dtest=com.example.task.TaskServiceTest

    # 使用通配符执行名称匹配的测试类
    mvn test -Dtest=*ServiceTest

    # 执行多个指定测试类
    mvn test -Dtest=TaskServiceTest,UserServiceTest

    执行指定测试方法

    # 只执行指定测试类中的一个方法
    mvn test -Dtest=TaskServiceTest#addTaskShouldStoreValidTask

    # 使用通配符执行名称匹配的方法
    mvn test -Dtest=TaskServiceTest#addTask*

    在 Windows PowerShell 中,# 可能被解释为注释起始符。出现命令被截断时,使用引号:

    # 使用引号保护包含 # 的测试选择表达式
    mvn test "-Dtest=TaskServiceTest#addTaskShouldStoreValidTask"

    跳过测试

    # 编译测试代码,但不执行测试
    mvn clean package -DskipTests

    # 测试代码既不编译也不执行
    mvn clean package -Dmaven.test.skip=true

    区别如下:

    参数是否编译测试代码是否运行测试推荐程度
    -DskipTests 临时构建时可用
    -Dmaven.test.skip=true 仅在明确需要时使用

    正式提交、合并和发布前应去掉跳过参数,重新执行 mvn clean verify。

    集成测试

    如果项目使用 Maven Failsafe Plugin 管理集成测试,可以使用:

    # 执行包含集成测试结果验证的完整生命周期
    mvn verify

    # 只选择一个集成测试类;仅在项目配置了 Failsafe 时有效
    mvn verify -Dit.test=UserApiIT

    # 跳过集成测试,但是否仍执行单元测试取决于插件配置
    mvn verify -DskipITs

    普通项目没有配置 Failsafe 时,不要默认认为 -Dit.test 会生效。

    7.5 依赖查看与分析

    查看依赖树

    # 显示项目实际解析后的完整依赖层级
    mvn dependency:tree

    # 只显示指定 groupId 下的依赖
    mvn dependency:tree -Dincludes=org.junit.jupiter

    # 只追踪某个具体依赖及其引入路径
    mvn dependency:tree -Dincludes=org.slf4j:slf4j-api

    # 排除不需要显示的依赖
    mvn dependency:tree -Dexcludes=org.junit.jupiter

    # 将依赖树写入文本文件
    mvn dependency:tree -DoutputFile=dependency-tree.txt

    依赖版本冲突、类重复、运行时找不到类时,应首先查看 dependency:tree,不要直接删除整个本地仓库。

    查看扁平依赖列表

    # 列出项目最终解析到的全部依赖,不显示树形父子关系
    mvn dependency:list

    # 只显示运行时需要的依赖
    mvn dependency:list -DincludeScope=runtime

    # 显示依赖在本地仓库中的绝对文件路径
    mvn dependency:list -DoutputAbsoluteArtifactFilename=true

    解析依赖但不构建项目

    # 解析当前项目依赖,并显示解析结果
    mvn dependency:resolve

    # 解析当前项目构建所需插件
    mvn dependency:resolve-plugins

    # 提前解析依赖、插件和报告插件,为离线构建做准备
    mvn dependency:go-offline

    dependency:go-offline 能提前下载大量构建资源,但某些动态插件或项目脚本仍可能在离线运行时需要额外文件,因此应在断网前实际执行一次完整构建验证。

    分析依赖声明

    # 检查已声明但未使用,以及代码使用了但未直接声明的依赖
    mvn dependency:analyze

    # 检查现有 exclusions 是否已经失去作用
    mvn dependency:analyze-exclusions

    dependency:analyze 基于字节码分析,反射、SPI、注解处理器等依赖可能被误判为“未使用”,删除前必须结合项目实际用途确认。

    生成运行类路径

    # 在终端输出当前项目依赖组成的类路径
    mvn dependency:build-classpath

    # 将依赖类路径写入 cp.txt
    mvn dependency:build-classpath -Dmdep.outputFile=cp.txt

    该命令适合需要通过 java -cp 手动运行类的场景。

    复制项目依赖

    # 将当前项目的依赖复制到 target/dependency
    mvn dependency:copy-dependencies

    # 将运行时依赖复制到自定义目录
    mvn dependency:copy-dependencies `
    -DincludeScope=runtime `
    -DoutputDirectory=target/lib

    该命令只是复制依赖文件,不会自动让普通 JAR 变成包含全部依赖的可执行 Fat JAR。

    单独下载一个制品

    # 将指定制品及其传递依赖解析到本地仓库
    mvn dependency:get -Dartifact=org.slf4j:slf4j-api:2.0.17

    # 只下载指定制品,不解析传递依赖
    mvn dependency:get `
    -Dartifact=org.slf4j:slf4j-api:2.0.17 `
    -Dtransitive=false

    该命令适合验证坐标是否存在,或预先填充本地仓库。它不会自动把依赖写入当前项目的 pom.xml。

    修复疑似损坏的本地依赖缓存

    # 删除当前项目依赖在本地仓库中的缓存,并重新解析
    mvn dependency:purge-local-repository

    # 只清理 SNAPSHOT 依赖并重新解析
    mvn dependency:purge-local-repository -DsnapshotsOnly=true

    该命令会修改本地仓库,应在确认依赖缓存异常后使用。普通依赖冲突应先通过 dependency:tree 和 POM 版本管理解决。

    查看项目使用的仓库

    # 列出当前构建及传递依赖涉及的仓库
    mvn dependency:list-repositories

    如果依赖下载来源不符合预期,还应结合 mvn help:effective-settings 检查镜像配置。

    7.6 Profile 与属性参数

    激活 Profile

    # 激活 dev Profile 后执行验证
    mvn clean verify -Pdev

    # 同时激活多个 Profile
    mvn clean verify -Pdev,local

    Profile 名称必须与 pom.xml 或 settings.xml 中的 <id> 一致。先执行 mvn help:all-profiles 可以查看项目提供了哪些 Profile。

    传入用户属性

    # 向 POM 或插件传入 env=dev 属性
    mvn clean package -Denv=dev

    # 同时传入多个属性
    mvn clean package -Denv=dev -Dfeature.enabled=true

    如果 POM 使用 ${env} 或插件声明了相同名称的用户属性,命令行值会参与本次构建。

    -P 与 -D 不同

    -Pdev 激活名为 dev 的 Profile;-Denv=dev 只是定义属性 env,只有 POM 或插件引用该属性时才产生效果。

    7.7 离线、更新与仓库控制

    # 强制检查缺失的 Release 和更新后的 SNAPSHOT
    mvn -U clean verify

    # 禁止检查 SNAPSHOT 更新,使用本地已有版本
    mvn -nsu clean verify

    # 完全离线构建,只使用本地仓库已有内容
    mvn -o clean verify

    三者区别:

    选项作用常见用途
    -U 强制检查远程更新 SNAPSHOT 未更新、失败缓存已修复
    -nsu 不检查 SNAPSHOT 更新 临时减少远程请求
    -o 禁止访问远程仓库 断网构建或验证本地缓存完整性

    离线构建失败通常表示本地仓库缺少依赖或插件。联网时先执行:

    # 提前解析离线构建所需内容
    mvn dependency:go-offline

    # 再实际完成一次构建,确认没有遗漏
    mvn clean verify

    7.8 日志与错误诊断

    # 显示更完整的异常堆栈
    mvn -e clean verify

    # 输出最详细的调试日志
    mvn -X clean verify

    # 只显示错误信息,普通开发中不建议默认使用
    mvn -q clean verify

    # 将全部构建日志写入文件
    mvn -l build.log clean verify

    # 不显示依赖下载进度,持续集成日志更整洁
    mvn -ntp clean verify

    # 非交互批处理模式,适合 CI
    mvn -B clean verify

    推荐排错顺序:

  • 先执行普通 mvn clean verify;
  • 找到第一个 [ERROR];
  • 需要完整异常链时加 -e;
  • 仍无法判断 Maven 如何解析配置时再加 -X;
  • 将长日志保存到文件后搜索 Caused by、Failure 和具体插件名称。
  • -X 日志可能包含敏感信息

    调试日志可能打印仓库地址、用户名、请求头或环境信息,公开前应先检查和脱敏。

    7.9 指定 POM、settings 与执行目录

    # 在当前目录之外,指定另一个 pom.xml 执行构建
    mvn -f D:\\projects\\task-manager\\pom.xml clean verify

    # 也可以把 -f 指向包含 pom.xml 的目录
    mvn -f D:\\projects\\task-manager clean verify

    # 使用指定的用户级 settings.xml
    mvn -s D:\\maven-config\\settings.xml clean verify

    # 使用指定的全局 settings.xml
    mvn -gs D:\\maven-config\\global-settings.xml clean verify

    适用场景:

    • 自动化脚本不在项目目录运行;
    • 不同公司私服需要不同 settings.xml;
    • 临时验证某套镜像或代理配置;
    • 同一机器维护多个 Maven 构建环境。

    路径包含空格时应加引号:

    # 路径含空格时使用引号
    mvn -f "D:\\Java Projects\\task-manager\\pom.xml" clean verify

    7.10 创建 Maven 项目

    交互式创建

    在准备存放项目的父目录执行:

    # 打开 Archetype 列表并交互式选择项目模板
    mvn archetype:generate

    该方式会要求选择 Archetype、填写 groupId、artifactId、版本和包名,候选模板较多,初学者容易选错。

    使用 Quickstart 模板创建基础项目

    Windows PowerShell 可执行:

    # 在当前目录生成 demo-app 项目,不进入交互式选择
    mvn archetype:generate `
    "-DgroupId=com.example" `
    "-DartifactId=demo-app" `
    "-DarchetypeGroupId=org.apache.maven.archetypes" `
    "-DarchetypeArtifactId=maven-archetype-quickstart" `
    "-DarchetypeVersion=1.5" `
    "-DinteractiveMode=false"

    Linux/macOS 可执行:

    # 反斜杠表示命令换行,实际仍是一条命令
    mvn archetype:generate \\
    -DgroupId=com.example \\
    -DartifactId=demo-app \\
    -DarchetypeGroupId=org.apache.maven.archetypes \\
    -DarchetypeArtifactId=maven-archetype-quickstart \\
    -DarchetypeVersion=1.5 \\
    -DinteractiveMode=false

    生成后进入项目目录并验证:

    # 进入新项目根目录
    cd demo-app

    # 执行模板自带的测试并完成构建验证
    mvn clean verify

    Archetype 适合快速获得基础目录,但真实工程通常使用团队模板、框架初始化工具或已有项目骨架。生成后仍需检查 POM 中的 Java、依赖和插件版本。

    7.11 Maven Wrapper 命令

    添加或更新 Wrapper

    # 按当前 Maven 版本向项目加入 Wrapper 文件
    mvn wrapper:wrapper

    # 明确要求 Wrapper 使用 Maven 3.9.16
    mvn wrapper:wrapper -Dmaven=3.9.16

    执行后项目通常新增:

    .mvn/
    mvnw
    mvnw.cmd

    这些文件应提交到版本库,使其他成员无需预先安装完全相同的 Maven 版本。

    使用 Wrapper 构建

    # Windows PowerShell
    .\\mvnw.cmd clean verify

    # Linux/macOS
    ./mvnw clean verify

    第一次执行 Wrapper 时可能需要联网下载指定 Maven 版本,后续会复用用户目录中的缓存。

    7.12 多模块项目命令

    假设父项目包含以下模块:

    parent-project/
    ├─ pom.xml
    ├─ common/
    ├─ service/
    └─ web/

    以下命令应在父项目根目录执行。

    构建指定模块

    # 只构建 artifactId 为 service 的模块
    mvn -pl :service clean verify

    # 使用相对路径指定模块
    mvn -pl service clean verify

    同时构建模块依赖项

    # 构建 service,并自动构建 service 依赖的同一 Reactor 内模块
    mvn -pl :service -am clean verify

    -am 是 –also-make,常用于 service 依赖 common 的情况。

    同时构建依赖当前模块的下游模块

    # 构建 common,并同时构建 Reactor 中依赖 common 的模块
    mvn -pl :common -amd clean verify

    -amd 是 –also-make-dependents,适合修改基础模块后验证受影响模块。

    从失败模块继续

    # 从 service 模块开始继续 Reactor 构建
    mvn -rf :service verify

    修复多模块构建中的失败模块后,可以根据 Maven 失败日志给出的提示使用 -rf,减少重复构建已成功模块。

    只构建父项目本身

    # 不递归进入子模块,只执行当前 POM
    mvn -N validate

    -N 是 –non-recursive,常用于只检查父 POM 或执行聚合层操作。

    多线程构建

    # 使用 4 个线程并行构建模块
    mvn -T 4 clean verify

    # 每个 CPU 核心使用 1 个构建线程
    mvn -T 1C clean verify

    多线程构建只适用于线程安全的插件。出现偶发失败、输出混乱或插件明确不支持并行时,应去掉 -T 重新验证。

    多模块失败处理

    # 第一个模块失败时立即停止,默认行为
    mvn -ff clean verify

    # 当前模块失败后,继续构建不受影响的模块,最后整体失败
    mvn -fae clean verify

    # 即使模块失败也不让 Maven 返回失败,不推荐用于正式流水线
    mvn -fn clean verify

    持续集成通常使用默认 -ff 或根据项目需要使用 -fae。-fn 会掩盖失败状态,应谨慎使用。

    7.13 常用命令行选项速查

    选项完整形式作用
    -h –help 显示 Maven 命令帮助
    -v –version 显示版本后退出
    -V –show-version 显示版本并继续构建
    -Dname=value –define 定义用户属性
    -Pdev –activate-profiles 激活 Profile
    -f –file 指定其他 POM 或项目目录
    -s –settings 指定用户 settings.xml
    -gs –global-settings 指定全局 settings.xml
    -U –update-snapshots 强制检查缺失 Release 和更新 SNAPSHOT
    -nsu –no-snapshot-updates 禁止更新 SNAPSHOT
    -o –offline 离线构建
    -B –batch-mode 非交互批处理模式
    -ntp –no-transfer-progress 隐藏下载和上传进度
    -q –quiet 只显示错误日志
    -e –errors 显示完整异常信息
    -X –debug 显示调试日志
    -l –log-file 将构建日志写入文件
    -T –threads 多线程构建 Reactor
    -N –non-recursive 不递归构建子模块
    -pl –projects 选择要构建的模块
    -am –also-make 同时构建所选模块依赖的模块
    -amd –also-make-dependents 同时构建依赖所选模块的模块
    -rf –resume-from 从指定模块继续构建
    -ff –fail-fast 首次失败后停止,默认行为
    -fae –fail-at-end 继续不受影响模块,最后报告失败
    -fn –fail-never 始终不返回失败,不推荐用于正式构建

    7.14 开发场景命令选择

    开发场景推荐命令
    刚拉取项目,确认能否正常构建 mvn clean verify
    只检查正式代码能否编译 mvn compile
    只检查测试代码能否编译 mvn test-compile
    运行全部单元测试 mvn test
    运行一个测试类 mvn test -Dtest=类名
    生成 JAR/WAR mvn clean package
    提交代码前完整检查 mvn clean verify
    让其他本地项目使用当前模块 mvn clean install
    排查依赖版本冲突 mvn dependency:tree
    检查依赖声明是否合理 mvn dependency:analyze
    查看父 POM 和 Profile 合并结果 mvn help:effective-pom -Dverbose
    查看镜像、私服和本地仓库设置 mvn help:effective-settings
    SNAPSHOT 没有更新 mvn -U clean verify
    查看详细失败原因 mvn -e clean verify
    Maven 行为难以解释 mvn -X clean verify
    构建指定模块及其依赖 mvn -pl :模块名 -am clean verify
    修复失败模块后继续 mvn -rf :模块名 verify
    持续集成构建 mvn -B -ntp clean verify

    8. 完整项目案例

    8.1 案例需求

    创建一个命令行任务管理器,实现以下功能:

  • 添加非空任务;
  • 保存任务添加顺序;
  • 查询全部任务;
  • 使用 JUnit 验证正常添加与空任务校验;
  • 使用 Maven 测试、打包并运行 JAR。
  • 项目 POM 已在 3. POM 项目配置 中完成,本节直接创建源码和测试文件。

    8.2 实现思路

    TaskService 负责保存和校验任务,App 负责演示程序入口,TaskServiceTest 负责自动验证业务行为。

    App
    ↓ 调用
    TaskService
    ↓ 保存
    内存中的 List<String>

    TaskServiceTest
    ↓ 通过 JUnit 调用并断言
    TaskService

    该案例不引入运行时第三方依赖,因此 Maven 生成的 JAR 可以直接通过 java -jar 运行。JUnit 只在测试阶段使用。

    8.3 任务服务

    文件位置:src/main/java/com/example/task/TaskService.java

    package com.example.task;

    import java.util.ArrayList;
    import java.util.List;

    /**
    * 管理任务的添加与查询。
    * 当前示例使用内存保存数据,程序退出后数据不会持久化。
    */
    public class TaskService {

    // 使用 List 保存任务,以保留任务的添加顺序。
    private final List<String> tasks = new ArrayList<>();

    /**
    * 添加一个任务。
    *
    * @param taskTitle 任务标题
    * @throws IllegalArgumentException 当标题为 null、空字符串或仅包含空格时抛出
    */
    public void addTask(String taskTitle) {
    // 在业务入口统一校验,避免无效任务进入集合。
    if (taskTitle == null || taskTitle.isBlank()) {
    throw new IllegalArgumentException("任务标题不能为空");
    }

    // 去除标题首尾空格,使保存结果保持一致。
    tasks.add(taskTitle.trim());
    }

    /**
    * 返回当前全部任务。
    *
    * @return 不可修改的任务副本,避免调用方直接改变内部集合
    */
    public List<String> listTasks() {
    // List.copyOf 创建只读副本,保护 TaskService 内部状态。
    return List.copyOf(tasks);
    }
    }

    关键点:

    • TaskService 不处理终端输入输出,只负责业务逻辑;
    • isBlank() 能识别空字符串和只包含空格的字符串;
    • trim() 统一清理任务标题两侧空格;
    • List.copyOf() 防止外部代码直接修改内部任务列表。

    8.4 程序入口

    文件位置:src/main/java/com/example/task/App.java

    package com.example.task;

    /**
    * 命令行任务管理器的程序入口。
    */
    public class App {

    public static void main(String[] args) {
    // 创建任务服务,后续所有任务操作都通过该对象完成。
    TaskService taskService = new TaskService();

    // 添加两条示例任务,用于验证程序的基本功能。
    taskService.addTask("学习 Maven POM");
    taskService.addTask("执行 Maven 构建");

    System.out.println("当前任务:");

    // 按添加顺序输出任务,并从 1 开始显示序号。
    for (int index = 0; index < taskService.listTasks().size(); index++) {
    String task = taskService.listTasks().get(index);
    System.out.printf("%d. %s%n", index + 1, task);
    }
    }
    }

    pom.xml 中的主类配置必须与这里的完整类名一致:

    com.example.task.App

    8.5 单元测试

    文件位置:src/test/java/com/example/task/TaskServiceTest.java

    package com.example.task;

    import org.junit.jupiter.api.Test;

    import java.util.List;

    import static org.junit.jupiter.api.Assertions.assertEquals;
    import static org.junit.jupiter.api.Assertions.assertThrows;

    /**
    * 验证 TaskService 的核心业务行为。
    */
    class TaskServiceTest {

    @Test
    void addTaskShouldStoreValidTask() {
    // 准备一个全新的服务,保证测试之间互不影响。
    TaskService taskService = new TaskService();

    // 添加前后带空格的任务,验证保存时会清理首尾空格。
    taskService.addTask(" 学习 Maven ");

    // 任务列表应只包含清理后的标题。
    assertEquals(List.of("学习 Maven"), taskService.listTasks());
    }

    @Test
    void addTaskShouldRejectBlankTitle() {
    // 准备测试对象。
    TaskService taskService = new TaskService();

    // 空白标题必须抛出异常,避免无效数据进入任务列表。
    IllegalArgumentException exception = assertThrows(
    IllegalArgumentException.class,
    () -> taskService.addTask(" ")
    );

    // 同时验证异常信息,确保错误原因明确。
    assertEquals("任务标题不能为空", exception.getMessage());
    }
    }

    测试类默认放在 src/test/java。Surefire 通常会发现符合 *Test、Test* 等命名规则的测试类,并在 test 阶段执行。

    8.6 执行测试

    在 task-manager 根目录执行:

    # 编译正式代码与测试代码,并执行全部单元测试
    mvn test

    正常结果应包含类似信息:

    Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
    BUILD SUCCESS

    测试报告位于:

    target/surefire-reports/

    验证重点:

    • target/classes 中存在 App.class 和 TaskService.class;
    • target/test-classes 中存在 TaskServiceTest.class;
    • 测试统计为 2 个测试全部通过。

    8.7 打包项目

    # 删除旧 target,重新编译、测试并生成 JAR
    mvn clean package

    成功后应生成:

    target/task-manager-1.0.0.jar

    package 阶段会执行前面的编译和测试。只要测试失败,默认就不会生成成功的正式构建结果。

    8.8 运行 JAR

    Windows PowerShell、CMD、Linux 和 macOS 都可以在项目根目录执行:

    # 运行 Maven 生成的可执行 JAR
    java -jar target/task-manager-1.0.0.jar

    预期输出:

    当前任务:
    1. 学习 Maven POM
    2. 执行 Maven 构建

    如果出现“没有主清单属性”,说明 JAR 中没有正确写入主类,应检查 maven-jar-plugin 中的 mainClass 是否与 App 的包名和类名一致。

    8.9 案例闭环

    本案例完成了 Maven 项目的基本工程流程:

    编写 pom.xml

    按照约定创建源码和测试目录

    mvn test 执行自动化测试

    mvn clean package 生成 JAR

    java -jar 运行构建结果

    真实项目可以在此基础上继续扩展数据库、日志、Web 框架和多模块结构,但 Maven 的核心流程仍然是 POM、依赖、生命周期和插件之间的配合。

    9. 常见问题与排查

    9.1 终端无法识别 mvn

    问题表现

    mvn: command not found

    或:

    'mvn' 不是内部或外部命令

    检查方法

    # 检查 Maven 命令是否进入 PATH
    mvn -v

    常见原因

    • Maven 未正确配置到 PATH;
    • 修改环境变量后,当前终端尚未重新打开;
    • 项目本应使用 Maven Wrapper,但执行了全局 mvn。

    解决方法

    • 重新检查 Maven 的 bin 目录是否位于 PATH;
    • 关闭并重新打开终端;
    • 项目包含 mvnw.cmd 时,Windows PowerShell 可执行 .\\mvnw.cmd -v;Linux/macOS 可执行 ./mvnw -v。

    9.2 Maven 使用了错误的 Java 版本

    问题表现

    release version 17 not supported

    或构建时显示的 Java 版本与预期不同。

    检查方法

    # 同时查看 Maven 实际使用的 Java 版本和路径
    mvn -v

    # 查看终端默认 Java 版本
    java -version

    常见原因

    • JAVA_HOME 指向旧 JDK;
    • IDE 使用 JDK 17,但终端 Maven 使用另一套 JDK;
    • 只修改了 PATH,没有同步修改 JAVA_HOME。

    解决方法

    使 mvn -v 中的 Java version 和 Java home 指向项目要求的 JDK 17,然后重新打开终端执行:

    # 清理旧编译结果后重新验证
    mvn clean verify

    9.3 依赖无法下载

    问题表现

    Could not resolve dependencies

    或:

    Could not transfer artifact

    检查方法

  • 检查网络是否能够访问远程仓库;
  • 检查用户目录 .m2/settings.xml 中的镜像、代理和私服配置;
  • 核对 POM 中的 groupId、artifactId 和 version;
  • 使用 -U 强制重新检查更新。
  • # 强制更新依赖并显示详细错误
    mvn -U clean verify

    解决方法

    • 修正错误坐标或仓库地址;
    • 企业项目使用正确的私服镜像;
    • 删除本地仓库中该依赖对应目录里的失败缓存文件后重新构建;
    • 不要在 POM 中写入明文仓库密码。

    9.4 出现 package … does not exist

    问题表现

    package org.example.xxx does not exist

    常见原因

    • POM 未声明所需依赖;
    • 依赖的 scope 错误,例如正式代码使用了 test 依赖;
    • 依赖版本不包含当前使用的类;
    • IDE 已手工添加 JAR,但 POM 没有对应声明。

    检查方法

    # 确认依赖是否真实进入 Maven 类路径
    mvn dependency:tree

    解决方法

    将依赖正确声明在 pom.xml,调整 scope,然后执行:

    # 清理 IDE 或旧构建产生的缓存影响
    mvn clean compile

    9.5 测试没有执行

    问题表现

    构建成功,但输出中显示:

    Tests run: 0

    常见原因

    • 测试类放错目录;
    • 测试类命名不符合 Surefire 默认发现规则;
    • 使用了错误的 @Test 导入;
    • POM 缺少 JUnit 依赖;
    • 命令中使用了跳过测试参数。

    检查方法

    • 文件应位于 src/test/java;
    • 当前案例测试类名应为 TaskServiceTest;
    • 注解应导入 org.junit.jupiter.api.Test;
    • 执行指定测试类确认。

    # 明确要求执行当前测试类
    mvn test -Dtest=TaskServiceTest

    9.6 测试失败导致无法打包

    问题表现

    There are test failures
    BUILD FAILURE

    检查方法

    • 查看终端中第一个失败测试;
    • 打开 target/surefire-reports 中的文本或 XML 报告;
    • 对比期望值与实际值;
    • 单独执行失败测试方法。

    # 只运行失败的方法,缩小排查范围
    mvn test -Dtest=TaskServiceTest#addTaskShouldStoreValidTask

    解决方法

    修复业务代码或测试预期后重新执行 mvn clean verify。只有在临时确认非测试环节时才使用 -DskipTests,不要把跳过测试作为长期解决方式。

    9.7 JAR 无法通过 java -jar 运行

    问题表现

    no main manifest attribute

    或:

    Could not find or load main class

    检查方法

  • 确认 App.java 的包名为 com.example.task;
  • 确认 POM 中 mainClass 为 com.example.task.App;
  • 执行干净打包,避免运行旧 JAR。
  • # 重新生成包含最新清单配置的 JAR
    mvn clean package

    9.8 修改配置后结果没有变化

    常见原因

    • 实际执行命令的目录不是当前项目根目录;
    • 运行了旧的 target 文件;
    • IDE 尚未重新加载 POM;
    • 父 POM、Profile 或默认配置覆盖了当前设置。

    检查方法

    # 查看当前 Maven 真正使用的最终配置
    mvn help:effective-pom

    # 删除旧结果并完整验证
    mvn clean verify

    同时确认终端当前目录中存在正确的 pom.xml。

    10. 总结与速查

    10.1 核心关系

    pom.xml
    ├─ 项目坐标:项目是谁
    ├─ properties:集中保存版本和参数
    ├─ dependencies:项目代码需要什么类库
    └─ build/plugins:构建过程由哪些工具执行

    Maven 生命周期
    ├─ compile:编译正式代码
    ├─ test:执行单元测试
    ├─ package:生成 JAR/WAR
    ├─ verify:执行完整验证
    ├─ install:安装到本地仓库
    └─ deploy:发布到远程仓库

    10.2 高频命令

    分类目标命令
    环境 查看 Maven 与 Java 环境 mvn -v
    帮助 查看命令行参数 mvn -h
    配置 查看最终 POM mvn help:effective-pom -Dverbose
    配置 查看最终 settings mvn help:effective-settings
    配置 查看激活的 Profile mvn help:active-profiles
    生命周期 清理构建结果 mvn clean
    生命周期 编译正式源码 mvn compile
    生命周期 编译测试源码 mvn test-compile
    生命周期 执行单元测试 mvn test
    生命周期 生成 JAR/WAR mvn clean package
    生命周期 完整质量验证 mvn clean verify
    生命周期 安装到本地仓库 mvn clean install
    生命周期 发布到远程仓库 mvn clean deploy
    测试 运行指定测试类 mvn test -Dtest=TaskServiceTest
    测试 运行指定测试方法 mvn test "-Dtest=TaskServiceTest#方法名"
    测试 跳过测试执行 mvn package -DskipTests
    依赖 查看依赖树 mvn dependency:tree
    依赖 分析依赖声明 mvn dependency:analyze
    依赖 为离线构建预下载 mvn dependency:go-offline
    依赖 重新解析项目依赖 mvn dependency:purge-local-repository
    仓库 强制检查远程更新 mvn -U clean verify
    仓库 离线构建 mvn -o clean verify
    Profile 激活开发环境配置 mvn clean verify -Pdev
    排错 显示完整异常 mvn -e clean verify
    排错 显示调试日志 mvn -X clean verify
    Wrapper Windows 统一版本构建 .\\mvnw.cmd clean verify
    Wrapper Linux/macOS 统一版本构建 ./mvnw clean verify
    多模块 构建指定模块及其依赖 mvn -pl :模块名 -am clean verify
    多模块 从失败模块继续 mvn -rf :模块名 verify
    CI 非交互且隐藏传输进度 mvn -B -ntp clean verify

    10.3 依赖范围速查

    scope主要用途
    compile 正式编译和运行都需要,默认值
    runtime 编译主代码不需要,运行时需要
    test 仅测试代码和测试执行需要
    provided 编译需要,运行环境负责提供
    import 在 dependencyManagement 中导入 BOM
    system 直接绑定本机 JAR,应避免使用

    10.4 工程实践

    • 将 pom.xml、src、.mvn、mvnw 和 mvnw.cmd 提交到版本库;
    • 不提交 target、IDE 缓存和本地仓库;
    • 明确固定关键依赖与插件版本,避免不同环境得到不同构建行为;
    • 依赖冲突先执行 mvn dependency:tree,配置异常先执行 mvn help:effective-pom;
    • 不手工修改 target,也不要把 IDE 手工添加的 JAR 当作 Maven 依赖;
    • 多模块项目使用父 POM 和 dependencyManagement 统一版本;
    • 团队项目优先使用 Maven Wrapper,减少成员本机 Maven 版本差异;
    • 提交或合并代码前执行 mvn clean verify;
    • 初学阶段先掌握单模块 JAR 项目,再学习父子模块、BOM、Profile、私服和发布流程。

    最需要记住的一句话

    Maven 通过 pom.xml 描述项目,按照生命周期调用插件,并从仓库解析依赖,最终在 target 中生成可验证、可重复的构建结果。

    赞(0)
    未经允许不得转载:171主机测评 » Java项目管家-Maven
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址