maven项目的pom.xml文件给项目导入了不一样的jar包,有时候不一样的dependency会引入同一个jar包的不一样版本。spring
当不一样版本的jar包被依赖后,可能会出现:apache
在pom.xml文件的目录下使用mvn dependency:tree命令能够查看jar包的传递依赖。api
使用-Dverbose 参数能够列出更详细的信息。tomcat
mvn -Dverbose dependency:treebash
从命令运行的输出内容来看,该命令执行的时候会从新build一次。eclipse
若是节选输出内容的其中一部分,多是这样的:jsp
[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包,根据缩进能够看到依赖的关系。maven
解决重复依赖和冲突的方法:ide
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能够只填写一个,为了保证准确性,通常都会填两个(填写时不包括尖括号)。
其余方法:
一、对于maven工程,个人办法是使用eclipse来解决,点开pom.xml,切换到hierarchy dependency,右上角搜索对应的包,能够清晰地看到冲突版本
二、可使用idea,在pom.xml中右单击 选择Diagrams-》show dependencies
三、mvn dependency:tree -Dverbose > tree.log
直接输出冲突的jar文件
1. 依赖的配置
根元素project下的dependencies能够包含一个或多个dependency元素,以声明一个或多个依赖。每一个依赖能够包含的元素有:
groupId、artifactId和version:依赖的基本坐标,坐标三元素。
type:依赖的类型,对应于项目定义的packaging,大部分状况下不须要定义,使用默认值jar。
scope:依赖的范围。
optional:标记依赖是否可选。
exclusions:用来排除传递性依赖。
依赖范围(scope) | 对于编译classpath有效 | 对于测试classpath有效 | 对于运行时classpath有效 |
编译依赖范围:compile | Y | Y | Y |
测试依赖范围:test | - | Y | - |
已提供依赖范围:provided | Y | Y | - |
运行时依赖范围:runtime | - | Y | Y |
系统依赖范围:system | Y | Y | - |
另外还有导入依赖范围:import,该范围不会对三种classpath产生实际的影响。
当A有一个compile范围的B依赖,B有一个compile范围的C依赖,那么C就会成为A的compile范围依赖,C是A的一个传递性依赖。
有了传递性依赖的机制,在使用某个依赖时就不须要考虑它依赖了什么,也不须要担忧引入多余的依赖。Maven会解析各个直接依赖的POM,将那些必要的间接依赖,以传递性依赖的形式引入到当前的项目中。
须要注意的是,可选依赖不会被传递。
compile | test | provided | runtime | |
compile | compile | - | - | runtime |
test | test | - | - | test |
provided | provided | - | provided | provided |
runtime | runtime | - | - | runtime |
当两个依赖路径上有两个版本的依赖X时,有如下两个依赖调解原则:
第一原则:路径最近者优先;
第二原则:路径长度同样时,第一声明者优先。
当项目A依赖于项目B,可是不想引入传递性依赖C,而是本身显示的声明对项目C另外一个版本的依赖,使用exclusions元素声明排除性依赖。
exclusions能够包含一个或者多个exclusion子元素,声明exclusion时只须要groupId和artifactId,不须要version元素。
当项目中依赖了同一项目的不一样模块,它们的版本都是相同的,所以在升级的时候,这些依赖的版本会一块儿升级。为了不重复,且须要修改时只修改一处,能够经过归类依赖来解决。
使用properties元素定义Maven属性,如springframework.version子元素,并定义其值。有了这个属性定义,maven运行时会将POM中全部的${springframwork.version}替换成定义的实际值。
使用dependency:list和dependency:tree 帮助咱们详细了解项目中全部依赖的具体信息。
使用dependency:analyze工具能够帮助分析当前项目的依赖。
analyze的结果中包含了两部分:
Used undeclared dependencies:项目中使用但未显式声明的依赖。这种依赖意味着潜在的风险;
Unused declared dependencise:项目中未使用的,但显式声明的依赖。对于这种依赖不能直接删除,由于analyze只会分析编译和测试须要的依赖,其余依赖它没法发现,所以须要仔细分析。