前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Maven 依赖调解源码解析(六):dependencyManagement 版本锁定

Maven 依赖调解源码解析(六):dependencyManagement 版本锁定

作者头像
xiaoxi666
发布2021-11-24 09:54:28
7580
发布2021-11-24 09:54:28
举报
文章被收录于专栏:xiaoxi666的专栏

场景

我们在根模块 mavenDependencyDemo 中,用 dependencyManagement 的形式直接指定 X 的版本为 2.0。同时,A 依赖 C,而 C 依赖了 X(1.0)。我们观察下,最终 A 会依赖 X(1.0)还是 X(2.0)。

根模块 mavenDependencyDemo 的 pom.xml 文件内容:

代码语言:javascript
复制
<?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>org.example</groupId>
    <artifactId>mavenDependencyDemo</artifactId>
    <packaging>pom</packaging>
    <version>1.0</version>
    <modules>
        <module>A</module>
        <module>B</module>
        <module>C</module>
        <module>D</module>
        <module>X</module>
    </modules>
    
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.example</groupId>
                <artifactId>X</artifactId>
                <version>2.0</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

</project>

A 的 pom.xml 内容如下:

代码语言:javascript
复制
<?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">
    <parent>
        <artifactId>mavenDependencyDemo</artifactId>
        <groupId>org.example</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>A</artifactId>
    <version>1.0</version>

    <dependencies>
        <dependency>
            <groupId>org.example</groupId>
           <artifactId>C</artifactId>
            <version>1.0</version>
        </dependency>
    </dependencies>

</project>

使用命令 mvn dependency:tree -Dverbose 命令,看看依赖树:

代码语言:javascript
复制
[INFO] Scanning for projects...
[INFO] 
[INFO] ---------------------------< org.example:A >----------------------------
[INFO] Building A 1.0
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ A ---
[INFO] org.example:A:jar:1.0
[INFO] \- org.example:C:jar:1.0:compile
[INFO]    \- org.example:X:jar:2.0:compile (version managed from 1.0)
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  1.410 s
[INFO] Finished at: 2021-11-20T16:31:03+08:00
[INFO] ------------------------------------------------------------------------

可以看到使用了 X(2.0),也就是 dependencyManagement 中指定的 X(2.0)。

另外输出了一行关键信息:

代码语言:javascript
复制
(version managed from 1.0)

稍后,我们将根据这个关键信息去找一下对应的源码。

阅读源码之前,我们先到 Maven 官网 https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html 探索一下,发现有一句话:

Dependency management - this allows project authors to directly specify the versions of artifacts to be used when they are encountered in transitive dependencies or in dependencies where no version has been specified.

简单翻译一下,使用 dependencyManagement 声明的依赖若指定了版本,可以:

  1. 指定传递依赖的版本(即使传递依赖有自定义版本,也会被覆盖掉);
  2. 当直接依赖没有指定版本时,指定其版本。

当然,如果使用 dependencyManagement 声明的依赖没有指定版本,传递依赖的自定义版本就会生效了。

我们的场景,显然对应着第一种描述:dependencyManagement 可以指定传递依赖的版本(即使传递依赖有自定义版本,也会被覆盖掉),其实就是版本锁定的概念了。

源码

顺着关键信息,我们找到了 maven-dependency-tree 的源码段:

可以看到是由 getPremanagedVersion 这个方法控制的,那我们进去看看它干了啥:

可以看到,该方法的作用就是:获取「被 dependencyManagement 中定义的版本号更新之前」的版本号,顺藤摸瓜,我们看看 premanagedVersion 是在哪里被赋值的:

可以看到有两个赋值的地方(this.xxx 那个只是个简单的 set 方法),那我们都打上断点,重新调试:

可以看到,是 DependencyTreeResolutionListener 实现了 Maven 核心项目中的 org.apache.maven.artifact.resolver.ResolutionListener#includeArtifact 方法。

那回到 Maven 核心项目,看看 includeArtifact 方法被哪里调用了:

继续顺着调用链往上找,

看到很奇怪的现象,C 依赖的 X 变成 2.0 版本了,但是 C 中明明是 X(1.0)啊,到底是哪里把它改了呢?

同时,我们会发现 managedVersions 也有值了(以前是没有的),而且正好是 dependencyManagement 中定义的 X(2.0):

我们重新调试,继续探索,看看是不是 managedVersions 动了什么手脚。还是进入递归方法。

可以看到,解析 C 依赖的 X(1.0)时,被“manage”了,也即:版本被改成了 2.0。

很明显,版本号是以 managedVersions 为准的。

现在我们知道了 X(1.0)是如何被改成了 X(2.0),那么问题来了,上面说的 managedVersions 又是从哪里来的呢?

让我们继续顺着调用链寻找:

可以看到,dependency:tree 插件是 magenedVersions 的「出生地」,这也是这个插件被称作核心插件的原因。因此我们根据调用栈的提示,再到这个插件项目的 org.apache.maven.shared.dependency.tree.DefaultDependencyTreeBuilder#buildDependencyTree 看看:

可以看到是从 project 中获取到的:

那么我们需要继续看 project 是在哪里出生的:

看到这里,已经发现 project 是从 maven-dependency-plugin 中传过来的,那么我们转到这个 TreeMojo 中继续调试:

可以看出,project 其实是从 Maven 核心项目传入的,那我们继续回到 Maven 核心项目中调试:

往上翻翻,发现 project 是从 session 中获取到的:

到此,我们发现一种包含关系:session -> currentProject -> managedVersionMap。赋值的地方比较多,在不熟悉源码的情况下,我们把所有 setCurrentProject 和 setManagedVersionMap 的地方都打上断点,看看哪里进行了赋值。最终我们找到了这里:

至此,我们找到了「解析 dependencyManagement 中定义的依赖版本」的源码。

小结

依赖解析过程中,会解析 dependencyManagement 定义的依赖版本,如果解析到了,会以 dependencyManagement 重定义的依赖版本为准,也就是我们常提到的版本锁定。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021-11-21 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 场景
  • 源码
  • 小结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档