一般Java开发过程中,包冲突异常提现在下面几个
- AbstractMethodError
- NoClassDefFoundError
- ClassNotFoundException
- LinkageError
像上图,就发生了包引用冲突,如果C包v0.1版本在前,JVM加载过v0.1版本里的同名类后就不会加载v0.2里的同名类,这很可能造成丢失了v0.2里同类里新增的方法属性等等。
接下来我们看看Maven对这种情况是如何进行管理的。
Maven 对 pom 文件的传递性依赖自动进行管理, 其中有一个原则 绝对不允许最终的classpath出现同名不同版本的jar包
。
Maven会根据pom文件中的groupId、artifactId、version来判断jar是否冲突,这就是规范约定的好处之一。
如果出现了同名不同版本的jar包,Maven的处理原则是 离你项目更近的jar包会被选中,其他的淘汰
, 这句话什么意思呢 请看下图:
C包V0.2胜出,因为它离我们的项目更近,看图应该就感觉很直观啦,
最终的classpath
就是 A包、B包、D包、C包v0.2, Maven将C包v0.1排除掉啦。
Maven如何查看jar包依赖的jar
- 使用idea左侧的Maven工具栏,点击关心的jar包展开下面依赖的jar就好。
- 使用
mvn dependency:tree
使用 mvn dependency:tree -Dverbose -Dincludes=groupId:artifactId 可以列举出自己只关心的jar包, 例如我只关心org.springframework.boot:spring-boot-starter-json
这个jar就使用后面这个命令: mvn dependency:tree -Dverbose -Dincludes=org.springframework.boot:spring-boot-starter-json
也可以把结果输出到一个文件里打开查看: mvn dependency:tree -Dverbose -Dincludes=org.springframework.boot:spring-boot-starter-json > a.txt
手动处理包冲突
上面我们知道了Maven帮我们自动处理冲突采用的是就近原则,但这样并不保险,
有的时候万一最近的包是低版本的,这时候就会出现问题,
这时候工具做不了的事情就得我们人工手动来干预了,手动解决冲突有两种办法:
- 将需要的jar包直接写入到自己项目中的pom文件里去,这样根据Maven就近原则,即使其他的jar依赖了相同jar不同版本的,Maven也不会再采用。
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.0.4.RELEASE</version>
</dependency>
- 使用Maven的exclusion,直接把其他包里的依赖排除掉,保留一个自己需要的就行。
<dependency>
<groupId>com.xxx.xxx</groupId>
<artifactId>xxxx-common</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</exclusion>
</exclusions>
<version>2.0-SNAPSHOT</version>
</dependency>
- 使用Idea插件
Maven Helper
这个插件可以很方便的分析出来有冲突的jar包,并且可以点击进行排除等处理,
具体查看官方文档 https://plugins.jetbrains.com/plugin/7179-maven-helper/ 。
最后一个问题
如上图,C包1和2的版本感觉距离是一样的呀,那么Maven会排除哪一个呢?
答案是保留v0.1,如果距离一样,那么谁先声明就先保留谁,A包比D包先声明,所以保留A包的引用依赖。