我们在平时的开发中都会或多或少的使用maven来管理和构建我们的项目,即使使用了各种框架jar包也是通过Maven来引入的,所以我觉得有必要了解pom.xml文件中的每一项配置,来帮助我更好的使用Maven这个自动化工具;
1、Maven的下载安装
首选我们需要搭建一个Maven环境,由于本篇侧重于分享解析pom.xml里面的元素,对Maven的下载安装不做过多的阐述,可以参考Maven的安装配置、IDEA中搭建Maven环境一文自行操作,如有问题可以评论区留言;
2、什么是pom?
POM全程Project Object Model,又称项目对象模型。他是Maven工程的基本工作单元,是一个XML(可扩展标记语言)文件,包含了项目的基本信息,用于描述项目如何构建,声明项目依赖等等。执行任务或目标时,Maven会在当前目录中查找 POM并读取从而获取所需的配置信息执行目标,属于项目级别的配置文件。
总之pom最厉害的是提供一站式支持,可用于管理:源代码、配置文件、缺陷跟踪系统(defect tracking system)、组织和许可证(licenses)、项目所在的URL地址、开发者的信息和角色、项目依赖以及其他所有的和代码生命周期相关的方面。而在Maven中就只需要一个pom.xml文件,可以说pom.xml就是Maven的核心!
一个完整的pom.xml文件放在项目的根目录下;
3、较完整的pom元素
pom的整体结构,更详细pom可见 超级POM_POM文件总体配置说明.pdf;
<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>
<!-- The Basics 基本部分 -->
<groupId>...</groupId>
<artifactId>...</artifactId>
<version>...</version>
<packaging>...</packaging>
<dependencies>...</dependencies>
<parent>...</parent>
<dependencyManagement>...</dependencyManagement>
<modules>...</modules>
<properties>...</properties>
<!-- Build Settings 构建设置 -->
<build>...</build>
<reporting>...</reporting>
<!-- More Project Information 更多项目信息 -->
<name>...</name>
<description>...</description>
<url>...</url>
<inceptionYear>...</inceptionYear>
<licenses>...</licenses>
<organization>...</organization>
<developers>...</developers>
<contributors>...</contributors>
<!-- Environment Settings 环境设置 -->
<issueManagement>...</issueManagement>
<ciManagement>...</ciManagement>
<mailingLists>...</mailingLists>
<scm>...</scm>
<prerequisites>...</prerequisites>
<repositories>...</repositories>
<pluginRepositories>...</pluginRepositories>
<distributionManagement>...</distributionManagement>
<profiles>...</profiles>
</project>
4、默认生成Maven工程的pom内容
其中groupId,artifactId,version组成了项目的唯一坐标。
<?xml version="1.0" encoding="UTF-8"?>
<!--project是pom.xml根元素,它包含了pom.xml的一些约束信息,声明了一些POM相关的命名空间以及xsd元素-->
<!-- xmlns 命名空间,类似包名-->
<!-- xmlns:xsi xml遵循的标签规范-->
<!--xsi:schemaLocation 定义xmlschema的地址,xml书写时需要遵循的语法-->
<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">
<!-- 指定了当前pom.xml版本,目前固定为4.0.0版本。-->
<modelVersion>4.0.0</modelVersion>
<!-- 坐标 -->
<!-- 属于哪个组,一般是项目所在组织或公司域名的倒序 -->
<groupId>com.sx.kak</groupId>
<!-- 定义当前项目在组中的唯一ID,一个groupId下面可能多个项目,就是靠artifactId来区分的 -->
<artifactId>nacospro</artifactId>
<!-- 定义项目当前的版本 -->
<version>1.0-SNAPSHOT</version>
<!-- 打包类型,可取值:pom , jar , maven-plugin , ejb , war , ear , rar , par等等 -->
<packaging>jar</packaging>
<!-- 项目的名称(可省略) 默认artifactId,可修改为用户友好的名称 -->
<name>nacospro</name>
<!-- 仓库的地址(可省略) -->
<url>http://maven.apache.org</url>
<!--定义的依赖清单,有所依赖包都需要写在这个标签里面-->
<dependencies>
<!--具体的依赖 -->
<dependency>
</dependency>
</dependencies>
</project>
5、自定义的属性变量
我们可以在POM的元素下自定义Maven属性。定义的属性变量,在其他地方进行使用。
<!-- 定义的属性变量,在其他地方进行使用 -->
<properties>
<!-- Java版本 -->
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- 通过${hutool.version}来使用 -->
<hutool.version>5.0.6</hutool.version>
<!-- 通过${pagehelper.version}来使用 -->
<pagehelper.version>1.3.0</pagehelper.version>
</properties>
6、依赖管理
依赖关系:描述了项目相关的所有依赖,组成了项目构建过程中的一个个环节;它们会自动从项目定义的仓库中下载,一个项目可以设置多了依赖;
可通过https://mvnrepository.com/寻找依赖,获得相应的坐标;具体操作可阅读Maven的安装配置、IDEA中搭建Maven环境一文;
6.1、整体依赖关系列表
<!--定义的依赖清单,有所依赖包都需要写在这个标签里面-->
<dependencies>
<!--HuTool工具包 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool.version}</version>
</dependency>
<!--pagehelper-->
<dependency>
<!--依赖项的组织名-->
<groupId>com.github.pagehelper</groupId>
<!--依赖项的子项目名-->
<artifactId>pagehelper-spring-boot-starter</artifactId>
<!--依赖项的版本-->
<version>${pagehelper.version}</version>
<!-- 依赖项的适用范围 -->
<scope>test</scope>
<!-- 可选依赖 ,对外隐藏当前所依赖的资源,是不透明的;如果别人依赖了本项目,被配置的不会在别人的项目中依赖到-->
<optional>true</optional>
<!-- 排除依赖,主动断开依赖的资源,排除项目中的依赖冲突时使用,不依赖该项目,被排除的资源不需要指定版本-->
<exclusions>
<exclusion>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
</exclusion>
<exclusion>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
6.2、依赖关系的传递性
直接依赖:在当前项目中通过依赖配置建立的依赖关系;
间接依赖:当前工程pom配置了依赖A,A又依赖B,则本工程也依赖B,B为本工程的间接依赖。
如下图:A依赖于B,B又依赖于C,此时B是A的直接依赖,C是A的间接依赖。
我们都知道在Maven中依赖是有传递性的,不管Maven项目存在多少间接依赖,POM中都只需要定义其直接依赖,不必定义任何间接依赖,Maven会动读取当前项目各个直接依赖的POM,将那些必要的间接依赖以传递性依赖的形式引入到当前项目中,能够帮助用户简化POM的配置。
上图A、B、C三者的依赖关系,根据Maven的依赖传递机制,我们只需要在项目A的 POM 中定义其直接依赖B,在项目 B的POM中定义其直接依赖C,Maven会解析A的直接依赖B的POM ,将间接依赖C以传递性依赖的形式引入到项目A中。
6.3、依赖传递可能造成的问题
通过依赖传递关系,可以使依赖关系树迅速增长到一个很大的量级,但很有可能会出现依赖重复,依赖冲突等情况,Maven针对这些情况提供了如下功能进行处理:
- 依赖范围(Dependency scope)
- 依赖调解(Dependency mediation)
- 可选依赖(Optional dependencies)
- 排除依赖(Excluded dependencies)
- 依赖管理(Dependency management)
6.3.1、scope依赖范围
我们可以在POM的依赖声明使用scope元素来控制依赖与三种classpath(编译 classpath、测试 classpath、运行 classpath )之间的关系,这就是依赖范围。
scope依赖项有6个常用的可选范围:
1)compile:默认值,表示编译依赖范围;适用于所有阶段(编译、测试、运行),会随着项目一起发布。表明该jar包一直全程存在/需要;
2)provided:表示已提供依赖范围;编译、测试时需要,运行时不需要,不会被打包。如servlet.jar;
3)runtime:表示运行时提供依赖范围;只在运行时使用,如JDBC驱动,适用运行和测试阶段;
4)test:表示测试依赖范围;测试时有效,用于编译和运行测试代码。不会随项目发布;
5)system:类似provided,需要显式提供包含依赖的jar,Maven不会在Repository中查找它(不推荐);
6)optional:当项目自身被依赖时,标注依赖是否传递。用于连续依赖时使用;
依赖范围与三种classpath 的关系:
6.3.2、依赖调节
Maven中用户只需要关心项目的直接依赖,而不必关心这些直接依赖会引入哪些间接依赖。但当一个间接依赖存在多条引入路径时,为了避免出现依赖重复的问题就会通过依赖调节来确定间接依赖的引入路径。
当依赖中出现相同的资源时,层级越深,优先级越低,层级越浅,优先级越高;
A存在以下的依赖关系
情况一:A->B->C->D
情况二:A->E->D
D是A的间接依赖,但两条引入情况上有两个不同的版本,不可以同时引入,否则造成重复依赖的问题。根据Maven依赖调节的第一个原则:引入路径短者优先,情况一的路径长度为 3,情况二的路径长度为2,因此间接依赖D将从A->E->D路径引入到A中。
当资源在相同层级被依赖时,配置顺序靠前的覆盖配置顺序靠后的;
A存在以下依赖关系
情况一:A->B->D
情况二:A->C->D
<dependencies>
...
<dependency>
...
<artifactId>B</artifactId>
...
</dependency>
...
<dependency>
...
<artifactId>C</artifactId>
...
</dependency>
...
</dependencies>
D是A的间接依赖,其两条引入路径的长度都是2,此时路径优先已经无法解决,需要使用先声明者优先;由以上配置可以看出,由于B的依赖声明比C靠前,所以情况一的间接依赖将从A->B->D路径引入到A中。
优先使用第一条原则解决,第一条原则无法解决,再使用第二条原则解决;
当资源配置了相同资源的不同版本,后配置的覆盖先配置的(不做举例);
6.3.3、可选依赖(6.4.2中详解)
在依赖中配置optional为true/false 是否向下传递,如果配置为true,则别人依赖了本项目,被配置的不会在别人的项目中依赖到。如果为false表示可以向下传递称为间接依赖;
6.3.4、排除依赖(6.4.1中详解)
exclusions所包含坐标,排除依赖包中所包含的依赖关系 ,不需要添加版本,直接类别排除 ,排除依赖可以设置当前依赖中是否使用间接依赖。注意和可选依赖区分,可以达到同样的效果。
6.4、排除依赖和可选依赖
Maven依赖具有传递性,在不考虑依赖范围等因素的情况下,Maven根据依赖传递机制,会将间接依赖C引入到A中。但如果A希望将间接依赖C排除于是Maven提供了两种解决方式:排除依赖和可选依赖。
6.4.1、排除依赖
排除依赖是控制当前项目是否使用其直接依赖传递下来的间接依赖;
-
exclusions元素下可以包含若干个exclusion子元素,用于排除若干个间接依赖;
-
exclusion元素用来设置具体排除的间接依赖,该元素包含两个子元素:groupId 和 artifactId,用来确定需要排除的间接依赖的坐标信息;
-
exclusion元素中只需要设置groupId和artifactId 就可以确定需要排除的依赖,无需指定版本 version。
6.4.2、可选依赖
可选依赖用来控制当前依赖是否向下传递成为间接依赖;
- optional 默认值为 false,表示可以向下传递称为间接依赖;
- 若 optional 元素取值为 true,则表示当前依赖不能向下传递成为间接依赖。
6.4.3、排除依赖和可选依赖举例
假设A依赖于B,B依赖于X,B又依赖于Y。B 实现了两个特性,其中一个特性依赖于X,另一个特性依赖于Y,且两个特性是互斥的关系,用户无法同时使用两个特性,所以A需要排除X,此时就可以在A中将间接依赖X排除。
排除依赖是通过在A中使用 exclusions 元素实现的,该元素下可以包含若干个 exclusion 子元素,用于排除若干个间接依赖,示例代码如下。
<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.sx.kak</groupId>
<artifactId>A</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>com.sx.kak</groupId>
<artifactId>B</artifactId>
<version>1.0-SNAPSHOT</version>
<exclusions>
<!-- 设置排除 -->
<!-- 排除依赖必须基于直接依赖中的间接依赖设置为可以依赖为 false -->
<!-- 设置当前依赖中是否使用间接依赖 -->
<exclusion>
<!--设置具体排除-->
<groupId>com.sx.kak</groupId>
<artifactId>X</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
在B的POM关于X的依赖声明中使用optional 元素,将其设置成可选依赖,示例配置如下。
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.sx.kak</groupId>
<artifactId>B</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>net.biancheng.www</groupId>
<artifactId>X</artifactId>
<version>1.0-SNAPSHOT</version>
<!--设置可选依赖,true则表示当前依赖不能向下传递成为间接依赖 -->
<optional>true</optional>
</dependency>
</dependencies>
</project>
6.4.3、排除依赖 VS 可选依赖
排除依赖和可选依赖都能在项目中将间接依赖排除在外,但两者实现机制却完全不一样。
- 排除依赖是控制当前项目是否使用其直接依赖传递下来的接间依赖;
- 可选依赖是控制当前项目的依赖是否向下传递;
- 可选依赖的优先级高于排除依赖;
- 若对于同一个间接依赖同时使用排除依赖和可选依赖进行设置,那么可选依赖的取值必须为false,否则排除依赖无法生效。
7、Build插件配置
Build理解为构建项目需要的信息,主要用于编译设置;
7.1、Build的两个部分配置
在Maven的pom.xml文件中,Build相关配置包含两个部分,一个是build,另一个是reporting。
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
...
<!-- 称为Project Build,是<project>的直接子元素 -->
<build>...</build>
<profiles>
<profile>
<!-- 称为Profile Build,即是<profile>的直接子元素 -->
<build>...</build>
</profile>
</profiles>
</project>
Profile Build包含了基本的build元素,而Project Build还包含两个特殊的元素,即各种Directory和extensions。
7.2、常用的Build结构
<!-- 构建项目需要的信息 -->
<build>
<!-- 使用的插件列表 -->
<plugins>
<!-- plugin元素包含描述插件所需要的信息 -->
<plugin>
<!-- 插件在仓库里的group ID -->
<groupId>org.springframework.boot</groupId>
<!-- 插件在仓库里的artifact ID -->
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
<!-- 这个元素描述了项目相关的所有资源路径列表,例如和项目相关的属性文件,这些资源被包含在最终的打包文件里。 -->
<resources>
<!-- 这个元素描述了项目相关或测试相关的所有资源路径 -->
<resource>
<!-- 描述存放资源的目录,该路径相对POM路径 -->
<directory>src/main/java</directory>
<!-- 包含的模式列表,例如**/*.xml. -->
<includes>
<include>**/*.xml</include>
</includes>
<!-- 是否使用参数值代替参数名。参数值取自properties元素或者文件里配置的属性,文件在filters元素里列出 -->
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.*</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
8、写在最后_超级POM
看到这里有没有觉得对pom的理解又上了一个层次,经常使用POM文件却对他是一知半解有时候就那么用了却不知道这个标签到底是啥意思;于是就有了去了解每一个标签的作用的想法,虽说还可能有所欠缺,但是确实让我对Maven有了更上一层的理解;我觉得还是有很多没有整理到,后期在继续学习整理吧!
超级POM就是一个比较全的POM文件,如果有遇到不懂的,可以去文档里面搜索;如果有想要超级POM的小伙伴可见 超级POM_POM文件总体配置说明.pdf,或者评论区留下邮箱地址私发给你;
参考文章
- https://maven.apache.org/pom.html#What_is_the_POM
- https://blog.csdn.net/jk418756/article/details/87917776
- https://www.cnblogs.com/cy0628/p/15034450.html