一次Maven依赖冲突采坑,把依赖调解、类加载彻底整明白了

Maven依赖冲突经历
Encryptor类,主要是使用了DigestUtils、MessageDigest、HmacUtils等类对字符串进行加密(下面代码是随便写的,只表示使用到了这些类),如下:import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.codec.digest.HmacUtils;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
public class Encryptor {
public String encrype(String s) {
MessageDigest sha256Digest = DigestUtils.getSha256Digest();
String result = Hex.encodeHexString(sha256Digest.digest(s.getBytes(StandardCharsets.UTF_8)));
return Hex.encodeHexString(HmacUtils.getHmacSha256(result.getBytes()).doFinal(result.getBytes()));
}
public static void main(String[] args) {
Encryptor encryptor = new Encryptor();
String s = 'test';
String result = encryptor.encrype(s);
System.out.println(result);
}
/**
output: fdd04dcac94e9803a72e4268141f773e2024a8fe46ba19a263be22c5ca83e931
**/
}
IllegalAccessError错误。
Encryptor时,发生了IllegalAccessError报错。根据图中的具体报错信息,是说没有权限访问getSha256Digest方法,我Ctrl+B点进getSha256Digest方法查看,如下:
getSha256Digest方法是public的访问级别,我一脸懵。由于这个方法很简单,既然报错,那我就索性不用了,换成下面这种写法。try {
MessageDigest sha256Digest = MessageDigest.getInstance('SHA-256');
String result = Hex.encodeHexString(sha256Digest.digest(s.getBytes(StandardCharsets.UTF_8)));
return Hex.encodeHexString(HmacUtils.getHmacSha256(result.getBytes()).doFinal(result.getBytes()));
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return 'error';
}
}

ClassNotFoundException,HmacUtils这个类找不到。可是我Ctrl+B进去,这个类好好的就在那里啊。这时我才把注意力集中在思考是不是发生了Maven依赖冲突。我打开pom.xml,用Dependency Analyzer查看,果然我使用的commons-codec包发生了冲突。
commons-codec包有两个版本1.6和1.10,所以Maven会进行依赖调解,第一原则是“路径最短者优先”,自然只会使用1.6版本的包。而我再去查看1.6的包下,getSha256Digest方法是private的访问级别,HmacUtils这个类也不存在。解释了之前的报错。解决该冲突,通过排除依赖便能解决了,将A包下的commons-codec排除,如下:<dependency>
<groupId>com.chaycao.maven.dependency</groupId>
<artifactId>A</artifactId>
<version>1.0-SNAPSHOT</version>
<exclusions>
<exclusion>
<artifactId>commons-codec</artifactId>
<groupId>commons-codec</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.chaycao.maven.dependency</groupId>
<artifactId>Y</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
为什么需要Maven依赖调解
NoClassDefFoundError、NoSuchMethodException、IllegalAccessError等错误。虽然Y模块在编译时,由于引入了commons-codec 1.10能正常编译,但是在运行时,由于依赖冲突,只加载了1.6版本的包,所以不能正常运行。注意:代码的编译仅仅是编译当前的代码。编译成功后,最后能否正常运行,还要取决于运行时的环境是否等同或兼容编译时环境。
shell命令将jar包拼接:LIB_JARS=`ls $LIB_DIR|grep .jar|awk '{print ''$LIB_DIR'/'$0}'|tr '\n' ':'`
排查在线问题的利器-Arthas
IllegalAccessError报错时,可以通过Arthas直接查看运行情况下的DigestUtils。我们把代码变为最初的情况,且在Main类中加个死循环,为了让程序不死掉,以通过Arthas观察。public static void main(String[] args) {
while (true) {
try {
Encryptor encryptor = new Encryptor();
String s = '1234567890';
String result = encryptor.encrype(s);
System.out.println(result);
} catch (Throwable e) {
}
}
}
}
sc命令查看DigestUtils:class-info org.apache.commons.codec.digest.DigestUtils
code-source /D:/mavenrepo/commons-codec/commons-codec/1.6/commons-codec-1.6.jar
name org.apache.commons.codec.digest.DigestUtils
isInterface false
isAnnotation false
isEnum false
isAnonymousClass false
isArray false
isLocalClass false
isMemberClass false
isPrimitive false
isSynthetic false
simple-name DigestUtils
modifier public
annotation
interfaces
super-class +-java.lang.Object
class-loader +-sun.misc.Launcher$AppClassLoader@58644d46
+-sun.misc.Launcher$ExtClassLoader@24e74ca5
classLoaderHash 58644d46
code-source中清晰的查到DigestUtils是哪个包下的Class,这时就该意识到发生了依赖冲突问题。jad命令,还能反编译,在线看代码。好用!参考
Arthas 实战,助你解决同名类依赖冲突问题 (https://www.cnblogs.com/goodAndyxublog/p/12424734.html) Maven依赖冲突问题原理简析 (https://blog.csdn.net/qq_27529917/article/details/79741607) 重新看待Jar包冲突问题及解决方案 (http://www.yangbing.club/2017/07/15/solution-for-jar-conflicts/)
