用dependency:tree查看maven引入jar包的传递依赖

2017/10/26 17:07
阅读数 1.8W

maven项目的pom.xml文件给项目导入了不同的jar包,有时候不同的dependency会引入同一个jar包的不同版本。

当不同版本的jar包被依赖后,可能会出现:

  1. 冲突。
  2. 有的版本的jar包不会被classloader引入,而有的代码确实需要那个没有被引入的jar包,进而出现错误。

在pom.xml文件的目录下使用mvn dependency:tree命令可以查看jar包的传递依赖。

使用-Dverbose 参数可以列出更详细的信息。

mvn -Dverbose dependency:tree

从命令运行的输出内容来看,该命令执行的时候会重新build一次。

如果节选输出内容的其中一部分,可能是这样的:

[INFO] +- org.apache.tomcat:tomcat-servlet-api:jar:7.0.70:compile
[INFO] +- org.apache.tomcat:tomcat-jsp-api:jar:7.0.70:compile
[INFO] |  +- org.apache.tomcat:tomcat-el-api:jar:7.0.70:compile
[INFO] |  \- (org.apache.tomcat:tomcat-servlet-api:jar:7.0.70:compile - omitted for duplicate)
[INFO] +- net.sf.jasperreports:jasperreports:jar:5.6.0:compile
[INFO] |  +- (commons-beanutils:commons-beanutils:jar:1.8.0:compile - omitted for conflict with 1.8.3)
[INFO] |  +- commons-collections:commons-collections:jar:3.2.1:compile
[INFO] |  +- commons-digester:commons-digester:jar:2.1:compile
[INFO] |  |  +- (commons-beanutils:commons-beanutils:jar:1.8.3:compile - omitted for duplicate)
[INFO] |  |  \- (commons-logging:commons-logging:jar:1.1.1:compile - omitted for duplicate)

递归依赖的关系列的算是比较清楚了,每行都是一个jar包,根据缩进可以看到依赖的关系。

  • 最后写着compile的就是编译成功的。
  • 最后写着omitted for duplicate的就是有jar包被重复依赖了,但是jar包的版本是一样的。
  • 最后写着omitted for conflict with xxxx的,说明和别的jar包版本冲突了,而该行的jar包不会被引入。比如上面有一行最后写着omitted for conflict with 1.8.3,那么该行的commons-beanutils:jar:1.8.0不会被引入,只有1.8.3版本的会被引入。

解决重复依赖和冲突的方法:

1,修改pom文件中两个dependency元素的位置。如果两个dependency都引用了一个jar包,但是版本不同,classloader只会加载jar包在pom文件中出现的第一个版本,以后出现的其他版本的jar包会被忽略。

不建议使用该方法,因为引用不同版本的jar包本身就是很危险的。

2,使用<exclusions>标签来去掉某个dependency依赖中的某一个jar包或一堆jar包,<exclusion>中的jar包或者依赖的相关jar包都会被忽略,从而在两个dependency都依赖某个jar包时,可以保证只使用其中的一个。

可以这么写:

<dependency>
	<groupId>com.alibaba</groupId>
	<artifactId>dubbo</artifactId>
	<version>2.8.3.2</version>
	<exclusions>
		<exclusion>
			<artifactId>guava</artifactId>
			<groupId>com.google.guava</groupId>
		</exclusion>
		<exclusion>
			<artifactId>spring</artifactId>
			<groupId>org.springframework</groupId>
		</exclusion>	
	</exclusions>
</dependency>

 

对于Jar包冲突问题,我们开发人员经常都会有碰到,当我们使用一些jar包中的类、方法等,或者有时遇到一些日志系统的问题(参考另一篇文章Jar包冲突导致的日志问题),我们会遇到ClassNotFoundException,NoSuchFieldException,NoSuchMethodException 之类的运行时异常,从经验上我们就会判断,Jar包冲突了。解决Jar包冲突问题,每个人都有每个人的方法,这里我介绍一下我的方法,供大家参考。

处理方法

当遇到jar包冲突时,我们首先确定是哪个jar包冲突了,这个很容易,看我们调用的类或方法,是属于哪个Jar包。然后就是要找出冲突了,我这里使用命令

mvn dependency:tree -Dverbose -Dincludes=<groupId>:<artifactId>

填写上Jar包的groupId和artifactId,可以只有一个,但是中间的冒号不要少,这样就会输出依赖树,而且是仅包含这个Jar包的依赖树,这样那些地方依赖了这个Jar包的那个版本就一目了然了。
例如,我的项目中notify-common包存在冲突,我们使用命令

mvn dependency:tree -Dverbose -Dincludes=:notify-common

得到依赖树输出

[INFO] com.taobao.wlb:bis-server:war:1.0-SNAPSHOT
[INFO] +- com.taobao.wlb:bis-core:jar:1.0-SNAPSHOT:compile
[INFO] |  \- com.taobao.logistics:schedule-client:jar:1.1.1:compile
[INFO] |     \- (com.taobao.notify:notify-common:jar:1.8.15:compile - omitted for conflict with 1.8.19.26)
[INFO] \- com.taobao.notify:notify-tr-client:jar:1.8.19.26:compile
[INFO]    +- com.taobao.notify:notify-common:jar:1.8.19.26:compile
[INFO]    \- com.taobao.notify:notify-remoting:jar:1.8.19.26:compile
[INFO]       \- (com.taobao.notify:notify-common:jar:1.8.19.26:compile - omitted for duplicate)  

