Maven

最后更新: 2025-10-16 19:43:25 +0800 +0800
原文以英文撰写。您正在阅读AI翻译版本。

Maven 通过按生命周期顺序执行插件目标来构建项目。生命周期包含按顺序排列的阶段;调用某个阶段时,会执行该生命周期中该阶段及其之前的所有阶段。插件提供目标,而生命周期绑定则根据项目的打包类型选择要运行的目标。

项目模型与坐标 #

POM 使用 groupId、artifactId、version 和 packaging 标识构件。父 POM、依赖管理和导入的 BOM 可以集中管理版本,而不会将构件添加到类路径中。因此,dependencyManagement 只有在子项目声明依赖之后,才会约束这些依赖。

继承与聚合彼此独立。子项目继承 <parent> 指定的 POM;聚合器则在 <modules> 中列出 reactor 项目,并按依赖顺序构建它们。一个 POM 可以同时承担这两种角色,但模块不必继承自其聚合器。使用以下命令检查合并后的模型和活动配置文件:

sh
mvn help:effective-pom
mvn help:active-profiles

将仓库、凭据和部署配置分开管理:

  • 仅当项目需要时,才提交项目仓库配置。
  • 将凭据和组织专用镜像放入用户或 CI 的 settings.xml,而不是 POM。
  • 显式声明依赖和插件版本,可以直接声明,也可以通过经过评审的管理部分声明。缺少插件版本会使构建依赖 Maven 的解析行为,而不是源代码树。

生命周期与阶段 #

Maven 提供 clean、default 和 site 生命周期。日常构建使用 default 生命周期:

阶段范围用途
从 validate 到 process-resources验证模型、生成输入并准备资源。
从 compile 到 test编译生产代码和测试代码,然后执行单元测试。
package创建 JAR、WAR 或其他可分发构件。
从 pre-integration-test 到 post-integration-test准备、执行并拆除集成测试环境。
verify在测试之后执行质量检查。
install将构件添加到本地仓库。
deploy将构件发布到远程仓库。

mvn verify 会到达验证阶段但不会发布构件,因此适合作为 CI 基线。mvn clean verify 还会删除之前的构建输出。clean 生命周期包含 pre-clean、clean 和 post-clean;clean 会删除生成的构建文件。site 生命周期会生成项目文档,也可以发布这些文档。

插件、目标与执行 #

插件是构建时运行的代码,而不是项目依赖。在 build/plugins 中配置处于启用状态的构建插件。使用 build/pluginManagement 为子项目或同一个 POM 提供版本和共享配置,但它们仍必须显式启用这些配置。

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

一次执行会将一个或多个目标绑定到某个阶段。特定打包类型的默认绑定会提供编译 Java 源代码、创建 JAR 等常见目标。如果执行中省略 <phase>,则使用 Mojo 的 defaultPhase;但它不会单独将任意第三方目标绑定到生命周期。

xml
<plugin>
  <groupId>org.example</groupId>
  <artifactId>example-maven-plugin</artifactId>
  <version>1.0.0</version>
  <executions>
    <execution>
      <id>generate-sources</id>
      <phase>generate-sources</phase>
      <goals><goal>generate</goal></goals>
    </execution>
  </executions>
</plugin>

可以使用前缀或完整坐标直接调用目标:

sh
mvn dependency:tree
mvn org.apache.maven.plugins:maven-dependency-plugin:3.7.1:tree

前缀解析可能会搜索已配置的插件组和仓库。当 POM 尚未声明该插件时,在自动化中优先使用完整坐标。

插件内部结构与前缀解析 #

插件构件由 <packaging>maven-plugin</packaging> 标识。其可执行单元是 Mojo,Mojo 作为 AbstractMojo 的子类实现。插件自身的 POM 可以通过 maven-plugin-plugin 定义 <goalPrefix>,该前缀会成为 CLI 中的短名称。

前缀解析分为以下几层:

  1. 如果项目已经声明了具有所请求前缀的插件,Maven 可以从有效 POM 中解析该插件;
  2. 否则,Maven 会检查内置的 org.apache.maven.plugins 组,以及 settings.xml 中声明的其他 pluginGroups;
  3. 如有必要,Maven 会回退到 maven-XXX-plugin 或 XXX-maven-plugin 等 artifactId 约定。

这套解析模型解释了为什么无需完整坐标即可运行 mvn dependency:tree,而自动化通常更倾向于使用明确的 groupId:artifactId:version:goal 形式。

@Mojo 上的 defaultPhase 属性只会为显式执行提供阶段。它不会为非官方插件创建自动绑定。生命周期绑定来自打包模型和 Maven 的内置默认绑定,而不是仅来自该注解。

依赖与作用域 #

Maven 根据作用域将直接依赖和传递依赖解析到类路径中:

作用域主代码编译主代码运行时测试编译和运行时
compileyesyesyes
providedyesnoyes
runtimenoyesyes
testnonoyes

仅当部署环境提供兼容的 API(例如 Servlet 容器)时,才使用 provided。runtime 适用于编译器不需要的驱动程序和实现。system 要求使用本地文件路径,并绕过正常的仓库解析,因此不应用于可复现构建。import 仅适用于 POM 依赖,并将 BOM 导入依赖管理。

使用 mvn dependency:tree 查找版本仲裁和排除项。Maven 的最近定义规则会在冲突的传递版本中进行选择;深度相同时,按声明顺序解析。直接声明或 dependencyManagement 条目可以使选择明确。排除项只适用于声明它的依赖路径,因此应优先使用管理部分或 BOM,而不是分散的排除项。

仓库、镜像与设置 #

仓库是构件来源。镜像是 settings.xml 中用于重定向对所选仓库请求的规则;它不会添加依赖,也不会替换仓库授权策略。

xml
<mirrors>
  <mirror>
    <id>company-proxy</id>
    <mirrorOf>central</mirrorOf>
    <url>https://repository.example.invalid/maven-central</url>
  </mirror>
</mirrors>

全局设置文件位于 Maven 安装目录下,用户设置文件位于 ~/.m2/settings.xml。CI 应通过其密钥管理机制提供最小化的设置文件。不要将仓库密码存储在 POM 中。Maven 的 <relocation> 元数据允许发布者将过时坐标重定向到替代坐标。

Maven Wrapper #

提交 mvnw、mvnw.cmd 和 .mvn/wrapper/,使开发者和 CI 使用已声明的 Maven 发行版:

sh
mvn wrapper:wrapper -Dmaven=3.9.9
./mvnw clean verify

更新 Wrapper 时,检查发行版 URL 和校验和。Wrapper 固定的是 Maven 本身;插件和依赖版本仍需要通过 POM 管理。

评论

在 GitHub 上阅读或评论(发表评论需要登录)