Maven 依赖与插件解析机制

劝退提醒:不了解 maven 坐标机制,没有使用过 maven 插件,不了解 maven 插件命令的,可以关闭文章了。

我们经常在项目中引入包,使用 <dependency> 标签,然后填写包的坐标信息, Maven 就可以帮我们引入该 jar 包。当本地存在该 jar 包时,就从本地引入该 jar 包,否则就从远程仓库引入。

这背后的解析机制是什么呢?

依赖解析机制

依赖解析的背后机制可以概括如下:

  1. 依赖范围是 system,maven 就会从本地加载该 jar 包,完成构建。

  2. 正常解析依赖坐标,先去本地仓库找,找到就完成构建。

  3. 本地找不到,并且显示的指定了版本信息,就去远程仓库中遍历,找到并下载解析。

  4. 如果依赖的版本信息并没有指定,而是使用 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,没有则去远程仓库下载。

  5. 依赖的版本是 SNAPSHOT,则同样获取远程元数据与本地合并,计算版本信息,获取对应版本的包。

  6. 如果依赖版本为时间戳的快照版本,就会先转换成非时间戳的快照版本,然后去解析下载。

  7. maven 3 中如果不指定 version,则默认解析使用 RELEASE 版本。

插件解析机制

我们常用的 maven 插件命令都是 mvn dependency:tree mvn flyway:migrate,诸如此类的,他们的格式都是 mvn 插件前缀:目标

为了方便用户使用和配置插件,maven 不需要用户提供完整的坐标信息,就能解析得到正确的插件。那么问题来了,maven 是怎么确定插件的坐标和版本的呢?例如 mvn dependency:tree 他执行了什么插件,插件坐标版本信息是什么?

插件的解析机制和依赖解析机制基本一致,不同的是远程仓库不一样。配置插件的远程仓库地址需要使用 <pluginRepository> 标签配置,如下:

1
2
3
4
5
6
7
<pluginRepositories>
<pluginRepository>
<id></id>
<name></name>
<url></url>
</pluginRepository>
</pluginRepositories>

除了 <pluginRepositories><pluginRepository>不同,其他的与依赖的仓库配置信息一致。

默认的 groupId

在 pom 文件在红配置插件信息的时候,如果插件是 maven 官方的插件(groupId 为 org.apache.maven.plugins),就可以省略 groupId 的配置。如下是官方的 clean 插件配置,没有指定 groupId,但是解析坐标的时候会带上。

1
2
3
4
5
6
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
</plugins>

在插件配置中,不推荐省略 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 ,我们去远程仓库找到该文件,如下图。

image.png

我们可以找到插件前缀 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.pluginsorg.codehaus.mojo。maven 解析插件的时候就会默认使用这两个 groupId 去匹配,检查 org/apache/maven/plugins/maven-metadata.xml,org/codehaus.momjo/maven-metadata.xml 文件,判断是否有匹配的插件前缀,如果有则获取对应的坐标信息,完成解析。

倘若想使用的插件是第三方的,就可以通过配置 setting.xml 文件,让 maven 也检查其他 groupId 上的仓库 metadata.xml 文件。

1
2
3
4
5
<setting>
<pluginGroups>
<pluginGroup>第三方仓库 groupId</pluginGroup>
</pluginGroups>
</setting>

示例

解析 dependency:tree 命令

  1. 先使用默认的和配置的第三方的 groupId 去找到对应的 maven-metadata.xml文件,然后检查是否包含 dependency 前缀信息。

  2. 包含前缀信息,获取对应 articfactId,不包含则使用下一个 groupId 的 maven-metadata.xml 文件。如果所有都获取不到,则报错。

  3. 获取到 articfactId 之后根据上面 获取版本信息,即可得到完整的坐标,完成解析执行。


Maven 依赖与插件解析机制
http://wszzf.top/2021/08/26/Maven 依赖与插件解析机制/
作者
Greek
发布于
2021年8月26日
许可协议