Skip to content

S01-08 Servlet-Maven ​

[TOC]

概述 ​

核心定义 ​

Maven 是 Apache 软件基金会维护的开源 Java 项目管理与构建自动化工具。它以项目对象模型(Project Object Model, POM)为核心,将 Java 项目的编译、测试、打包、部署以及第三方依赖包管理整合为一套标准化的流水线作业。

诞生背景 ​

在传统 Java 开发阶段(如早期通过手动管理 Jar 包或编写纯 Ant 脚本构建),开发流程面临三个痛点:

  • Jar 包依赖混乱:三方库需手动下载并拷贝到工程 lib 目录,容易引起版本漂移且代码仓库异常臃肿。
  • 间接依赖冲突:类库之间的间接依赖容易导致运行时抛出 NoSuchMethodError 或 ClassNotFoundException。
  • 构建规范割裂:各团队工程结构与编译脚本形式各异,新人接入和跨项目迁移成本高昂。

Maven 通过统一的元数据规范和中央化仓库机制,彻底替代了手工管理模式。

核心理念 ​

Maven 遵循“约定优于配置”(Convention Over Configuration)的设计哲学。

传统构建工具需要手动指定源码路径、资源存放位置以及编译产物目录。Maven 预设了一套工业界通用的标准约定:

  • 开发者遵循固定的目录层级存放代码与资源,无需编写脚本即可直接执行编译、打包等操作。
  • 只有在项目有特殊定制需求(如更改源码编码、自定义打标行为)时,才需要在配置文件中显式覆盖默认规则。

基础结构 ​

目录规范:

标准 Maven 工程遵循以下分层结构:

  • src/main/java:业务主代码。
  • src/main/resources:业务主配置文件及静态资源。
  • src/test/java:自动化测试用例源码。
  • src/test/resources:测试专用的配置文件。
  • target:临时编译产物与最终生成的打包文件(如 Jar、War)。
  • pom.xml:项目对象模型描述文件,置于工程根目录。

最小配置:

每个 Maven 工程的核心都是根目录下的 pom.xml。一个最小可用工程仅需声明基本的坐标属性与打包格式:

xml
<project xmlns="http://maven.apache.org/POM/4.0.0">
  <!-- 声明工程的唯一坐标元数据 -->
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.example</groupId>
  <artifactId>quickstart-demo</artifactId>
  <version>1.0.0</version>
  <!-- 声明当前模块的输出包格式 -->
  <packaging>jar</packaging>
</project>

核心要素 ​

Maven 将项目的元数据、外部依赖、仓库系统以及构建过程统一抽象并解耦:

  • POM(项目对象模型):使用 XML 语言统一描述工程的静态元数据、外部类库依赖、开发者信息和插件配置。
  • 依赖管理系统:通过 groupId、artifactId、version 三元坐标唯一定位网络构件,并自动化处理依赖的树形传递与版本仲裁。
  • 分级仓库体系:构建所需的构件按照“本地仓库 -> 局域网私服(Nexus) -> 互联网中央仓库(Maven Central)”的三级拓扑进行查找与落盘缓存。
  • 构建生命周期:抽象出独立且标准的构建流程(clean 清理、default 构建核心、site 文档生成),使得不同的构建行为具备统一的操作界面。
  • 插件驱动机制:Maven 核心程序仅负责生命周期调度与阶段驱动,实际的编译、测试、打包行为均委托给插件(Plugin)的目标(Goal)完成。

构建运转流程 ​

在开发与持续集成场景中,构建动作由标准化流程按步执行:

  1. 配置读取与依赖仲裁:解析 pom.xml,校验工程结构规范,并向本地或远程仓库拉取解析依赖树。

  2. 生命周期阶段驱动:当触发具体阶段命令(如 mvn package)时,Maven 按预设顺序自前向后执行前置阶段(如 validate -> compile -> test -> package)。

  3. 插件任务派发:各个阶段依次唤醒其绑定的插件目标(如编译插件 maven-compiler-plugin),完成字节码生成、测试报告导出以及压缩归档。

安装与配置 ​

Maven 基于 Java 平台运行,其安装本质是解压二进制包并配置系统 PATH 变量,核心配置则通过修改 settings.xml 来接管仓库路径、镜像分发与网络凭证。

