依赖冲突一篇搞定

1 什么是传递依赖冲突

Maven引入的传递性依赖机制,一方面大大简化和方便了依赖声明,另一方面,大部分情况下我们只需要关心项目的直接依赖是什么,而不用考虑这些直接依赖会引入什么传递性依赖。但有时候,当传递性依赖会造成问题。

dependencies-example-1-e1473694110436-1.jpg

例如项目A有这样的依赖关系:X->Y->Z(1.0)、X->G->Z(2.0),Z是X的传递性依赖,但是两条依赖路径上有两个版本的Z,那么哪个Z会被Maven解析使用呢?两个版本都被解析显然是不对的,因为那会造成依赖重复,因此必须选择一个。


2 你必须知道的依赖调解规则

Maven依赖调解(Dependency Mediation)的第一原则是:路径最近者优先。该例中X(1.0)的路径长度为3,而X(2.0)的路径长度为2,因此X(2.0)会被解析使用。

依赖调解第一原则不能解决所有问题,比如上面这个例子,两条依赖路径长度是一样的,都为2。那么到底谁会被解析使用呢?在Maven 2.0.8及之前的版本中,这是不确定的,但是从Maven 2.0.9开始,为了尽可能避免构建的不确定性,Maven定义了依赖调解的第二原则:第一声明者优先


3 如何解决依赖冲突

3.1 加载提前

在清楚了Maven的依赖调解规则后,我可以很自然地想到解决方案,就是把我们需要的版本的路径缩短或者声明提前。如下图:

direct-child-z-dependencies-example-e1473768217230-1.jpg

3.2 排除依赖

也就是使用exclusions元素声明排除其中一个依赖,exclusions可以包含一个或者多个exclusion子元素,因此可以排除一个或者多个传递性依赖。需要注意的是,声明exclusion的时候只需要groupId和artifactId,而不需要version元素,这是因为只需要groupId和artifactId就能唯一定位依赖图中的某个依赖。换句话说,Maven解析后的依赖中,不可能出现groupId和artifactId相同,但是version不同的两个依赖。

            <dependency>
                <groupId>com.alibaba.lava</groupId>
                <artifactId>lava-core</artifactId>
                <version>3.0.1-YUJUN-SNAPSHOT</version>
                <exclusions>
                    <exclusion>
                        <groupId>org.mybatis</groupId>
                        <artifactId>*</artifactId>
                    </exclusion>
                    <exclusion>
                        <artifactId>jakarta.commons.collections</artifactId>
                        <groupId>com.alibaba.external</groupId>
                    </exclusion>
                    <exclusion>
                        <artifactId>mcms.client</artifactId>
                        <groupId>com.alibaba.intl.sourcing.shared</groupId>
                    </exclusion>
                </exclusions>
            </dependency>

3.3 升级父节点

使用上面三种方法都有一个前提,那就是你选定的version是可以兼容两个冲突的jar。但是两个jar不兼容的话,针对这种情况, 去掉任何一个依赖,都会出现异常。这时,我们查看整个依赖树,找到其父节点,升级其父节点version。


4 全路径冲突

还有一种特殊的冲突,多个dependency的groupID或artifactID不同(或两者都不同),但包中存在全路径类名相同的类Java类加载器根据classpath加载类时,根据classpath中jar包出现的先后顺序进行查找类并缓存,后面jar包中的类不使用。这个时候的常见异常就是NoSuchMethodExceptionNoClassDefFoundErrorClassNotFoundExceptionNoSuchMethodError等。

如果其中一个jar是我们不需要的,那么排除它就行了。但是,如果这个jar被很多dependency依赖,你需要一个个去写exclusions是不是很麻烦。这时我们可以直接在pom中添加一个空依赖(和想要去掉的jar的groupID,artifactID相同,但是version不同的一个空项目打包上传到远程仓库中)。

            <!-- ================================================= -->
            <!-- 排除依赖 -->
            <!-- ================================================= -->
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-nop</artifactId>
                <version>999-not-exist-v3</version>
            </dependency>
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-log4j12</artifactId>
                <version>999-not-exist-v3</version>
            </dependency>
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>log4j-over-slf4j</artifactId>
                <version>999-not-exist</version>
            </dependency>

5 如何快速发现依赖冲突

5.1 常用的Maven命令

5.1.1 mvn dependency:list

