Maven 依赖与插件解析机制
劝退提醒:不了解 maven 坐标机制,没有使用过 maven 插件,不了解 maven 插件命令的,可以关闭文章了。
我们经常在项目中引入包,使用 <dependency>
标签,然后填写包的坐标信息, Maven 就可以帮我们引入该 jar 包。当本地存在该 jar 包时,就从本地引入该 jar 包,否则就从远程仓库引入。
这背后的解析机制是什么呢?
依赖解析机制
依赖解析的背后机制可以概括如下:
依赖范围是
system
,maven 就会从本地加载该 jar 包,完成构建。正常解析依赖坐标,先去本地仓库找,找到就完成构建。
本地找不到,并且显示的指定了版本信息,就去远程仓库中遍历,找到并下载解析。
如果依赖的版本信息并没有指定,而是使用
RELEASE
或者LATEST
,他会找到远程仓库的元数据和本地的元数据进行合并,然后计算出真正的版本号。元数据指的就是仓库中groupId/artifactId/maven-metadata.xml
文件,例如我们打开 guava 包的元数据。元数据位置就在 com.google.guava/guava 包下,打开可以看到如下信息:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22<metadata modelVersion="1.1.0">
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<versioning>
<latest>30.1.1-jre</latest>
<release>30.1.1-jre</release>
<versions>
<version>r03</version>
<version>r05</version>
<version>r06</version>
<version>r07</version>
<version>r08</version>
<version>r09</version>
...
<version>30.1-android</version>
<version>30.1-jre</version>
<version>30.1.1-android</version>
<version>30.1.1-jre</version>
</versions>
<lastUpdated>20210319161151</lastUpdated>
</versioning>
</metadata上面的信息展示了 latest,release 对应的版本号,以及 guava 包的历代版本号,以及最近一次更新时间。例如我们引入 guava,并使用 latest 版本,maven 就会合并元数据,然后计算出 latest 对应的版本号,然后从本地仓库找到改版本的 guava,没有则去远程仓库下载。
依赖的版本是 SNAPSHOT,则同样获取远程元数据与本地合并,计算版本信息,获取对应版本的包。
如果依赖版本为时间戳的快照版本,就会先转换成非时间戳的快照版本,然后去解析下载。
maven 3 中如果不指定 version,则默认解析使用 RELEASE 版本。
插件解析机制
我们常用的 maven 插件命令都是 mvn dependency:tree
mvn flyway:migrate
,诸如此类的,他们的格式都是 mvn 插件前缀:目标
。
为了方便用户使用和配置插件,maven 不需要用户提供完整的坐标信息,就能解析得到正确的插件。那么问题来了,maven 是怎么确定插件的坐标和版本的呢?例如 mvn dependency:tree
他执行了什么插件,插件坐标版本信息是什么?
插件的解析机制和依赖解析机制基本一致,不同的是远程仓库不一样。配置插件的远程仓库地址需要使用 <pluginRepository>
标签配置,如下:
1 |
|
除了 <pluginRepositories>
和 <pluginRepository>
不同,其他的与依赖的仓库配置信息一致。
默认的 groupId
在 pom 文件在红配置插件信息的时候,如果插件是 maven 官方的插件(groupId 为 org.apache.maven.plugins),就可以省略 groupId 的配置。如下是官方的 clean 插件配置,没有指定 groupId,但是解析坐标的时候会带上。
1 |
|
在插件配置中,不推荐省略 groupId 的写法,但这与我们使用 maven 插件命令有关。
省略版本信息
maven 的核心插件都在超级 pom 中显示的声明了具体的版本信息,因此当这些插件未指定版本信息的时候,就会使用超级 pom 中指定的插件版本。
如果是非核心插件,且未指定版本信息。在 maven2 中默认解析至 latest,maven3 默认解析至 release,具体的解析过程与依赖解析一致,都是根据合并计算元数据,得到具体的版本信息。
插件前缀解析
我们使用命令 mvn dependency:tree
插件前缀是 dependency,我们是如何根据这个前缀信息得到该插件的完整坐标呢?
这就需要结合上面的知识了,首先存在一个保存 groupId 和 artifactId 对应关系的文件 maven-metadata.xml,该文件存在 groupId / maven-metadata.xml。由于上面的 dependency 是官方插件,因此 groupId 为 org.apache.maven.plugins
,我们去远程仓库找到该文件,如下图。
我们可以找到插件前缀 dependency 对应的 articfactId 是 maven-dependency-plugin
,因此插件的完整坐标可以确定了。
获取 groupId
这里还有个问题,因为我们是提前知道 dependency 是官方的插件,可以推出他的 groupId ,但是 maven 是怎么知道它的 groupId 呢?
maven 的主要插件都在 [https://repo1.maven.org/maven2/org/apache/maven/plugins](https://repo1.maven.org/maven2/org/apache/maven/plugins)
和[https://repository.codehaus.org/org/code-haus/mojo](https://repository.codehaus.org/org/code-haus/mojo)
下,他们对应的 groupId 分别是 org.apache.maven.plugins
和 org.codehaus.mojo
。maven 解析插件的时候就会默认使用这两个 groupId 去匹配,检查 org/apache/maven/plugins/maven-metadata.xml,org/codehaus.momjo/maven-metadata.xml 文件,判断是否有匹配的插件前缀,如果有则获取对应的坐标信息,完成解析。
倘若想使用的插件是第三方的,就可以通过配置 setting.xml 文件,让 maven 也检查其他 groupId 上的仓库 metadata.xml 文件。
1 |
|
示例
解析 dependency:tree 命令
先使用默认的和配置的第三方的 groupId 去找到对应的
maven-metadata.xml
文件,然后检查是否包含 dependency 前缀信息。包含前缀信息,获取对应 articfactId,不包含则使用下一个 groupId 的 maven-metadata.xml 文件。如果所有都获取不到,则报错。
获取到 articfactId 之后根据上面 获取版本信息,即可得到完整的坐标,完成解析执行。