环境准备 ​

依赖需求

Maven 依靠 JDK 执行项目的编译与分析,安装前必须确保操作系统中已安装合适版本的 JDK(当前主流推荐 JDK 8、17 或更高版本)。


环境校验

在终端或控制台执行以下命令,确认系统已识别到有效的 Java 开发环境:

bash
# 验证 JDK 是否安装以及环境变量是否生效
java -version
javac -version

系统必须正确输出 Java 运行时版本号与编译器版本号,且系统环境变量中已配置指向 JDK 根目录的 JAVA_HOME。

下载解压 ​

介质获取

访问 Apache Maven 官方下载页面,根据操作系统下载对应的二进制压缩包(Binary distribution):

  • Windows 环境推荐下载:apache-maven-x.y.z-bin.zip
  • Linux / macOS 环境推荐下载:apache-maven-x.y.z-bin.tar.gz

目录结构

将下载的压缩文件解压至无中文字符、无空格的物理路径(如 /usr/local/apache-maven 或 C:\opt\apache-maven)。解压后的核心目录职责如下:

  • bin:包含可执行脚本(mvn、mvn.cmd)。
  • boot:包含 Maven 内部类加载器依赖包(Plexus Classworlds)。
  • conf:配置文件目录,包含全局核心配置 settings.xml。
  • lib:Maven 运行时依赖的核心 Jar 库。

环境变量 ​

变量声明

为了让终端在任何工作目录下均能调用 mvn 命令,需要将 Maven 的 bin 路径注入系统全局环境变量。


多系统配置

  1. Windows 系统:

    • 打开“系统属性” -> “高级系统设置” -> “环境变量”。
    • 新建系统变量:变量名设为 MAVEN_HOME,变量值填入 Maven 的解压根路径(如 C:\opt\apache-maven-3.9.16)。
    • 编辑 Path 系统变量,添加新建项 %MAVEN_HOME%\bin。
  2. macOS / Linux 系统:

    • 打开终端配置文件(如 ~/.bashrc 或 ~/.zshrc)。

    • 写入配置:

      bash
      export MAVEN_HOME=/usr/local/apache-maven-3.9.16
      export PATH=$MAVEN_HOME/bin:$PATH
    • 执行 source ~/.zshrc 或 source ~/.bashrc 使变更即刻生效。


终端验证

重新打开终端窗口,输入命令检测安装结果:

bash
mvn -v

若终端打印出 Maven 版本、Maven home 目录路径以及识别到的 Java runtime 版本,即表明全局安装成功。

基础配置 ​

配置层级 ​

Maven 配置中枢为 settings.xml,分为两个层级:

  • 全局配置:位于 Maven 安装目录下的 conf/settings.xml,对使用该 Maven 实例的所有机器用户生效。
  • 用户配置:位于用户主目录的 ~/.m2/settings.xml,作用范围仅限于当前操作系统用户,且该配置会优先覆盖全局配置。推荐复制一份 settings.xml 至用户目录中按需修改。

本地仓库 ​

Maven 默认将拉取的构件保存在系统盘用户目录(~/.m2/repository)中。为了防止系统盘空间不足,可在 settings.xml 中指定自定义磁盘路径:

xml
<settings xmlns="http://maven.apache.org/SETTINGS/1.2.0">
  <!-- 指定本地仓库存储依赖包的自定义物理路径 -->
  <localRepository>/usr/local/maven-repository</localRepository>
</settings>

镜像源加速 ​

Maven 官方中央仓库服务器位于海外,国内下载构件易出现超时中断。通过在 <mirrors> 标签内配置公共镜像代理(如阿里云镜像服务),可大幅提升依赖下载速率:

xml
<mirrors>
  <!-- 配置中央仓库的国内镜像节点以加快依赖下载速度 -->
  <mirror>
    <id>aliyunmaven</id>
    <mirrorOf>central</mirrorOf>
    <name>Aliyun Public Mirror</name>
    <url>https://maven.aliyun.com/repository/public</url>
  </mirror>
</mirrors>

进阶配置 ​

编译环境统一 ​

