1.2 Maven 核心概念 1.2 Maven 核心概念 1.2.1 项目对象模型 (Project Object Model - POM) POM 是 Maven 的灵魂,它是一个 XML 文件 ( ),位于项目的根目录下。POM 描述了项目的基本信息、构建配置、依赖关系、插件以及其他各种配置。Maven 正是通过读取和解析 POM 文件来了解项目的结构和构建需求,从而执行相应的构建任务。 1.2.1.1 POM 的基本结构 一个基本的 文件通常包含以下核心元素: : POM 文件的根元素,所有其他元素都嵌套在其中。 : 指定 POM 模型的版本。对于 Maven 3 及以上版本,通常设置为 。 : 项目的组织或组的唯一标识符。通常采用反向域名格式,例如 。
POM 是 Maven 的灵魂,它是一个 XML 文件 (pom.xml),位于项目的根目录下。POM 描述了项目的基本信息、构建配置、依赖关系、插件以及其他各种配置。Maven 正是通过读取和解析 POM 文件来了解项目的结构和构建需求,从而执行相应的构建任务。
1.2.1.1 POM 的基本结构
一个基本的 pom.xml 文件通常包含以下核心元素:
<project>: POM 文件的根元素,所有其他元素都嵌套在其中。
<modelVersion>: 指定 POM 模型的版本。对于 Maven 3 及以上版本,通常设置为 4.0.0。
<groupId>: 项目的组织或组的唯一标识符。通常采用反向域名格式,例如 com.example.myapp。
<artifactId>: 项目的模块或工件的唯一标识符。通常是项目名称,例如 my-webapp。
<version>: 项目的版本号。例如 1.0-SNAPSHOT, 1.0.0, 1.0-RELEASE。
<packaging>: 项目构建输出的包类型。常见的类型有 jar, war, pom, ear 等。默认为 jar。
<name>: 项目的显示名称,用于文档和报告。
<dependencies>: 声明项目依赖的其他库或模块。
<build>: 配置项目的构建过程,包括插件配置、资源管理等。
<repositories>: 定义 Maven 仓库的位置,用于下载依赖和插件。
<properties>: 定义可以在 POM 文件中使用的属性,方便配置管理。
<modules>: 用于多模块项目,声明子模块。
<parent>: 声明父 POM,实现配置的继承和复用。
1.2.1.2 代码实践:创建一个简单的 POM 文件
下面是一个简单的 pom.xml 文件的示例,用于创建一个 Java 项目:
<?xml version="1.0" encoding="UTF-8"?> <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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>my-simple-app</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <name>My Simple Application</name> <description>A simple Java application built with Maven.</description> <properties> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <!-- 依赖项列表 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <!-- 构建插件列表 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> <configuration> <source>${maven.compiler.source}</source> <target>${maven.compiler.target}</target> <encoding>${project.build.sourceEncoding}</encoding> </configuration> </plugin> </plugins> </build> </project>
代码详解:
<modelVersion>4.0.0</modelVersion>: 指定 POM 模型版本为 4.0.0。
<groupId>com.example</groupId>, <artifactId>my-simple-app</artifactId>, <version>1.0-SNAPSHOT</version>: 定义了项目的 GAV 坐标 (GroupId, ArtifactId, Version),这是 Maven 唯一标识一个工件的方式。
<packaging>jar</packaging>: 指定项目打包类型为 JAR 文件。
<name>My Simple Application</name>, <description>...</description>: 项目的名称和描述信息。
<properties>: 定义了一些属性,例如 Java 编译版本和字符编码,可以在 POM 文件其他地方引用,例如 ${maven.compiler.source}。
<dependencies>: 声明项目依赖了 JUnit 4.12 版本,scope 设置为 test,表示该依赖只在测试阶段使用。
<build> -> <plugins>: 配置了 maven-compiler-plugin 插件,用于编译 Java 代码,并指定了源代码和目标代码的 Java 版本,以及字符编码。
1.2.1.3 POM 的继承与聚合
继承 (Inheritance): 通过 <parent> 元素,子 POM 可以继承父 POM 的配置,例如依赖管理、插件配置、属性定义等。这有助于在多个模块之间共享配置,减少重复,提高维护性。父 POM 的 packaging 通常设置为 pom。
聚合 (Aggregation): 通过 <modules> 元素,一个 POM 可以聚合多个子模块。聚合 POM 负责统一构建和管理多个子模块。聚合 POM 的 packaging 也通常设置为 pom。
Mermaid 图示:POM 继承与聚合
1.2.2 Maven 生命周期 (Build Lifecycle)
Maven 构建生命周期定义了一系列有序的构建阶段 (Build Phase),每个阶段负责处理构建过程的不同方面。Maven 保证执行某个阶段之前,会先执行其之前的全部阶段。这使得构建过程标准化、可预测。
Maven 定义了三个内置的生命周期:
clean: 清理项目,删除之前构建生成的文件。
default (或 build): 构建项目的核心生命周期,包括编译、测试、打包、安装和部署等。
site: 生成项目站点文档。
1.2.2.1 default 生命周期阶段
default 生命周期是最常用的生命周期,它包含了以下主要阶段 (按顺序排列):
validate: 验证项目是否正确,所有必要的资源是否可用。
initialize: 初始化构建状态。
generate-sources: 生成源代码。
process-sources: 处理源代码,例如过滤。
generate-resources: 生成资源文件。
process-resources: 复制并处理资源文件到输出目录。
compile: 编译源代码。
process-classes: 后处理编译生成的 class 文件。
generate-test-sources: 生成测试源代码。
process-test-sources: 处理测试源代码。
generate-test-resources: 生成测试资源文件。
process-test-resources: 处理测试资源文件。
test-compile: 编译测试源代码。
process-test-classes: 后处理测试编译生成的 class 文件。
test: 运行单元测试。
package: 将编译后的代码打包成可发布的格式,如 JAR, WAR, EAR。
verify: 运行检查,验证 package 是否有效且达到质量标准。
install: 将 package 安装到本地 Maven 仓库,供其他项目使用。
deploy: 将最终的 package 部署到远程仓库,供共享和发布。
1.2.2.2 代码实践:执行 Maven 生命周期阶段
在命令行中,可以使用 mvn [phase] 命令来执行特定的生命周期阶段。例如:
mvn clean: 执行 clean 生命周期,清理项目。
mvn compile: 执行 default 生命周期的 compile 阶段,编译源代码。
mvn test: 执行 default 生命周期的 test 阶段,运行单元测试。
mvn package: 执行 default 生命周期的 package 阶段,打包项目。
mvn install: 执行 default 生命周期的 install 阶段,安装项目到本地仓库。
mvn deploy: 执行 default 生命周期的 deploy 阶段,部署项目到远程仓库。
1.2.2.3 生命周期与插件目标 (Plugin Goals)
每个生命周期阶段都关联着一个或多个插件目标 (Plugin Goal)。插件目标是插件提供的具体执行单元,负责完成特定任务。例如,compile 阶段通常关联 maven-compiler-plugin 的 compile 目标,负责编译 Java 源代码。
Mermaid 图示:Maven 生命周期与插件目标
1.2.3 Maven 插件 (Plugins) 和目标 (Goals)
Maven 的核心功能是通过插件来实现的。插件是 Maven 生态系统中可重用的组件,用于执行各种构建任务,例如编译代码、运行测试、打包、部署、生成文档等等。
插件 (Plugins): 是一组相关的目标 (Goals) 的集合,通常用于完成特定类型的构建任务。例如,maven-compiler-plugin 用于编译源代码,maven-surefire-plugin 用于运行单元测试。
目标 (Goals): 是插件提供的可执行单元,代表一个具体的构建任务。例如,maven-compiler-plugin 插件提供了 compile 和 testCompile 两个目标。
1.2.3.1 插件的配置与使用
插件可以在 POM 文件的 <build> -> <plugins> 节点中配置。每个插件配置通常包括以下信息:
<groupId>: 插件的 GroupId。
<artifactId>: 插件的 ArtifactId。
<version>: 插件的版本。
<executions>: 配置插件的执行,可以将插件目标绑定到特定的生命周期阶段,并配置执行参数。
<configuration>: 配置插件的参数。
1.2.3.2 代码实践:配置 Maven 插件
在 1.2.1.2 的 pom.xml 示例中,我们已经配置了 maven-compiler-plugin 插件:
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> <configuration> <source>${maven.compiler.source}</source> <target>${maven.compiler.target}</target> <encoding>${project.build.sourceEncoding}</encoding> </configuration> </plugin> </plugins> </build>
代码详解:
<groupId>org.apache.maven.plugins</groupId>, <artifactId>maven-compiler-plugin</artifactId>, <version>3.8.1</version>: 指定了要使用的插件的 GAV 坐标。
<configuration>: 配置了 maven-compiler-plugin 插件的参数,包括源代码版本 (source),目标代码版本 (target),以及字符编码 (encoding)。这些参数的值使用了在 <properties> 中定义的属性。
1.2.3.3 插件目标的绑定
Maven 默认会将一些常用的插件目标绑定到默认生命周期的各个阶段。例如,maven-compiler-plugin:compile 默认绑定到 compile 阶段,maven-surefire-plugin:test 默认绑定到 test 阶段。
可以通过在 <plugin> -> <executions> 中配置 <phase> 元素,显式地将插件目标绑定到特定的生命周期阶段。也可以配置 <goals> 元素,指定要执行的插件目标。
代码实践:显式绑定插件目标
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-assembly-plugin</artifactId> <version>3.3.0</version> <executions> <execution> <id>create-distribution</id> <phase>package</phase> <!- 绑定到 package 阶段 --> <goals> <goal>single</goal> <!- 执行 single 目标 --> </goals> <configuration> <!-- 插件配置 --> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> <archive> <manifest> <mainClass>com.example.Main</mainClass> </manifest> </archive> </configuration> </execution> </executions> </plugin> </plugins> </build>
代码详解:
<plugin>maven-assembly-plugin</plugin>: 配置了 maven-assembly-plugin 插件,用于创建项目分发包。
<executions>: 定义了一个执行配置。
<id>create-distribution</id>: 为该执行配置指定一个 ID,方便引用。
<phase>package</phase>: 将 maven-assembly-plugin:single 目标绑定到 package 生命周期阶段。这意味着在执行 package 阶段时,会自动执行 maven-assembly-plugin:single 目标。
<goals>: 指定要执行的插件目标,这里是 single 目标,用于创建单个分发包。
<configuration>: 配置了 maven-assembly-plugin 插件的参数,例如 descriptorRefs 指定了要使用的描述符,archive 配置了归档信息。
1.2.4 Maven 依赖管理 (Dependency Management)
Maven 的依赖管理是其核心功能之一,它允许项目声明对外部库、模块或其他项目的依赖,并负责自动下载、解析和管理这些依赖。
1.2.4.1 依赖声明
依赖在 POM 文件的 <dependencies> 节点中声明。每个依赖使用 <dependency> 元素定义,通常包含以下信息:
<groupId>: 依赖的 GroupId。
<artifactId>: 依赖的 ArtifactId。
<version>: 依赖的版本。
<scope>: 依赖的作用范围,例如 compile, test, runtime, provided, system 等。
<optional>: 是否可选依赖,默认为 false。
<exclusions>: 排除传递性依赖。
1.2.4.2 代码实践:声明项目依赖
在 1.2.1.2 的 pom.xml 示例中,我们已经声明了 JUnit 依赖:
<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> </dependencies>
代码详解:
<dependency>: 声明一个依赖。
<groupId>junit</groupId>, <artifactId>junit</artifactId>, <version>4.12</version>: 指定了 JUnit 依赖的 GAV 坐标。
<scope>test</scope>: 指定依赖的作用范围为 test,表示该依赖只在测试阶段有效。
1.2.4.3 依赖范围 (Scope)
依赖范围 (Scope) 用于控制依赖在哪些 classpath 中可用,以及是否会包含在最终的打包文件中。常见的依赖范围包括:
compile (默认): 编译依赖,在编译、测试、运行和打包时都有效。
test: 测试依赖,只在测试编译和测试运行时有效,不会包含在最终的打包文件中。
runtime: 运行时依赖,在测试和运行时有效,编译时不需要,打包时会包含。
provided: 已提供依赖,期望运行时环境 (例如应用服务器) 提供,编译和测试时需要,打包时不会包含。
system: 系统依赖,与 provided 类似,但需要显式指定依赖的系统路径,不推荐使用。
import: 导入依赖,只在 <dependencyManagement> 中有效,用于导入其他 POM 的依赖配置。
1.2.4.4 传递性依赖 (Transitive Dependencies)
Maven 的依赖管理支持传递性依赖。当项目 A 依赖于项目 B,而项目 B 又依赖于项目 C 时,项目 A 会自动传递性地依赖于项目 C。Maven 会自动解析和下载所有传递性依赖。
1.2.4.5 依赖冲突解决 (Dependency Conflict Resolution)
当项目中存在多个版本的同一个依赖时,可能会发生依赖冲突。Maven 使用 "最近原则" 和 "第一声明原则" 来解决依赖冲突:
最近原则 (Nearest Wins): 当依赖路径长度不同时,选择路径最短的依赖版本。
第一声明原则 (First Declaration Wins): 当依赖路径长度相同时,选择在 POM 文件中先声明的依赖版本。
1.2.4.6 依赖管理 <dependencyManagement>
<dependencyManagement> 节点用于统一管理项目依赖的版本。在 <dependencyManagement> 中声明的依赖不会实际引入依赖,而是用于约束子模块或当前模块中依赖的版本。子模块或当前模块在声明依赖时,可以省略版本号,Maven 会自动从 <dependencyManagement> 中查找并使用指定的版本。
代码实践:使用 <dependencyManagement> 管理依赖版本
父 POM 的 pom.xml:
<dependencyManagement> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <!- 统一管理 JUnit 版本 --> <scope>test</scope> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.9</version> <!- 统一管理 Commons Lang3 版本 --> </dependency> </dependencies> </dependencyManagement>
子模块或当前模块的 pom.xml:
<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <!- 省略版本号,使用父 POM 中 <dependencyManagement> 定义的版本 --> <scope>test</scope> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <!- 省略版本号,使用父 POM 中 <dependencyManagement> 定义的版本 --> </dependency> </dependencies>
1.2.5 Maven 仓库 (Repositories)
Maven 仓库是用于存储和管理 Maven 工件 (Artifacts) 的场所。Maven 仓库分为三种类型:
本地仓库 (Local Repository): 位于开发人员本地机器上的仓库,用于缓存下载的远程仓库工件,以及存储本地构建的工件。默认本地仓库路径为 ~/.m2/repository。
中央仓库 (Central Repository): 由 Maven 社区维护的公共远程仓库,包含了大量的开源 Java 库和框架。Maven 默认配置的中央仓库地址为 https://repo.maven.apache.org/maven2/。
远程仓库 (Remote Repository): 除了中央仓库之外的其他远程仓库,例如公司内部的私有仓库、第三方公共仓库等。远程仓库需要在 POM 文件或 settings.xml 文件中配置。
1.2.5.1 仓库的配置
本地仓库: 本地仓库路径可以通过 Maven 的 settings.xml 文件配置。
远程仓库: 远程仓库可以在 POM 文件的 <repositories> 节点中配置,也可以在 settings.xml 文件的 <profiles> -> <profile> -> <repositories> 中配置。
1.2.5.2 代码实践:配置远程仓库
在 POM 文件的 <repositories> 节点中配置远程仓库:
<repositories> <repository> <id>my-company-repo</id> <url>http://repository.mycompany.com/maven2</url> <releases> <enabled>true</enabled> </releases> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories>
代码详解:
<repository>: 定义一个远程仓库。
<id>my-company-repo</id>: 仓库的唯一标识符。
<url>http://repository.mycompany.com/maven2</url>: 仓库的 URL 地址。
<releases>: 配置发布版本 (Release) 的下载策略。
<enabled>true</enabled>: 启用发布版本下载。<snapshots>: 配置快照版本 (Snapshot) 的下载策略。
<enabled>false</enabled>: 禁用快照版本下载。1.2.5.3 仓库的搜索顺序
Maven 搜索工件的顺序为:
本地仓库
远程仓库 (按配置顺序)
中央仓库 (如果配置了)
1.2.6 Maven 工件 (Artifacts) 和坐标 (Coordinates)
Maven 工件 (Artifacts) 是 Maven 构建的最终产物,例如 JAR 文件、WAR 文件、POM 文件等。每个工件都由其唯一的坐标 (Coordinates) 标识,也称为 GAV 坐标:
GroupId: 组织或组的唯一标识符。
ArtifactId: 模块或工件的唯一标识符。
Version: 版本号。
除了 GAV 坐标外,工件还可能包含以下信息:
Packaging: 打包类型,例如 jar, war, pom。
Classifier: 分类器,用于区分同一 GAV 下的不同变体工件,例如 sources, javadoc。
1.2.6.1 工件的 GAV 坐标
GAV 坐标是 Maven 唯一标识一个工件的关键信息。通过 GAV 坐标,Maven 可以在仓库中准确地定位和下载所需的工件。
1.2.6.2 代码实践:理解工件坐标
例如,JUnit 4.12 版本的工件坐标为:
GroupId: junit
ArtifactId: junit
Version: 4.12
在 POM 文件中声明依赖时,就需要使用这些坐标信息。
1.2.7 Maven Settings 文件 (settings.xml)
settings.xml 文件是 Maven 的全局配置文件,用于配置 Maven 的运行时行为,例如本地仓库路径、远程仓库配置、代理设置、服务器认证信息等。settings.xml 文件通常位于两个位置:
Maven 安装目录下的 conf 目录: 全局 settings.xml,对所有用户和项目生效。
用户目录下的 ~/.m2 目录: 用户 settings.xml,只对当前用户生效,优先级高于全局 settings.xml。
1.2.7.1 settings.xml 的主要配置
<localRepository>: 配置本地仓库路径。
<mirrors>: 配置仓库镜像,用于加速仓库下载。
<proxies>: 配置代理服务器。
<servers>: 配置服务器认证信息,例如部署仓库的用户名和密码。
<profiles>: 配置 Maven Profile,可以根据不同的环境激活不同的配置。
1.2.7.2 代码实践:配置 settings.xml 文件
例如,在 ~/.m2/settings.xml 文件中配置本地仓库路径和 Maven 中央仓库的镜像:
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd"> <localRepository>/path/to/my/local/repository</localRepository> <!- 配置本地仓库路径 --> <mirrors> <mirror> <id>aliyun-maven-mirror</id> <mirrorOf>central</mirrorOf> <!- 镜像 Maven 中央仓库 --> <name>阿里云 Maven 镜像</name> <url>https://maven.aliyun.com/repository/central</url> <!- 阿里云 Maven 镜像地址 --> </mirror> </mirrors> </settings>