看一下依赖树中所有的叶子节点就是所有的notify-common包,我们可以看到我们依赖的bis-core中依赖了schedule-client包,它依赖了一个notify-common包,版本是1.8.15,第四行的后面也提示了这个包同其他包有冲突- omitted for conflict with 1.8.19.26)。而我们的系统依赖的notify-tr-client包所依赖的版本是1.8.19.26,于是我们知道是这里冲突了,在POM排除掉依赖,OK了。

说明

这里我们对我们执行的命令做一个简单的说明。

mvn dependency:tree -Dverbose -Dincludes=<groupId>:<artifactId>

第一部分mvn dependency:tree是maven依赖的分析命令,作用是对我们的项目的依赖进行分析,并输出项目依赖树
第二部分-Dverbose的作用是添加了verbose一个环境变量,起的作用是在分析项目依赖时输出明细,这样项目中依赖的所有引用都会被输出出来,包含了所有的间接引用,会有很多很多,我们只需要我们要找的,所以就需要第三个参数了

第三部分-Dincludes=<groupId>:<artifactId>的作用就是进行过滤,只包含我们想要的依赖的依赖时,排除掉其它不需要的,依赖树的所有叶子节点就是我们的找的依赖包。其中的groupId和artifactId可以只填写一个,为了保证准确性,一般都会填两个(填写时不包括尖括号)。

其他方法:

1、对于maven工程,我的办法是使用eclipse来解决,点开pom.xml,切换到hierarchy dependency,右上角搜索对应的包,可以清晰地看到冲突版本

2、可以使用idea,在pom.xml中右单击 选择Diagrams-》show dependencies

3、mvn dependency:tree -Dverbose > tree.log 
直接输出冲突的jar文件

Maven依赖

1. 依赖的配置

    根元素project下的dependencies可以包含一个或多个dependency元素,以声明一个或多个依赖。每个依赖可以包含的元素有:
groupId、artifactId和version:依赖的基本坐标,坐标三元素。
type:依赖的类型,对应于项目定义的packaging,大部分情况下不需要定义,使用默认值jar。
scope:依赖的范围。
optional:标记依赖是否可选。
exclusions:用来排除传递性依赖。

2. 依赖的范围

依赖范围(scope) 对于编译classpath有效 对于测试classpath有效 对于运行时classpath有效
编译依赖范围:compile Y Y Y
测试依赖范围:test - Y -
已提供依赖范围:provided Y Y -
运行时依赖范围:runtime - Y Y
系统依赖范围:system Y Y -

另外还有导入依赖范围:import,该范围不会对三种classpath产生实际的影响。

3. 传递性依赖,和数学里的传递性,是同样的概念。

    当A有一个compile范围的B依赖,B有一个compile范围的C依赖,那么C就会成为A的compile范围依赖,C是A的一个传递性依赖。
    有了传递性依赖的机制,在使用某个依赖时就不需要考虑它依赖了什么,也不需要担心引入多余的依赖。Maven会解析各个直接依赖的POM,将那些必要的间接依赖,以传递性依赖的形式引入到当前的项目中。
    需要注意的是,可选依赖不会被传递。

4. 依赖范围影响传递性依赖

  compile test provided runtime
compile compile - - runtime
test test - - test
provided provided - provided provided
runtime runtime - - runtime

5. 依赖调解

 当两个依赖路径上有两个版本的依赖X时,有以下两个依赖调解原则:
第一原则:路径最近者优先;
第二原则:路径长度一样时,第一声明者优先。

6. 排除依赖

当项目A依赖于项目B,但是不想引入传递性依赖C,而是自己显示的声明对项目C另一个版本的依赖,使用exclusions元素声明排除性依赖。

    exclusions可以包含一个或者多个exclusion子元素,声明exclusion时只需要groupId和artifactId,不需要version元素。

7. 归类依赖

当项目中依赖了同一项目的不同模块,它们的版本都是相同的,因此在升级的时候,这些依赖的版本会一起升级。为了避免重复,且需要修改时只修改一处,可以通过归类依赖来解决。

    使用properties元素定义Maven属性,如springframework.version子元素,并定义其值。有了这个属性定义,maven运行时会将POM中所有的${springframwork.version}替换成定义的实际值。

8. 优化依赖

使用dependency:list和dependency:tree 帮助我们详细了解项目中所有依赖的具体信息。

使用dependency:analyze工具可以帮助分析当前项目的依赖。

analyze的结果中包含了两部分:

Used undeclared dependencies:项目中使用但未显式声明的依赖。这种依赖意味着潜在的风险;

Unused declared dependencise:项目中未使用的,但显式声明的依赖。对于这种依赖不能直接删除,因为analyze只会分析编译和测试需要的依赖,其他依赖它无法发现,因此需要仔细分析。

 

 

 

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部