新版 Maven 默认的 Java 编译级别可能较低。为了避免每个子项目重复配置 maven-compiler-plugin,可在 settings.xml 中配置全局编译规范:

xml
<profile>
  <!-- 全局指定项目构建时默认使用的 Java 编译版本 -->
  <id>jdk-17</id>
  <activation>
    <activeByDefault>true</activeByDefault>
    <jdk>17</jdk>
  </activation>
  <properties>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
    <maven.compiler.compilerVersion>17</maven.compiler.compilerVersion>
  </properties>
</profile>

私服认证 ​

当企业内部搭建 Nexus 或 Artifactory 架构时,拉取和发布私有 Jar 包通常需要访问凭据。出于安全性考量,账号密码不应写入项目的 pom.xml,而应定义在 settings.xml 的 <servers> 模块中:

xml
<servers>
  <!-- 企业私有仓库 Nexus 的访问凭证 -->
  <server>
    <id>nexus-releases</id>
    <username>deployment-user</username>
    <password>SafePassword2026</password>
  </server>
</servers>

仓库检索流程 ​

当项目声明构件依赖时,Maven 解析外部库的流转判定逻辑如下:

  1. 本地匹配:先检查本地仓库(localRepository)是否存在该版本构件,若命中且校验通过直接使用。

  2. 私服/Profile 检查:若本地无对应文件,按照 settings.xml 中激活的私服或仓库列表发起拉取请求。

  3. 镜像拦截:如果配置了匹配的 <mirrorOf> 规则,请求将被转交至指定的 Mirror 节点。

  4. 远程下载并落地缓存:构件自中央仓库或远程私服完成网络流传输,保存至本地缓存文件系统,以备后续工程离线复用。

工具集成 ​

IDEA 设置

在现代化 IDE(如 IntelliJ IDEA)中,推荐将自带的 Bundled Maven 替换为本地配置好的实例,确保本地构建与开发工具表现一致:

  1. 打开 Settings (或 Preferences) -> Build, Execution, Deployment -> Build Tools -> Maven。

  2. Maven home path:选择自定义安装解压的物理路径。

  3. User settings file:勾选 Override,指定配置好的 settings.xml 文件。

  4. Local repository:勾选 Override,指向自定义本地仓库路径。


构建性能优化

在 settings.xml 同级目录或工程根目录建立 .mvn/maven.config 文件,可固化高频构建参数以加速执行:

  • 并发多线程编译:输入 -T 1C(即按每核心 1 个线程并行构建模块)。
  • 跳过无改动测试:添加 -DskipTests 加快发布打包流程。

IDEA 工程构建 ​

在 IntelliJ IDEA 中构建 Java SE 与 Java Web 工程,核心在于通过 Maven 统一工程模型,将编译、测试与打包完全托管于构建生命周期,摆脱繁琐的手动类库引用。

环境预设 ​

全局环境隔离

在 IDEA 中配置 Maven 时,如果仅在打开的项目内修改配置,后续新建项目仍会重置为 IDEA 自带的 Bundled Maven。因此必须优先配置全局默认模板:

  1. 打开 IDEA 欢迎界面或顶部菜单栏,依次进入 File -> New Projects Setup -> Settings for New Projects。

  2. 展开左侧菜单至 Build, Execution, Deployment -> Build Tools -> Maven。

  3. Maven home path:指定本地安装的 Maven 物理路径(如 /usr/local/apache-maven 或自定义解压目录)。

  4. User settings file:勾选右侧 Override,选择定制后的 settings.xml。

  5. Local repository:勾选 Override,指定自定义的本地依赖缓存目录。


工程目录规范

无论是 SE 还是 Web 工程,IDEA 均严格遵循 Maven 标准目录组织规范:

  • src/main/java:编译为 class 字节码的业务源码目录(Sources Root)。
  • src/main/resources:打包时自动输出至根类路径的资源文件(Resources Root)。
  • src/test/java:仅在测试生命周期参与编译执行的用例目录(Test Sources Root)。
  • pom.xml:模块的核心配置与坐标中枢。

SE 工程构建 ​

空白创建方式

