Maven
Maven By Example
archetype:generate
1mvn archetype:generate \
2 -DgroupId=org.sonatype.mavenbook.ch03 \
3 -DartifactId=simple \
4 -DarchetypeGroupId=org.apache.maven.archetypes \
5 -DarchetypeArtifactId=maven-archetype-quickstart \
6 -DinteractiveMode=false
这个命令是 maven-archetype-plugin
插件的 generate
目标。它的核心功能是:根据一个原型(Archetype)来生成一个新项目的骨架
插件前缀解析 (Plugin Prefix Resolution)
为什么 maven-archetype-plugin
可以直接简写为 archetype
使用?
这个规则是 Maven 内部机制和插件命名规范共同作用的结果,具体规定在以下两个地方:
- Maven 的核心内置配置 (Built-in Configuration):规定了“去哪里找”
- 插件本身的元数据 (Plugin Metadata):规定了“我叫什么”
当你执行一个命令,比如 mvn archetype:generate
时,Maven 并不知道 archetype
是什么。它会执行一个叫做“前缀解析”的查找过程,来把这个简短的前缀(prefix)映射到一个完整的插件坐标org.apache.maven.plugins:maven-archetype-plugin
(groupId:artifactId)。
遵循以下规则:
- 查找默认的插件组 (Default Plugin Groups)
Maven 的核心配置中,预先定义了一个默认的插件组列表 (pluginGroups)。其中最重要的、硬编码在程序里的就是:org.apache.maven.plugins
- 若插件元数据文件定义了
<goalPrefix>
如果插件的元数据文件(通常是META-INF/maven/plugin.xml
)中定义了<goalPrefix>
,Maven 会直接使用这个前缀来匹配插件。例如:
1<project>
2 ...
3 <build>
4 ...
5 <plugins>
6 ...
7 <plugin>
8 <artifactId>maven-plugin-plugin</artifactId>
9 <version>3.15.1</version>
10 <configuration>
11 ...
12 <goalPrefix>somePrefix</goalPrefix>
13 </configuration>
14 </plugin>
15 </plugins>
16 </build>
17</project>
上述配置将允许用户通过前缀 somePrefix 来引用你的插件,如下例所示:
1mvn somePrefix:goal
- 根据命名规范进行匹配
当 Maven 拿到一个前缀(比如 archetype)时,它会尝试在这个默认的插件组 (
org.apache.maven.plugins
) 中寻找一个符合命名规范的 artifactId。匹配的模式主要有两种:maven-${prefix}-plugin
(官方插件最常用的模式)${prefix}-maven-plugin
同样的道理也适用于其他官方插件:mvn compiler:compile -> compiler
被解析为maven-compiler-plugin
mvn surefire:test -> surefire
被解析为maven-surefire-plugin
在哪里可以自定义插件组(Plugin Groups)?
可以在 settings.xml
文件中扩展插件组列表 (pluginGroups),添加 <pluginGroups>
标签,把常用的第三方插件的 groupId 加进去。
例如: 如果你经常使用 Spring Boot 的插件,它的 groupId 是 org.springframework.boot。你可以这样配置:
1<settings>
2 ...
3 <pluginGroups>
4 <pluginGroup>org.springframework.boot</pluginGroup>
5 </pluginGroups>
6 ...
7</settings>
配置好之后,原本需要输入完整命令:
1mvn org.springframework.boot:spring-boot-maven-plugin:run
现在就可以简写为:
1mvn spring-boot:run
Maven 会在默认的 org.apache.maven.plugins
中查找失败后,继续在你配置的 org.springframework.boot
组中查找,并成功匹配到 spring-boot-maven-plugin
我们来解析一下这些 -D 参数:
- -DgroupId, -DartifactId, -Dversion: 这三个定义了你的新项目的坐标。
- -DarchetypeGroupId, -DarchetypeArtifactId, -DarchetypeVersion: 这三个定义了你使用的原型的坐标。generate 命令需要明确知道你想用哪个模板来创建项目。
- -DinteractiveMode=false: 这个参数告诉 Maven 关闭交互模式,直接使用命令行提供的参数创建项目
事实上这里体现了约定优于配置(Convention over Configuration)原则。我们只是传入了 groupId 和 artifactId 这两个项目信息,Maven 就能根据这些信息和预定义的原型模板来生成一个完整的项目结构。
Maven 插件架构
Maven 核心本身非常小,它只包含了解析命令、管理 pom.xml 和下载插件等最基础的功能。所有具体的工作(如编译、打包)都委托给插件完成,并且这些插件只在需要时才会被下载。这种插件化架构将“做什么”(Maven 核心的生命周期)和“怎么做”(插件的具体实现)彻底解耦,带来了轻量、标准、灵活、可重用和易于维护的巨大优势。
一个Maven插件(Maven plugin)是一组或多个目标(goals)的集合。目标(goals)是一个具体任务,可以作为独立目标执行,也可以与其他目标一起作为更大构建的一部分执行。目标(goals)通过配置属性配置,这些属性可用于定制行为。例如,Compiler 插件的编译目标定义了一组配置参数,这些参数允许您指定目标 JDK 版本或是否使用编译器优化。
Maven 生命周期
Maven 的 构建生命周期 (Build Lifecycle) 是一个项目构建过程的有序步骤序列。
这个序列中的每一步都称为一个阶段 (Phase)。最常用的生命周期是默认生命周期 (Default Lifecycle),它从验证项目开始,到部署项目结束。
阶段本身只是一个“标记”,它本身不做任何事。真正的工作是由绑定(或附加)到这些阶段上的插件目标 (Plugin Goals) 完成的。
当 Maven 按顺序执行生命周期时,它会触发每个阶段上绑定的所有插件目标。从生命周期的起点开始,按顺序执行直到你指定的阶段为止。一个阶段可以绑定零个或多个目标。
生命周期阶段的定义是故意设计得比较模糊的,其具体含义取决于项目的类型。
例子:package 阶段对于一个普通 Java 项目(打包方式为 jar),意味着“创建一个 JAR 文件”,因此会绑定 jar:jar 这个目标。但对于一个 Web 项目(打包方式为 war),同一个 package 階段则可能意味着“创建一个 WAR 文件”。
Maven 的生命周期是 Maven 的核心定义的吗?
是的,绝对是。
Maven 的生命周期(Lifecycle)及其包含的阶段(Phase)的定义、顺序和骨架,是 Maven 框架最核心、最基础的组成部分。可以这样理解:
生命周期和阶段 (Lifecycle & Phase):是 Maven 定义的一套标准化的流程和步骤(比如先编译、再测试、后打包)。这个“流程图”是内置在 Maven 核心中的,它规定了“做什么”以及“按什么顺序做”。 插件 (Plugin):是具体“干活的人”。它们负责执行每个步骤的实际工作。 所以,Maven 核心提供了不变的、标准化的构建流程框架,而插件则提供了可变的、可配置的具体实现。这种分离正是 Maven 强大和灵活的关键。
完整的生命周期是什么?
A. default
生命周期(最重要的构建生命周期)
validate
: 验证项目是否正确以及所有必需的信息是否可用。initialize
: 初始化构建状态,例如设置属性。generate-sources
: 生成任何需要包含在编译过程中的源代码。process-sources
: 处理源代码,例如过滤值。generate-resources
: 生成需要打包到项目中的资源文件。process-resources
: 将资源文件复制并处理到目标目录,准备打包。compile
: 编译项目的源代码。process-classes
: 对编译生成的文件进行后处理,例如字节码增强。generate-test-sources
: 生成测试用的源代码。process-test-sources
: 处理测试源代码。generate-test-resources
: 生成测试用的资源文件。process-test-resources
: 将测试资源文件复制并处理到测试目标目录。test-compile
: 编译测试源代码。process-test-classes
: 对测试编译生成的文件进行后处理。test
: 使用合适的单元测试框架(如 JUnit)运行测试。prepare-package
: 在打包前进行准备工作。package
: 将编译后的代码打包成可分发的格式,如 JAR、WAR 等。pre-integration-test
: 在集成测试前进行准备。integration-test
: 运行集成测试。post-integration-test
: 在集成测试后进行清理。verify
: 运行检查以验证包是否有效并符合质量标准。install
: 将包安装到本地仓库,供本地其他项目依赖。deploy
: 将最终的包复制到远程仓库,与其他开发者或项目共享。
B. clean
生命周期
用于清理上一次构建生成的文件。
主要阶段:
pre-clean
: 在实际清理前执行一些操作。clean
: 移除上一次构建生成的所有文件(通常是target
目录)。post-clean
: 在实际清理后执行一些操作。
C. site
生命周期
用于生成和部署项目站点(项目文档、报告等)。
主要阶段:
pre-site
: 在生成站点文档前执行一些操作。site
: 生成项目的站点文档。post-site
: 在生成站点文档后执行一些操作,例如进行打包。site-deploy
: 将生成的站点文档部署到指定的服务器。
一个插件如何绑定到生命周期中?
方式一:默认绑定 (由打包方式 <packaging>
决定)
这是 Maven “约定大于配置”理念的体现。Maven 会根据你在 pom.xml
中设置的打包方式(<packaging>
,如 jar
, war
, pom
),为核心的生命周期阶段绑定一套默认的插件目标。
- 如果你的
pom.xml
中有<packaging>jar</packaging>
(或者不写,因为jar
是默认值)。 - Maven 会自动将
maven-compiler-plugin
的compile
目标绑定到compile
阶段,将maven-surefire-plugin
的test
目标绑定到test
阶段,将maven-jar-plugin
的jar
目标绑定到package
阶段。 - 你完全不需要任何配置,执行
mvn package
时,编译、测试、打包工作就会自动进行。
参考:Built-in Lifecycle Bindings
方式二:插件的“默认阶段绑定” (Default Phase Binding)
有些为特定目的而设计的插件(比如代码生成、资源处理),其作者已经预见到了它最常见的用例。因此,在插件本身的元数据中,开发者已经定义了它的默认绑定阶段。
例如,Modello 插件默认将其目标 modello:java
绑定到 generate-sources
阶段,我们只需要在 pom.xml
中添加如下配置:
1 <plugin>
2 <groupId>org.codehaus.modello</groupId>
3 <artifactId>modello-maven-plugin</artifactId>
4 <version>1.8.1</version>
5 <executions>
6 <execution>
7 <configuration>
8 <models>
9 <model>src/main/mdo/maven.mdo</model>
10 </models>
11 <version>4.0.0</version>
12 </configuration>
13 <goals>
14 <goal>java</goal>
15 </goals>
16 </execution>
17 </executions>
18 </plugin>
您可能想知道为什么要使用 <executions>
元素。这样您就可以在需要时使用不同的配置多次运行同一个目标。还可以给单独的执行赋予一个 ID,这样在继承或应用配置文件时,就可以控制目标配置是被合并还是被转化为额外的执行。
方式三:手动绑定 (Manual Binding)
如果你需要更细粒度的控制,或者想要在特定阶段执行某个插件的目标,你可以手动绑定插件目标到生命周期阶段。
例如,假设有一个目标 display:time,它能在命令行中回声显示当前时间,你希望它在 process-test-resources 阶段运行,以显示测试何时开始。可以这样配置
1 <plugin>
2 <groupId>com.mycompany.example</groupId>
3 <artifactId>display-maven-plugin</artifactId>
4 <version>1.0</version>
5 <executions>
6 <execution>
7 <phase>process-test-resources</phase>
8 <goals>
9 <goal>time</goal>
10 </goals>
11 </execution>
12 </executions>
13 </plugin>
Maven 命令格式
Maven 的命令格式通常是 mvn [phase] [plugin-prefix:goal]
,主要分为两大类:
-
执行生命周期阶段 (Lifecycle Phase)
- 命令格式:
mvn [phase]
- 例子:
mvn compile
,mvn package
,mvn install
- 目的: 对一个已经存在的项目,按照预定义的标准流程进行构建、测试、打包和安装。
- 工作方式: 当你执行一个
phase
时,Maven 会按顺序执行该生命周期中从开始到指定阶段的所有步骤。例如,mvn install
会依次执行validate
,compile
,test
,package
,install
等一系列阶段。这些阶段的核心是处理你项目的源代码和资源。
- 命令格式:
-
直接执行插件目标 (Direct Plugin Goal)
- 命令格式:
mvn [plugin-prefix]:[goal]
- 例子:
mvn archetype:generate
,mvn clean:clean
,mvn dependency:tree
- 目的: 执行一个独立的、特定的任务,这个任务通常不属于标准的项目构建流程,或者是构建流程中的某个具体步骤的独立调用。
- 工作方式: 此命令会绕过生命周期,直接找到指定的插件并执行其提供的特定功能(
goal
)。
- 命令格式:
Maven Cordinates
Maven 坐标(Coordinates)是 Maven 用来唯一标识一个项目或依赖的方式。它由以下四个部分组成:
- groupId: 组织或公司标识符,通常是反向域名格式(如
org.apache.maven.plugins
) - artifactId: 项目的唯一标识符,通常是项目的名称(如
maven-archetype-plugin
) - packaging: 项目的打包方式;通常是
jar
、war
、pom
等 - version: 项目的版本号(如
3.2.0
);处于积极开发中的项目可以使用一个特殊的标识符,将版本标记为 SNAPSHOT。
其排列的方式为 groupId:artifactId[:packaging[:version]]
例如 JUnit:junit:jar:4.13.2
代表了 JUnit 4.13.2 版本的 JAR 包。
Maven Repositories
Maven 仓库是用来存储所有项目构件(Artifacts,如 JAR 文件)和插件(Plugins)的地方。Maven 本身安装包之所以很小(约 1.5MB),就是因为它只包含最核心的功能。所有具体的工作(如编译、打包)都由插件完成,而这些插件都是在需要时才从远程仓库下载的。
Maven 仓库的种类
- 远程仓库 (Remote Repository)
- 中央仓库 (Central Repository):Maven 自带一个默认的远程仓库地址 (
https://repo1.maven.org/maven2/
),绝大多数开源的库都可以从这里下载。 - 私有/自定义仓库 (Custom Repository):公司或组织可以架设自己的私有仓库,用于存放内部项目或无法公开的商业库。这些私有仓库可以作为中央仓库的补充或镜像。
- 中央仓库 (Central Repository):Maven 自带一个默认的远程仓库地址 (
- 本地仓库 (Local Repository)
- 这是一个位于你自己的电脑上的文件夹(通常在 ~/.m2/repository)
- 作为本地项目间的共享机制和缓存机制
Maven 仓库的目录结构
Maven 仓库不是一个随意的文件夹,它有着非常严格的、标准化的目录结构
按照 /<groupId 的路径>/<artifactId>/<version>/<artifactId>-<version>.<packaging>
的格式来组织文件。
例子:坐标为 org.apache.commons:commons-email:1.1
的 JAR 文件,其存储路径就是 /org/apache/commons/commons-email/1.1/commons-email-1.1.jar
,结合中央仓库地址得到:
https://repo1.maven.org/maven2/org/apache/commons/commons-email/1.1/commons-email-1.1.jar