Maven提高篇系列之——处理依赖冲突
2018-02-17 11:15
309 查看
Maven依赖jar包冲突常见的解决方法
项目中,经常会遇到ClassNotFound,NoSuchMethod异常,第一反应往往是类路径不对,jar没有正确的引用。第一步判断jar是否加载,还是 加载的jar由于maven依赖管理存在传递依赖,造成依赖的jar版本号不对,相应的类找不到,或者是相应类版本不对,没有对应的方法。一 造成jar 冲突的原因:如果项目中存在对同一jar不同版本依赖的时候,maven 2根据最近原则,默认引用最靠近项目版本的jar,maven 2.0.9会根据最先声明原则 来引用相应版本的jar;无论那种方式,都会出现jar包冲突。在这里提一下gradle依赖,会依赖最新版本的jar。二 判断jar是否正确的被引用 有两种方法:1在项目启动时加上VM参数:-verbose:class项目启动的时候会把所有加载的jar都打印出来 类似如下的信息:classpath加载的jar-classpath /home/yao/tool/jdk1.8.0_25/jre/lib/jce.jar:/home/yao/tool/jdk1.8.0_25/jre/lib/resources.jar:
/home/yao/tool/jdk1.8.0_25/jre/lib/rt.jar:
/home/yao/tool/jdk1.8.0_25/jre/lib/management-agent.jar:
/home/yao/tool/jdk1.8.0_25/jre/lib/jfxswt.jar:
/home/yao/tool/jdk1.8.0_25/jre/lib/plugin.jar:
/home/yao/tool/jdk1.8.0_25/jre/lib/javaws.jar:
/home/yao/tool/jdk1.8.0_25/jre/lib/charsets.jar:
/home/yao/tool/jdk1.8.0_25/jre/lib/jsse.jar:
/home/yao/tool/jdk1.8.0_25/jre/lib/deploy.jar:
/home/yao/tool/jdk1.8.0_25/jre/lib/jfr.jar:
/home/yao/tool/jdk1.8.0_25/jre/lib/ext/localedata.jar:
/home/yao/tool/jdk1.8.0_25/jre/lib/ext/nashorn.jar:
/home/yao/tool/jdk1.8.0_25/jre/lib/ext/jfxrt.jar:
/home/yao/tool/jdk1.8.0_25/jre/lib/ext/sunjce_provider.jar:
...............等等
具体load的类[Loaded java.lang.Object from /home/yao/tool/jdk1.8.0_25/jre/lib/rt.jar]
[Loaded java.io.Serializable from /home/yao/tool/jdk1.8.0_25/jre/lib/rt.jar]
[Loaded java.lang.Comparable from /home/yao/tool/jdk1.8.0_25/jre/lib/rt.jar]
[Loaded java.lang.CharSequence from /home/yao/tool/jdk1.8.0_25/jre/lib/rt.jar]
[Loaded java.lang.String from /home/yao/tool/jdk1.8.0_25/jre/lib/rt.jar]
[Loaded java.lang.reflect.AnnotatedElement from /home/yao/tool/jdk1.8.0_25/jre/lib/rt.jar]
[Loaded java.lang.reflect.GenericDeclaration from /home/yao/tool/jdk1.8.0_25/jre/lib/rt.jar]
[Loaded java.lang.reflect.Type from /home/yao/tool/jdk1.8.0_25/jre/lib/rt.jar]
[Loaded java.lang.Class from /home/yao/tool/jdk1.8.0_25/jre/lib/rt.jar]
..................等等
我们可以通过上面的信息查找对应的jar是否正确的被依赖,具体类加载情况,同时可以看到版本号,确定是否由于依赖冲突造成的jar引用不正确;2 通过maven自带的工具:mvn dependency:tree具体后面可以加 -Dverbose 参数 ,详细参数可以去自己搜,这里不详细介绍。比如分析如下POM运行: mvn dependency:tree -Dverbose <dependencies>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.2-FINAL</version>
</dependency>
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.7.0</version>
</dependency>
</dependencies>
输出结果:[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ Dependency ---
[INFO] com.yao:Dependency:pom:1.0-SNAPSHOT
[INFO] +- org.apache.poi:poi:jar:3.2-FINAL:compile
[INFO] | +- commons-logging:commons-logging:jar:1.1:compile
[INFO] | | \- log4j:log4j:jar:1.2.13:compile
[INFO] | \- (log4j:log4j:jar:1.2.13:compile - scope updated from runtime; omitted for duplicate)
[INFO] \- commons-beanutils:commons-beanutils:jar:1.7.0:compile
[INFO] \- (commons-logging:commons-logging:jar:1.0.3:compile - omitted for conflict with 1.1)
[INFO] ------------------------------------------------------------------------
通过里面的信息可以看到 两个jar都commons-logging存在依赖,但是版本不同。里面的详细信息显示引用了 commons-logging:commons-logging:jar:1.1 去掉了commons-logging:commons-logging:jar:1.0.3 (omitted for duplicate)。通过以上方法我们可以看到项目中引用jar版本号;接下来就是如何排除掉我们不想要版本的jar;三 通过Idea intellij 中的Show Dependencies的工具去除重复的jar在Pom.xml文件上右击 选择 Diagrams -> Show Dependencies 即可查看Pom的依赖图,通过图可以很容易的看到依赖冲突的jar,exculude掉不想要的版本jar即可。
Maven提高篇系列之五——处理依赖冲突
不知道你在使用Maven时是否遇到过诸如"NoSuchMethodError"或"ClassNotFoundException"之类的问题,甚至发生这些问题的Java类你没都没有听说过。要搞清楚这里面的缘由,我们得学习Maven对依赖冲突的处理机制。 Maven采用“最近获胜策略(nearest wins strategy)”的方式处理依赖冲突,即如果一个项目最终依赖于相同artifact的多个版本,在依赖树中离项目最近的那个版本将被使用。让我们来看看一个实际的例子。 请下载本文的github源代码:https://github.com/davenkin/maven-dependency-conflict-demo 我们有一个web应用resolve-web,该工程依赖于project-A和project-B,project-A依赖于project-common的1.0版本并调用其中的sayHello()方法。project-B依赖于project-C,而project-C又进一步依赖于project-common的2.0版本并调用其中的sayGoodBye()方法。project-common的1.0和2.0版本是不同的,1.0中之包含sayHello()方法,而2.0中包含了sayHello()和sayGoodBye()两个方法。整个项目的依赖关系如下图:根据Maven的transitive依赖机制,resolve-web将同时依赖于project-common的1.0和2.0版本,这就造成了依赖冲突。而根据最近获胜策略,Maven将选择project-common的1.0版本作为最终的依赖。这和Gradle不同,Gradle在默认情况下将选择最新的版本作为获胜版本。而对于Maven,由于proejct-common的1.0版本比2.0版本在依赖树中离resolve-web更近,故1.0版本获胜。在resolve-web中执行"mvn dependency:tree -Dverbose"可以看到resolve-web的依赖关系:
[INFO] resolve-web:resolve-web:war:1.0-SNAPSHOT[INFO] +- junit:junit:jar:3.8.1:test[INFO] +- project-B:project-B:jar:1.0:compile[INFO] | \- project-C:project-C:jar:1.0:compile[INFO] | \- (project-common:project-commmon:jar:2.0:compile - omitted for conflict with 1.0)[INFO] +- project-A:project-A:jar:1.0:compile[INFO] | \- project-common:project-commmon:jar:1.0:compile[INFO] \- javax.servlet:servlet-api:jar:2.4:provided
由上可知,project-common:project-commmon:jar:2.0被忽略掉了。此时在resolve-web的war包中将只包含project-common的1.0版本,于是问题来了。由于project-common的1.0版本中不包含sayGoodBye()方法,而该方法正是project-C所需要的,所以运行时将出现“NoSuchMethodError”。(请根据本文github工程中的README.md中的步骤重现该错误信息。)
解决办法对于这种有依赖冲突所导致的问题,我们有两种解决方法。
方法1:显式加入对project-common 2.0版本的依赖。先前的2.0版本不是离resolve-web远了点吗,那我们就直接将它作为resolve-web的依赖,这不就比1.0版本离resolve-web还近吗?在resove-web的pom.xml文件中直接加上对project-common 2.0 的依赖:
<dependency> <groupId>project-common</groupId> <artifactId>project-commmon</artifactId> <version>2.0</version> </dependency>
方法2:resolve-web对project-A的dependency声明中,将project-common排除掉。在resolve-web的pom.xml文件中修改对project-A的dependency声明:
<dependency> <groupId>project-A</groupId> <artifactId>project-A</artifactId> <version>1.0</version> <exclusions> <exclusion> <groupId>project-common</groupId> <artifactId>project-commmon</artifactId> </exclusion> </exclusions> </dependency>
此时再在resolve-web中执行"mvn dependency:tree -Dverbose",结果如下:
......[INFO] resolve-web:resolve-web:war:1.0-SNAPSHOT[INFO] +- junit:junit:jar:3.8.1:test[INFO] +- project-B:project-B:jar:1.0:compile[INFO] | \- project-C:project-C:jar:1.0:compile[INFO] | \- project-common:project-commmon:jar:2.0:compile[INFO] +- project-A:project-A:jar:1.0:compile[INFO] \- javax.servlet:servlet-api:jar:2.4:provided......
此时的依赖树中已经不包含project-common的1.0版本了。
另外,我们还可以在project-A中将对project-common的依赖声明为optional,optional即表示非transitive,此时当在resolve-web中引用project-A时,Maven并不会将project-common作为transitive依赖自动加入,除非有别的项目(比如project-B)声明了对project-common的transitive依赖或者我们在resolve-web中显式声明对project-common的依赖(方法一)。
相关文章推荐
- Maven提高篇系列之五——处理依赖冲突
- Maven提高篇系列之五——处理依赖冲突
- Maven提高篇系列之(五)——处理依赖冲突
- Maven提高篇系列之(五)——处理依赖冲突
- Maven提高篇系列之(五)——处理依赖冲突
- Maven提高篇系列之五——处理依赖冲突
- Maven依赖冲突处理
- Maven管理项目依赖-依赖冲突快速处理
- maven处理依赖冲突(未完)
- Maven处理依赖冲突
- Maven 处理依赖冲突
- maven之Transitive dependencies(默认树的先序遍历算法处理依赖冲突)
- 如何快速的解决Maven依赖冲突
- maven下载依赖jar包失败处理方法--下载jar包到本地并安装到maven仓库中
- 德州仪器增强Jacinto信息娱乐处理器系列的DSP和视觉处理以提高数字化驾驶舱集成
- Maven传递依赖冲突解决
- maven依赖本地非repository中的jar包-依赖jar包放在WEB-INF/lib等目录下的情况客户端编译出错的处理
- maven 检查依赖冲突和版本冲突
- maven 检查依赖冲突和版本冲突
- maven隐式依赖引起的包冲突