标准 Java SE 工程打包形态通常为 Jar 包,推荐采用轻量级直建流程:

  1. 点击 File -> New -> Project。

  2. 左侧生成器选择 Maven(不勾选任何 Archetype 原型模板)。

  3. 填入项目基础元数据:

    • Name:工程模块名称(例如 se-demo)。
    • Location:工程存放的本地物理目录。
    • JDK:选择项目构建所使用的 Java 版本。
  4. 展开 Advanced Settings,设定当前工程的坐标属性:

    • GroupId:企业或组织域名反写(如 com.example)。
    • ArtifactId:模块唯一标识(如 se-demo)。
    • Version:初始版本号(默认 1.0-SNAPSHOT)。

编码与生命周期

创建完毕后,在 src/main/java 下建立包名并新建启动类:

java
// 系统主程序执行入口
public static void main(String[] args) {
  // 终端输出启动日志
  System.out.println("Maven SE Project Started.");
}

在右侧打开 Maven 工具窗口,双击展开 Lifecycle 模块:

  • 双击 compile:编译源码至 target/classes 目录。
  • 双击 package:自动执行编译与测试,在 target 目录下生成可分发的 se-demo-1.0-SNAPSHOT.jar。

Web 工程构建 ​

骨架与平滑升级

Java Web 工程的目标产物为 War 包,包含专属的动态发布资源目录(webapp)。构建 Web 工程主要有两种途径:

  1. 改造升级法(推荐):新建标准 SE 项目,在 pom.xml 中将打包方式声明为 war,再手动创建 src/main/webapp/WEB-INF/web.xml 目录与文件。该方式结构纯净,避开骨架下载卡顿。

  2. 骨架创建法:新建项目时勾选原型模板 maven-archetype-webapp。骨架生成后通常缺失 src/main/java 与 resources 文件夹,需手动右键新建对应目录并分别标记为 Sources Root 与 Resources Root。


核心依赖与作用域

Web 工程必须依赖 Servlet API 进行接口开发。在 pom.xml 中引入依赖时,必须声明 <scope>provided</scope>:

xml
<project>
  <!-- 声明构建打包产物为 Web 应用专用的 war 包 -->
  <packaging>war</packaging>
  <dependencies>
    <!-- 引入 Servlet 规范依赖并设置范围为 provided -->
    <dependency>
      <groupId>jakarta.servlet</groupId>
      <artifactId>jakarta.servlet-api</artifactId>
      <version>6.0.0</version>
      <scope>provided</scope>
    </dependency>
  </dependencies>
</project>

依赖范围说明:若未设置 provided,Maven 打包时会将 Servlet API 打入 War 包内的 WEB-INF/lib 目录,部署到 Tomcat 等容器时会与容器自带的 API 产生双亲委派类加载冲突,导致服务异常。

运行与调试 ​

插件驱动运行

无需在本地完整安装 Tomcat 容器,可以通过在 pom.xml 中集成嵌入式 Web 容器插件直接启动:

xml
<plugins>
  <!-- 配置嵌入式 Tomcat 插件实现无缝运行与热部署 -->
  <plugin>
    <groupId>org.apache.tomcat.maven</groupId>
    <artifactId>tomcat7-maven-plugin</artifactId>
    <version>2.2</version>
    <!-- 设定服务端口与上下文根路径 -->
    <configuration>
      <port>8080</port>
      <path>/</path>
    </configuration>
  </plugin>
</plugins>

配置完成后,在 IDEA 终端执行 mvn tomcat7:run,或在右侧 Maven 插件窗口双击插件命令即可启动容器。


本地容器部署

在生产与严格调试环境中,通常使用本地独立部署的 Tomcat:

  1. 点击 IDEA 顶部 Run -> Edit Configurations。

  2. 点击左上角 + 号,选择 Tomcat Server -> Local。

  3. 在 Application server 栏选择本地解压的 Tomcat 根目录。

  4. 切换到 Deployment 标签页,点击 + 选择 Artifact:

    • 选择带有 war exploded 后缀的工件(解压目录形式),支持开发期类文件与静态页面热重载。
    • 设定 Application context 访问上下文路径(例如 /api 或 /)。
  5. 点击 Debug 模式运行,支持在代码中断点跟踪请求链路。

进阶调优与规范 ​

打包插件配置

