详解Maven JAR包冲突问题排查及解决方案

网友投稿 369 2022-11-01


详解Maven JAR包冲突问题排查及解决方案

前言

写这篇文章的初衷是因为今天在使用mvn dependency:tree命令时,突然想起一年前面试阿里的一道面试题。面试题是说假设线上发生JAR包冲突,应该怎么排查?我那时候的回答是IDEA有个Maven Helper的插件,可以帮忙分析依赖冲突,然后还有一种办法是如果一个类import的时候提示两个地方可导入,那就说明有冲突。现在回头想想确实太不专业了,以下是一次JAR包冲突的一个比较正规的流程,是通过整理几篇博客后总结的希望对大家也有帮助,如果有错误的地方也欢迎指出

github地址:https://github.com/RobertoHuang

JAR冲突产生的原因

Pom.xml

/ \

B C

/ \ / \

X Y X M

在以上依赖关系中项目除了会引入B、C还会引入X、Y、M的依赖包,但是如果B依赖的X版本会1.0而C依赖的X版本为2.0时,那最后项目使用的到底是X的1.0版本还是2.0版本就无法确定了。这是就要看ClassLoader的加载顺序,假设ClassLoader先加载1.0版本那就不会加载2.0版本,反之同理

使用mvn -Dverbose dependency:tree排查冲突

[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 xx的,说明和别的JAR包版本冲突了,该行的JAR包不会被引入

该命令可配合-Dincludes和-Dexcludes进行使用,只输出自己感兴趣/不感兴趣的JAR

参数格式为:[groupId]:[artifactId]:[type]:[version]

每个部分(冒号分割的部分)是支持*通配符的,如果要指定多个格式则可以用,分割,如:

mvn dependency:tree -Dincludes=javax.servlet,org.apache.*

解决冲突,使用exclusion标签将冲突的JAR排除

com.alibaba

dubbo

2.8.3.2

guava</artifactId>

com.google.guava

spring

org.springframework

解决了冲突后的树(解决冲突的策略是:就近原则,即离根近的依赖被采纳)

查看运行期类来源的JAR包

有时你以为解决了但是偏偏还是报类包冲突,典型症状是java.lang.ClassNotFoundException或Method不兼容等异常,这时你可以设置一个断点,http://在断点处通过下面这个工具类来查看Class所来源的JAR包

public class ClassLocationUtils {

public static String where(final Class clazz) {

if (clazz == null) {

throw new IllegalArgumentException("null input: cls");

}

URL result = null;

final String clazzAsResource = clazz.getName().replace('.', '/').concat(".class");

final ProtectionDomain protectionDomain = clazz.getProtectionDomain();

if (protectionDomain != null) {

final CodeSource codeSource = protectionDomain.getCodeSource();

if (codeSource != null) result = codeSource.getLocation();

if (result != null) {

if ("file".equals(result.getProtocol())) {

try {

if (result.toExternalForm().endsWith(".jar") || result.toExternalForm().endsWith(".zip")) {

result = new URL("jar:".concat(result.toExternalForm()).concat("!/").concat(clazzAsResource));

} else if (new File(result.getFile()).isDirectory()) {

result = new URL(result, clazzAsResource);

}

} catch (MalformedURLException ignore) {

}

}

}

}

if (result == null) {

final ClassLoader clsLoader = clazz.getClassLoader();

result = clsLoader != null ? clsLoader.getResource(clazzAsResource) : ClassLoader.getSystemResource(clazzAsResource);

}

return result.toString();

}

}

然后随便写一个测试设置好断点,在执行到断点出使用ALT+F8动态执行代码,如

ClassLocationUtils.where(Logger.class)

即可马上找到对应的JAR,如果这个JAR不是你期望的就说明是IDE缓存造成的,清除缓存后重试即可


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:UVA 1635 Irrelevant Elements——组合数+唯一分解定理
下一篇:UVA 10003 Cutting Sticks——四边形不等式优化dp
相关文章

 发表评论

暂时没有评论,来抢沙发吧~