Maven会自动解析所有项目的直接依赖和传递性依赖,并且根据规则正确判断每个依赖的范围,对于一些依赖冲突,也能进行调节,以确保任何一个构件只有唯一的版本在依赖中存在。在这些工作之后,最后得到的那些依赖被称为已解析依赖(Resolved Dependency)。可以运行如下的命令查看当前项目的已解析依赖:

mvn dependency:list

5.1.2 mvn dependency:tree

能够查看依赖树,通过这棵树能够很清楚的看到某个依赖是通过哪条路径引入进来的。

mvn dependency:tree -Dverbose

详细显示依赖信息,把版本冲突中被抛弃,重复的都显示出来,便于排查问题。

mvn dependency:tree -Dverbose --> a.txt

将结果保存到文件中

mvn dependence:tree -Dverbose -Dincludes=org.mybatis:mybatis

includes指的是想看哪些信息。

参数格式[groupId]:[artifactedId]

5.1.3 mvn dependency:analyze

可以帮助分析当前项目的依赖。

(1)Used undeclared dependencies

意指项目中使用到的,但是没有显式声明的依赖,这里是spring-context。这种依赖意味着潜在的风险,当前项目直接在使用它们,例如有很多相关的Java import声明,而这种依赖是通过直接依赖传递进来的,当升级直接依赖的时候,相关传递性依赖的版本也可能发生变化,这种变化不易察觉,但是有可能导致当前项目出错。例如由于接口的改变,当前项目中的相关代码无法编译。这种隐藏的、潜在的威胁一旦出现,就往往需要耗费大量的时间来查明真相。因此,显式声明任何项目中直接用到的依赖。

(2)Unused declared dependencies

意指项目中未使用的,但显式声明的依赖,这里有spring-core和spring-beans。需要注意的是,对于这样一类依赖,我们不应该简单地直接删除其声明,而是应该仔细分析。由于dependency:analyze只会分析编译主代码和测试代码需要用到的依赖,一些执行测试和运行时需要的依赖它就发现不了。很显然,该例中的spring-core和spring-beans是运行Spring Framework项目必要的类库,因此不应该删除依赖声明。当然,有时候确实能通过该信息找到一些没用的依赖,但一定要小心测试。

5.1.4 mvn enforcer:enforce

首先,需要将下面的插件添加到pom.xml中:

<plugins>
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-enforcer-plugin</artifactId>
        <version>1.4.1</version>
        <configuration>
            <rules><dependencyConvergence/></rules>
        </configuration>
    </plugin>
</plugins>

注意这里配置的rules规则,它有很多内置的规则:

  • dependencyConvergence:确保依赖只有一个版本存在于项目中,否则build报错,报错信息中就告诉你哪些有冲突了。

  • banDuplicateClasses:找到重复的类。

  • bannedDependencies:配置不允许使用的jar。例如只能使用log4j2,不允许使用log4j1。

5.2 IDEA maven helper插件

当Maven Helper 插件安装成功后,打开项目中的pom文件,下面就会多出一个试图

20190217233212.png

切换到此试图即可进行相应操作:

  • Conflicts(查看冲突)

  • All Dependencies as List(列表形式查看所有依赖)

  • All Dependencies as Tree(树形式查看所有依赖)

20190217233339.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,692评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,482评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,995评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,223评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,245评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,208评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,091评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,929评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,346评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,570评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,739评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,437评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,037评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,677评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,833评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,760评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,647评论 2 354

推荐阅读更多精彩内容

  • 前言什么是 POMQuick Overview POM 常用元素 pom.xml 完整注释 参考 0 前言 什么是...
    seyvoue阅读 12,601评论 1 36
  • Jar包冲突是老生常谈的问题,几乎每一个Java程序猿都不可避免地遇到过,并且也都能想到通常的原因一般是同一个Ja...
    sherlockyb阅读 37,333评论 1 65
  • |-1-更新内容[6.从仓库解析依赖的机制(重要)] 1Maven仓库作用 仓库用来存储所有项目使用到构件,在ma...
    zlcook阅读 6,052评论 0 25
  • spring官方文档:http://docs.spring.io/spring/docs/current/spri...
    牛马风情阅读 1,670评论 0 3
  • 朋友,也许你正为写作时不能准确而清晰的表达你的某种心情或描述一种状态而苦恼吧? 没关系,让我来告诉你解决这个问题的...
    鑫墉阅读 1,536评论 11 16