在基于注解的现代 Servlet 3.0+ 规范中,项目可以省去 web.xml 文件。为防止 maven-war-plugin 因缺少 web.xml 报错构建失败,需在 <build> 中添加保护配置:

xml
<build>
  <!-- 允许无 web.xml 情况下正常构建打出 war 包 -->
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-war-plugin</artifactId>
      <version>3.4.0</version>
      <configuration>
        <failOnMissingWebXml>false</failOnMissingWebXml>
      </configuration>
    </plugin>
  </plugins>
</build>

依赖管理 ​

Maven 的依赖管理通过统一的坐标系统定位构件,并借助树状解析引擎自动化处理第三方库的下载、传递、范围控制与版本冲突。

依赖基础 ​

坐标定义

Maven 使用三维基础坐标(GAV)在仓库中唯一定位任何构件:

  • groupId:组织或公司的反向域名标识(如 org.springframework)。
  • artifactId:模块或项目的实际名称(如 spring-core)。
  • version:当前构件的具体发布版本号(如 6.1.2)。

声明配置

在 pom.xml 中通过 <dependencies> 标签集中管理工程所需的所有直接依赖:

xml
<dependencies>
  <!-- 声明业务所需的核心依赖坐标 -->
  <dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>33.0.0-jre</version>
  </dependency>
</dependencies>

依赖范围 ​

Classpath 控制

Java 在不同阶段使用不同的类路径(Classpath)。依赖范围(Scope)用于限制依赖项在编译(Compile)、测试(Test)、运行(Runtime)三种 Classpath 中的可用性。


范围规则

通过 <scope> 属性可为依赖指定以下六种范围:

依赖范围 (Scope)编译 Classpath测试 Classpath运行 Classpath打包进产物典型示例
compile(默认)有效有效有效是commons-lang3 基础工具库
provided有效有效无效否jakarta.servlet-api,容器已内置
runtime无效有效有效是mysql-connector-j,仅运行时反射加载
test无效有效无效否junit-jupiter,仅测试阶段运行
system有效有效无效否本地绝对路径引入的非仓库 Jar 包
import无效无效无效否仅在 dependencyManagement 中导入 BOM

依赖传递 ​

传递机制

当工程 A 显式引入构件 B,而构件 B 本身依赖构件 C 时,Maven 会自动将构件 C 解析并加入工程 A 的依赖链路中,形成依赖树结构。


可选依赖与排除

当不希望某个依赖向下游工程自动传递时,可通过可选标记或显式排除进行拦截:

  • 可选依赖(Optional):由构件的维护方声明。设置 <optional>true</optional> 后,下游项目默认不引入此依赖,下游若需使用必须显式在自身 pom.xml 中声明。

  • 依赖排除(Exclusion):由当前项目的消费者声明。使用 <exclusions> 强制阻断上游依赖携带的特定传递性构件。

    xml
    <dependency>
      <!-- 引入第三方客户端并声明为可选依赖 -->
      <groupId>org.apache.curator</groupId>
      <artifactId>curator-framework</artifactId>
      <version>5.6.0</version>
      <optional>true</optional>
      <!-- 排除内部传递的低版本日志框架以避免日志冲突 -->
      <exclusions>
        <exclusion>
          <groupId>org.slf4j</groupId>
          <artifactId>slf4j-log4j12</artifactId>
        </exclusion>
      </exclusions>
    </dependency>

依赖冲突 ​

冲突成因

当不同依赖链路引入了同一构件的不同版本时(例如链路 A 引入 lib-core:1.0,链路 B 引入 lib-core:2.0),Maven 必须在依赖树中仲裁出一个最终生效版本。如果选用了不兼容的版本,可能在运行时引发 NoSuchMethodError 或 ClassNotFoundException。


仲裁规则

