Maven 是一个项目管理工具它包含了一个项目对象模型 (POMProject Object Model)
一组标准集合。由于 Maven 使用标准目录布局和默认构建生命周期开发团队几乎可以立即自动化项目的构建基础设施。在多个开发团队环境的情况下Maven 可以在很短的时间内按照标准设置工作方式。
Maven 之前更多的是使用 Ant 的项目构建工具Ant 有一个特点每次都得写每次都写的差不多配置也臃肿。所以后来搞出来 Maven。Maven 就是最先进的版本构建工具吗不是的只不过目前在 Java 领域 Maven 使用比较多。除了 Maven还有 Gradle。
它的主要功能有
为了实现上面的主要功能Maven提供了两大核心
下面来谈一谈Maven模型的整体结构包括三个部分
如上图所示包括蓝、黄两个部分分别对应着依赖关系和项目构建两大核心功能。
首当其冲的一个核心就是项目对象模型也就是经常使用的pom.xml
另外一个就是项目构建Maven的项目构建可以按照生命周期具备以下三个标准生命周期
POM 代表项目对象模型。它是 Maven 中的基本工作单元。它是一个 XML 文件作为 pom.xml 驻留在项目的跟目录中。POM 不仅包含有关项目的信息以及 Maven 用于构建项目的各种配置详细信息 还包含目标和插件。
在执行任务或目标时Maven 在当前目录中查找 POM。它读取 POM获取所需的配置信息然后执行目标。在POM文件中常见的配置包括一下几点
在创建 POM 之前我们应该首先确定项目组(groupId)、项目名称(artifactId) 和版本因为这些属性有助于在存储库中唯一标识项目。
<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.coderV</groupId> <artifactId>coderV</artifactId> <packaging>jar</packaging> <version>1.0-SNAPSHOT</version> <name>com.coderV</name> <url>http://maven.apache.org</url> <dependencies> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <version>5.8.2</version> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> //... </plugin> </plugins> </build> </project>
接下来仔细拆解POM文件结构
Maven 使用一组标识符也称为坐标来唯一标识项目并指定应如何打包项目工件
其中所有 POM 文件都需要项目元素和三个必填字段groupId、artifactId、version。
其中的前三个 ( groupId:artifactId:version ) 结合形成唯一标识符并且是您指定项目将使用的外部库例如 JAR版本的机制。
详细说明一些基本的项目标识符6
参数名称 | 描述 |
---|---|
Project root | 这是项目root的标签。您需要指定基本架构设置例如 apache 架构和 w3.org 规范。 |
Model version | 模型版本应为 4.0.0。 |
**groupId ** | 这是项目组/公司的 ID。这在组织或项目中通常是唯一的。 |
artifactId | 这是项目的 ID。这通常是项目的名称 |
version | 这是项目的版本 |
Maven 使用存储库的概念进行依赖管理项目中使用的这些外部库称为依赖项。Maven 中的依赖项管理功能可确保从中央存储库自动下载这些库因此您不必将它们存储在本地。
这是 Maven 的一个关键特性并提供以下好处
为了声明对外部库的依赖您需要提供库的groupId、artifactId、verison。让我们看一个例子
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>5.3.16</version> </dependency>
当 Maven 处理依赖项时它会将 Spring Core 库下载到本地 Maven 存储库中并在项目中使用
如下图所示项目 A 依赖于项目 BB 又依赖于项目 C此时 B 是 A 的直接依赖C 是 A 的间接依赖
Maven 的依赖传递机制是指不管 Maven 项目存在多少间接依赖POM 中都只需要定义其直接依赖不必定义任何间接依赖Maven 会动读取当前项目各个直接依赖的 POM将那些必要的间接依赖以传递性依赖的形式引入到当前项目中。Maven 的依赖传递机制能够帮助用户一定程度上简化 POM 的配置。
基于 A、B、C 三者的依赖关系根据 Maven 的依赖传递机制我们只需要在项目 A 的 POM 中定义其直接依赖 B在项目 B 的 POM 中定义其直接依赖 CMaven 会解析 A 的直接依赖 B的 POM 将间接依赖 C 以传递性依赖的形式引入到项目 A 中。
通过这种依赖传递关系可以使依赖关系树迅速增长到一个很大的量级很有可能会出现依赖重复依赖冲突等情况Maven 针对这些情况提供了如下功能进行处理。
Maven 具有以下 6 中常见的依赖范围如下表所示。
依赖范围 | 描述 |
---|---|
compile | 编译依赖范围scope 元素的缺省值。使用此依赖范围的 Maven 依赖对于三种 classpath 均有效即该 Maven 依赖在上述三种 classpath 均会被引入。例如log4j 在编译、测试、运行过程都是必须的。 |
test | 测试依赖范围。使用此依赖范围的 Maven 依赖只对测试 classpath 有效。例如Junit 依赖只有在测试阶段才需要。 |
provided | 已提供依赖范围。使用此依赖范围的 Maven 依赖只对编译 classpath 和测试 classpath 有效。例如servlet-api 依赖对于编译、测试阶段而言是需要的但是运行阶段由于外部容器已经提供故不需要 Maven 重复引入该依赖。 |
runtime | 运行时依赖范围。使用此依赖范围的 Maven 依赖只对测试 classpath、运行 classpath 有效。例如JDBC 驱动实现依赖其在编译时只需 JDK 提供的 JDBC 接口即可只有测试、运行阶段才需要实现了 JDBC 接口的驱动。 |
system | 系统依赖范围其效果与 provided 的依赖范围一致。其用于添加非 Maven 仓库的本地依赖通过依赖元素 dependency 中的 systemPath 元素指定本地依赖的路径。鉴于使用其会导致项目的可移植性降低一般不推荐使用。 |
import | 导入依赖范围该依赖范围只能与 dependencyManagement 元素配合使用其功能是将目标 pom.xml 文件中 dependencyManagement 的配置导入合并到当前 pom.xml 的 dependencyManagement 中。 |
依赖范围与三种 classpath 的关系一览表如下所示。
依赖范围 | 编译 classpath | 测试 classpath | 运行 classpath | 例子 |
---|---|---|---|---|
compile | √ | √ | √ | log4j |
test | - | √ | - | junit |
provided | √ | √ | - | servlet-api |
runtime | - | - | √ | JDBC-driver |
system | √ | √ | - | 非 Maven 仓库的本地依赖 |
项目 A 依赖于项目 BB 又依赖于项目 C此时我们可以将 A 对于 B 的依赖称之为第一直接依赖B 对于 C 的依赖称之为第二直接依赖。
B 是 A 的直接依赖C 是 A 的间接依赖根据 Maven 的依赖传递机制间接依赖 C 会以传递性依赖的形式引入到 A 中但这种引入并不是无条件的它会受到依赖范围的影响。
传递性依赖的依赖范围受第一直接依赖和第二直接依赖的范围影响如下表所示。
compile | test | provided | runtime | |
---|---|---|---|---|
compile | compile | - | - | runtime |
test | test | - | - | test |
provided | provided | - | provided | provided |
runtime | runtime | - | - | runtime |
注上表中左边第一列表示第一直接依赖的依赖范围上边第一行表示第二直接依赖的依赖范围。交叉部分的单元格的取值为传递性依赖的依赖范围若交叉单元格取值为“-”则表示该传递性依赖不能被传递。
通过上表可以总结出以下规律(*)
Maven 的依赖传递机制可以简化依赖的声明用户只需要关心项目的直接依赖而不必关心这些直接依赖会引入哪些间接依赖了。为了避免出现依赖重复的问题Maven 通过依赖调节来确定间接依赖的引入路径。
依赖调节遵循以下两条原则
引入路径短者优先顾名思义当一个间接依赖存在多条引入路径时引入路径短的会被解析使用。
例如A 存在这样的依赖关系
A->B->C->D(1.0)
A->X->D(2.0)
D 是 A 的间接依赖但两条引入路径上有两个不同的版本很显然不能同时引入否则造成重复依赖的问题。根据 Maven 依赖调节的第一个原则引入路径短者优先D1.0的路径长度为 3D2.0的路径长度为 2因此间接依赖 D2.0将从 A->X->D(2.0) 路径引入到 A 中。
先声明者优先顾名思义在引入路径长度相同的前提下POM 文件中依赖声明的顺序决定了间接依赖会不会被解析使用顺序靠前的优先使用。
例如A 存在以下依赖关系
A->B->D(1.0)
A->X->D(2.0)
D 是 A 的间接依赖其两条引入路径的长度都是 2此时 Maven 依赖调节的第一原则已经无法解决需要使用第二原则先声明者优先。
我们知道 Maven 依赖具有传递性例如 A 依赖于 BB 依赖于 C在不考虑依赖范围等因素的情况下Maven 会根据依赖传递机制将间接依赖 C 引入到 A 中。但如果 A 出于某种原因希望将间接依赖 C 排除那该怎么办呢Maven 为用户提供了两种解决方式排除依赖Dependency Exclusions和可选依赖Optional Dependencies。
假设存在这样的依赖关系A 依赖于 BB 依赖于 XB 又依赖于 Y。B 实现了两个特性其中一个特性依赖于 X另一个特性依赖于 Y且两个特性是互斥的关系用户无法同时使用两个特性所以 A 需要排除 X此时就可以在 A 中将间接依赖 X 排除。
排除依赖是通过在 A 中使用 exclusions 元素实现的该元素下可以包含若干个 exclusion 子元素用于排除若干个间接依赖示例代码如下
<dependencies> <dependency> <groupId>net.biancheng.www</groupId> <artifactId>B</artifactId> <version>1.0-SNAPSHOT</version> <exclusions> <!-- 设置排除 --> <!-- 排除依赖必须基于直接依赖中的间接依赖设置为可以依赖为 false --> <!-- 设置当前依赖中是否使用间接依赖 --> <exclusion> <!--设置具体排除--> <groupId>net.biancheng.www</groupId> <artifactId>X</artifactId> </exclusion> </exclusions> </dependency> </dependencies>
关于 exclusions 元素及排除依赖说明如下
与上文的应用场景相同也是 A 希望排除间接依赖 X除了在 B 中设置可选依赖外我们还可以在 B 中将 X 设置为可选依赖。
在 B 的 POM 关于 X 的依赖声明中使用 optional 元素将其设置成可选依赖示例配置如下:
<dependencies> <dependency> <groupId>net.biancheng.www</groupId> <artifactId>X</artifactId> <version>1.0-SNAPSHOT</version> <!--设置可选依赖 --> <optional>true</optional> </dependency> </dependencies>
关于 optional 元素及可选依赖说明如下
排除依赖和可选依赖都能在项目中将间接依赖排除在外但两者实现机制却完全不一样。
在 Maven 术语中仓库是存储所有项目的 jar包的地方Maven 可以轻松使用它们。根据类别可以将Maven仓库分为三类
1本地仓库
默认情况下每个本地计算机的用户目录下都有一个路径名为.m2/repository/的仓库目录这个就是本地的仓库
也可以在 settings.xml 文件配置本地仓库的路径
2远程仓库
远程仓库也称为私服由公司或者项目组维护是开发人员自己定义的远程仓库其中包含项目所需要的库或其他的jar包可以通过标签来制定远程仓库地址
<repositories> <repository> <id>companyname.lib1</id> <url>http://download.companyname.org/maven2/lib1</url> </repository> </repositories>
3中央仓库
Maven中央仓库是Maven社区提供的仓库。它包含大量常用的库。当 Maven 在本地存储库中找不到任何依赖项时就会从中央仓库中搜索
其中中央仓库有以下几点需要注意
当我们执行 Maven 构建命令时Maven 开始按以下顺序查找依赖库
自定义属性有助于pom.xml
文件更易于阅读和维护。一个十分经典的使用场景就是通过自定义属性来定义项目依赖项的版本。
Maven的properties
是一个占位符通过properties
定义不同属性名的值
例如下面的例子中通过<properties>
定义了一个spring.version
的属性其具体的值为 5.3.16
如果想要将 Spring 升级到更新的版本就只需更改<spring.version>
属性标签内的值所有在其<version>
标签中使用该属性的依赖项都将更新。
<properties> <spring.version>5.3.16</spring.version> </properties> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> </dependencies>
另一个常用的场景就是使用<properties>
定义构建路径的变量例如
<properties> <project.build.folder>${project.build.directory}/tmp/</project.build.folder> </properties> <plugin> //... <outputDirectory>${project.resources.build.folder}</outputDirectory> //... </plugin>
build
部分也是 Maven POM 中非常重要的部分。它提供有关默认 Maven目标、已编译项目的目录和应用程序的最终名称的信息。默认build
如下所示
<build> <!--当项目没有规定目标Maven2叫做阶段phase时的默认值 --> <!--必须跟命令行上的参数相同例如jar:jar或者与某个阶段phase相同例如install、compile等 --> <defaultGoal>install</defaultGoal> <!-- 构建产生的所有文件存放的目录,默认为${basedir}/target即项目根目录下的target --> <directory>${basedir}/target</directory> <!-- 产生的构件的文件名默认值是${artifactId}-${version}--> <finalName>${artifactId}-${version}</finalName> <!--当filtering开关打开时使用到的过滤器属性文件列表。 --> <!--项目配置信息中诸如${spring.version}之类的占位符会被属性文件中的实际值替换掉 --> <filters> <filter>filters/filter1.properties</filter> </filters> //... </build>
编译工件的默认输出文件夹名为*target*打包工件的最终名称由*artifactId*和*version*组成但您可以随时更改。
构建配置文件是一组配置值可用于设置或覆盖 Maven 构建的默认值。使用构建配置文件您可以为不同的环境例如生产环境和开发环境自定义构建。
配置文件主要分为两种类型一种是在定义在项目上pom.xml文件中另一种是定义在setting.xml上
<profiles> <profile> <id>production</id> <build> <plugins> <plugin> //... </plugin> </plugins> </build> </profile> <profile> <id>development</id> <activation> <activeByDefault>true</activeByDefault> </activation> <build> <plugins> <plugin> //... </plugin> </plugins> </build> </profile> </profiles>
定义在pom.xml文件上的profile可以看做pom.xml的副本拥有与pom.xml相同的子元素与配置方法。
正如您在上面的示例中看到的默认配置文件设置为development。如果要运行生产配置文件可以使用以下 Maven 的 -P 命令显示的激活一个profile
mvn clean install -P production
每个 Maven 构建都遵循指定的生命周期。您可以执行多个构建生命周期目标包括编译项目代码、创建包以及在本地 Maven 依赖项存储库中安装存档文件的目标。
以下列表显示了最重要的 Maven生命周期阶段
当我们执行 mvn post-clean 命令时Maven 调用 clean 生命周期它包含以下阶段
在一个生命周期中运行某个阶段的时候它之前的所有阶段都会被运行也就是说如果执行 mvn clean 将运行pre-clean, clean两个生命周期阶段运行 mvn post-clean 则运行pre-clean, clean, post-clean三个生命周期阶段
这是 Maven 的主要生命周期被用于构建应用包括下面的 23 个阶段
生命周期阶段 | 描述 |
---|---|
validate校验 | 校验项目是否正确并且所有必要的信息可以完成项目的构建过程。 |
initialize初始化 | 初始化构建状态比如设置属性值。 |
generate-sources生成源代码 | 生成包含在编译阶段中的任何源代码。 |
process-sources处理源代码 | 处理源代码比如说过滤任意值。 |
generate-resources生成资源文件 | 生成将会包含在项目包中的资源文件。 |
process-resources 处理资源文件 | 复制和处理资源到目标目录为打包阶段最好准备。 |
compile编译 | 编译项目的源代码。 |
process-classes处理类文件 | 处理编译生成的文件比如说对Java class文件做字节码改善优化。 |
generate-test-sources生成测试源代码 | 生成包含在编译阶段中的任何测试源代码。 |
process-test-sources处理测试源代码 | 处理测试源代码比如说过滤任意值。 |
generate-test-resources生成测试资源文件 | 为测试创建资源文件。 |
process-test-resources处理测试资源文件 | 复制和处理测试资源到目标目录。 |
test-compile编译测试源码 | 编译测试源代码到测试目标目录. |
process-test-classes处理测试类文件 | 处理测试源码编译生成的文件。 |
test测试 | 使用合适的单元测试框架运行测试Juint是其中之一。 |
prepare-package准备打包 | 在实际打包之前执行任何的必要的操作为打包做准备。 |
package打包 | 将编译后的代码打包成可分发格式的文件比如JAR、WAR或者EAR文件。 |
pre-integration-test集成测试前 | 在执行集成测试前进行必要的动作。比如说搭建需要的环境。 |
integration-test集成测试 | 处理和部署项目到可以运行集成测试环境中。 |
post-integration-test集成测试后 | 在执行集成测试完成后进行必要的动作。比如说清理集成测试环境。 |
verify 验证 | 运行任意的检查来验证项目包有效且达到质量标准。 |
install安装 | 安装项目包到本地仓库这样项目包可以用作其他本地项目的依赖。 |
deploy部署 | 将最终的项目包复制到远程仓库中与其他开发者和项目共享。 |
Maven Site 插件一般用来创建新的报告文档、部署站点等。
插件管理与依赖管理原理一样、不同的是定义的元素标签不一样、插件管理标签是build标签的子标签pluginManagement
中
pluginManagement
用来做插件管理的。它是表示插件声明即你在项目中的pluginManagement
下声明了插件Maven不会加载该插件pluginManagement
声明可以被继承。如下面的例子
<pluginManagement> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-source-plugin</artifactId> <version>2.1</version> <configuration> <attach>true</attach> </configuration> <executions> <execution> <phase>compile</phase> <goals> <goal>jar</goal> </goals> </execution> </executions> </plugin> </plugins> </pluginManagement> <!-- 子POM--> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-source-plugin</artifactId> </plugin> </plugins>
原创文章总结不易禁止侵权转载。转载请先私信知乎「编程大K」征求同意