Maven 内部遵循两套严格的仲裁优先级原则:

  1. 短路径优先原则(Path Depth):

    在依赖树中,距离当前工程路径层级较浅的版本优先入选。

    • 链路一:Project -> Service-A -> Core(1.0)(路径长度为 2)
    • 链路二:Project -> Service-B -> Common -> Core(2.0)(路径长度为 3)
    • 最终生效:Core:1.0
  2. 先声明优先原则(First-Declared):

    当多条链路的依赖路径深度完全相同时,优先采用在当前项目 pom.xml 中排在前面的依赖分支。

    • 链路一:Project -> Module-A -> Core(1.0)(路径长度为 2)
    • 链路二:Project -> Module-B -> Core(2.0)(路径长度为 2)
    • 若 Module-A 在 XML 文件中的声明顺序先于 Module-B,则最终生效:Core:1.0

进阶治理 ​

版本锁定机制 ​

在大型微服务或多模块工程中,为避免各子模块版本分散,父工程使用 <dependencyManagement> 统筹约束依赖版本。

  • <dependencyManagement> 仅负责统一定义版本号和依赖范围规则,本身不会真正下载和引入任何 Jar 包。
  • 子模块在 <dependencies> 中引用相同构件时,无需填写 <version> 标签,自动继承父模块声明的稳定版本。

BOM 清单导入 ​

使用 BOM(Bill of Materials,物料清单)可以集中管理大型框架生态(如 Spring Boot、Spring Cloud)的全量组件版本,借助 <type>pom</type> 和 <scope>import</scope> 实现配置复用:

xml
<dependencyManagement>
  <!-- 导入 Spring Boot 官方 BOM 清单统一锁定版本 -->
  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-dependencies</artifactId>
      <version>3.2.0</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

分析与排查 ​

命令行工具

排查类冲突或冗余依赖时,可利用 Maven 自带的分析插件:

  • 查看依赖全貌:mvn dependency:tree,将整棵解析后的依赖树以缩进格式打印至终端。
  • 过滤指定依赖:mvn dependency:tree -Dincludes=org.slf4j:*,快速定位某个特定类库是由哪些路径间接引入的。
  • 分析使用健康度:mvn dependency:analyze,检测代码中直接使用了但未显式声明的依赖(Used undeclared),以及声明了但代码从未调用的冗余依赖(Unused declared)。

依赖导入报错【 ​

image-20260910180655931

固定打包名【 ​

image-20260910180845915

外部插件引入【 ​

image-20260910181000895

资源打包控制【 ​

image-20260910182057195

构建生命周期 ​

Maven 构建生命周期(Build Lifecycle)是对项目构建过程的高度抽象与标准化,它定义了项目从清理、校验、编译、测试、打包到部署的完整作业顺序。

基础概念 ​

抽象与解耦

构建生命周期本身并不执行具体的编译或打包任务,它只充当流程调度中枢。生命周期由一系列具备先后顺序的构建阶段(Phase)组成。

  • 生命周期(Lifecycle):构建流程的集合(例如清理生命周期、默认构建生命周期)。
  • 阶段(Phase):生命周期中的某个里程碑节点(例如编译 compile、打包 package)。
  • 插件目标(Plugin Goal):具体负责干活的功能单元(例如 compiler:compile)。各个阶段通过挂载不同的目标来完成实际的构建动作。

image-20260910175142030

三套周期 ​

Maven 内部维护了三套相互独立、互不干扰的生命周期体系。执行其中一套生命周期的阶段,不会触发另一套生命周期的执行。

clean 周期

负责在构建前清理项目工作区,移除上次构建遗留的中间文件与打包产物,包含三个阶段:

  1. pre-clean:执行清理前的准备工作。

  2. clean:清理输出目录(默认删除 target 文件夹)。

  3. post-clean:执行清理后的收尾与日志记录。


default 周期

项目构建的核心生命周期,涵盖从源码处理到最终分发的全过程,包含 23 个阶段。最常用的核心主干阶段如下:

  • validate:校验项目基础配置与必要依赖是否有效。
  • compile:编译主代码目录下的源码。
  • test:运行单元测试用例。
  • package:将编译生成的字节码归档为可分发格式(如 Jar 或 War)。
  • verify:运行检查程序,验证打包产物的合规性。
  • install:将打包产物安装到本地仓库,供本机其他项目依赖。
  • deploy:将最终产物推送到远程私服仓库,供团队协同使用。

site 周期

负责基于项目的元数据与配置,自动化生成静态站点说明文档,包含四个阶段:

  1. pre-site:执行生成站点文档前的预处理。

  2. site:生成项目的 HTML 站点文档。

  3. post-site:执行站点收尾与部署准备。

  4. site-deploy:将生成的站点发布到远程服务器。

核心阶段 ​

阶段流转

在单套生命周期内部,阶段之间具有严格的前后继承与依赖关系。当在命令行触发某个阶段时,Maven 会自前向后自动依次执行该生命周期中处于该阶段之前的所有阶段。

以执行 mvn package 为例,实际的推进次序如下:

  1. validate:校验元数据规范。

  2. generate-sources:处理或自动生成源码。

  3. process-sources:解析与过滤主资源文件。

  4. compile:完成主程序编译。

  5. test-compile:完成测试代码编译。

  6. test:执行测试并输出结果报告。

  7. package:打包归档生成目标文件。

若直接执行 mvn compile,则打包与测试阶段均不会触发;若执行 mvn install,则会自动先执行完整的编译、测试和打包流程。


阶段组合

若需要跨生命周期协同操作,可以在命令行中按序声明多个阶段参数,Maven 会按顺序依次调度各个生命周期:

bash
mvn clean package

上述命令会先调用 clean 生命周期 执行到 clean 阶段(清空 target 目录),随后调用 default 生命周期 执行到 package 阶段(重新编译测试并打包)。

插件绑定 ​

默认绑定机制

Maven 阶段本身仅是一组插槽,为了让开箱即用的构建生效,Maven 会根据工程的 <packaging> 打包类型,将默认插件的目标自动绑定至对应阶段:

构建阶段 (Phase)默认绑定的插件目标 (Goal)实际职责说明
process-resourcesresources:resources复制并过滤 src/main/resources 资源
compilecompiler:compile编译 src/main/java 源码至 target/classes
test-compilecompiler:testCompile编译测试源码至 target/test-classes
testsurefire:test运行测试用例并生成测试报告
package(jar)jar:jar将编译后的 classes 归档为 Jar 文件
installinstall:install将产物安装至本地仓库 ~/.m2/repository
deploydeploy:deploy将构件上传推送至配置的远程仓库

自定义阶段绑定

当预设插件无法满足特定需求(如打包时生成源码包、构建时校验代码规范)时,可以在 pom.xml 中将自定义插件目标挂载至特定阶段:

xml
<plugin>
  <!-- 将源码打包插件的目标绑定至 verify 阶段 -->
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-source-plugin</artifactId>
  <version>3.3.0</version>
  <executions>
    <execution>
      <id>attach-sources</id>
      <phase>verify</phase>
      <goals>
        <goal>jar-no-fork</goal>
      </goals>
    </execution>
  </executions>
</plugin>

当执行 mvn verify 或后续的 install 时,Maven 执行到 verify 阶段便会触发 maven-source-plugin 的 jar-no-fork 目标,额外输出源码包。

进阶调优 ​

阶段跳过与加速

在日常持续集成或紧急发布时,经常需要对特定耗时阶段进行精细控制:

  • 跳过测试用例执行:使用 mvn package -DskipTests(测试代码仍会被编译,但不启动运行)。
  • 完全跳过测试编译与执行:使用 mvn package -Dmaven.test.skip=true(直接忽略 test-compile 与 test 阶段)。
  • 多模块并行构建:使用 mvn clean install -T 1C(根据 CPU 核心数启动多线程并行构建相互独立的子模块)。

一一一一一一一一一一一一 ​

坐标与依赖 ​

坐标定位 ​

Maven 通过坐标在仓库中唯一定位任何构件(Artifact)。坐标由三个核心基础维度(GAV)确定:

  • groupId:组织或公司的唯一标识,通常为倒置的域名。

  • artifactId:具体模块或工程的标识名称。

  • version:当前构件的具体版本号。

    xml
    <dependency>
      <!-- 基础依赖坐标 -->
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
      <version>3.2.0</version>
      <!-- 排除传递过来的冲突依赖 -->
      <exclusions>
        <exclusion>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
      </exclusions>
    </dependency>

依赖范围 ​

依赖范围(Scope)用于控制依赖项在编译(Compile)、测试(Test)、运行(Runtime)这三种不同 Classpath 环境下的可见性:

依赖范围 (Scope)编译 Classpath测试 Classpath运行 Classpath典型场景
compile(默认)有效有效有效核心业务库,如 spring-core
provided有效有效无效容器已提供的 API,如 servlet-api
runtime无效有效有效仅运行时需要的实现,如 JDBC 驱动
test无效有效无效单元测试框架,如 junit-jupiter
system有效有效无效本地物理文件引用(需搭配 systemPath)

依赖传递与冲突 ​

当项目依赖项存在间接依赖时,Maven 会自动向下传递拉取。当多个依赖链路引入了同一构件的不同版本时,遵循两套仲裁规则:

  1. 短路径优先:优先采用调用路径最短的依赖。例如 A -> B -> C(1.0) 的依赖层级少于 A -> D -> E -> C(2.0),最终生效版本为 1.0。

  2. 先声明优先:在依赖深度完全一致时,优先采用在当前 pom.xml 中排在前面的依赖声明版本。

仓库体系 ​

仓库分类与检索机制 ​

Maven 构件的存储与查找通过分层仓库拓扑完成:

  • 本地仓库:位于本地磁盘(默认 ~/.m2/repository),存储项目构建及已拉取的缓存依赖。
  • 私服(Nexus / Artifactory):部署在局域网内部的构件服务器,隔离外网并存储内部业务包。
  • 中央仓库:由官方维护的公共互联网仓库,收录绝大多数公共开源构件。

构件检索的具体流程如下:

  1. 构建进程优先在本地仓库查找目标构件。

  2. 若本地未找到,向配置的内网私服或公共镜像节点发起拉取请求。

  3. 若私服没有缓存,私服代向外网中央仓库下载并落盘缓存,随后返回给本地仓库。

镜像与私服配置 ​

在全局 settings.xml 中配置公共镜像节点,可以提高网络下载稳定性:

xml
<mirrors>
  <!-- 配置中央仓库的公共国内镜像代理 -->
  <mirror>
    <id>aliyunmaven</id>
    <mirrorOf>central</mirrorOf>
    <name>Aliyun Public Mirror</name>
    <url>https://maven.aliyun.com/repository/public</url>
  </mirror>
</mirrors>

多模块与继承 ​

模块聚合 ​

在复杂微服务或大型系统中,聚合工程(Aggregator)充当所有子模块的统一构建入口。其打包方式必须为 pom,通过 <modules> 组织模块关系:

xml
<project>
  <!-- 聚合工程打包类型必须为 pom -->
  <groupId>com.example</groupId>
  <artifactId>parent-project</artifactId>
  <version>1.0.0</version>
  <packaging>pom</packaging>
  <modules>
    <module>common-utils</module>
    <module>order-service</module>
  </modules>
</project>

属性与继承机制 ​

继承用于集中收敛管理通用配置及依赖版本:

  • <properties>:定义统一常量与版本号变量。

  • <dependencyManagement>:声明依赖的版本锁定规范。子模块在引入对应依赖时无需显式标注版本号,从而避免团队版本割裂。

    xml
    <project>
      <!-- 父工程锁定全局依赖版本,子模块声明时不带版本号即可继承 -->
      <dependencyManagement>
        <dependencies>
          <dependency>
            <groupId>com.alibaba.fastjson2</groupId>
            <artifactId>fastjson2</artifactId>
            <version>2.0.43</version>
          </dependency>
        </dependencies>
      </dependencyManagement>
    </project>

环境配置与 Profile ​

多环境配置 ​

Profile 机制允许开发人员根据不同运行环境(开发、测试、生产)定制不同的参数和资源配置:

xml
<profiles>
  <!-- 开发环境配置项 -->
  <profile>
    <id>dev</id>
    <properties>
      <env.active>dev</env.active>
    </properties>
    <!-- 默认激活该配置环境 -->
    <activation>
      <activeByDefault>true</activeByDefault>
    </activation>
  </profile>
</profiles>

资源过滤与激活 ​

通过开启资源过滤插件(maven-resources-plugin),Maven 会在构建期将配置文件中的 ${env.active} 占位符解析替换为对应环境的实际属性值。

构建时可以通过命令行参数灵活切换目标配置:

mvn clean package -P dev