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

今年年初的时候 , 阅读过《Maven实战》 , 当时有了解到Maven可以依赖调解 , 即当包的版本不一致时 , 会根据一定规则选择相应的包来加载 , 从而避免冲突 。 当时不解的是既然Maven都能解决冲突 , 为何还经常听到“发生了依赖冲突” , 冲突不是解决了吗 , 还存在什么问题呢?直到这周在工作中自己遇到了 , 就明白是咋回事了 。 下面从我的实际经历说起 。
1. Maven依赖冲突经历我在Y模块中 , 写了一个Encryptor类 , 主要是使用了DigestUtils、MessageDigest、HmacUtils等类对字符串进行加密(下面代码是随便写的 , 只表示使用到了这些类) , 如下:
import org.apache.commons.codec.binary.Hex;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错误 。
一次Maven依赖冲突采坑,把依赖调解、类加载彻底整明白了文章插图
在Y模块下的单元测试运行时不会报错 , 但是当整个应用启动 , 作为程序入口的X模块 , 调用Y模块中的Encryptor时 , 发生了IllegalAccessError报错 。 根据图中的具体报错信息 , 是说没有权限访问getSha256Digest方法 , 我Ctrl+B点进getSha256Digest方法查看 , 如下:
一次Maven依赖冲突采坑,把依赖调解、类加载彻底整明白了文章插图
getSha256Digest方法是public的访问级别 , 我一脸懵 。 由于这个方法很简单 , 既然报错 , 那我就索性不用了 , 换成下面这种写法 。
public String encrype(String s) {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";}}复制代码又报错了 , 好吧 , 真是躲不过了!报错如下:
一次Maven依赖冲突采坑,把依赖调解、类加载彻底整明白了文章插图
这次报的是ClassNotFoundException , HmacUtils这个类找不到 。 可是我Ctrl+B进去 , 这个类好好的就在那里啊 。 这时我才把注意力集中在思考是不是发生了依赖冲突 。 我打开pom.xml , 用Dependency Analyzer查看 , 果然我使用的commons-codec包发生了冲突 。
一次Maven依赖冲突采坑,把依赖调解、类加载彻底整明白了文章插图
在Y模块中 , 依赖关系:Y -> B -> commons-codec-1.10 。 而在X模块中 , 引用了A包:X -> A -> commons-codec-1.6 , 也引用了Y模块:X -> Y -> B -> C -> commons-codec-1.10 。 可见commons-codec包有两个版本1.6和1.10 , 所以Maven会进行依赖调解 , 第一原则是“路径最短者优先” , 自然只会使用1.6版本的包 。 而我再去查看1.6的包下 , getSha256Digest方法是private的访问级别 , HmacUtils这个类也不存在 。 解释了之前的报错 。 解决该冲突 , 通过排除依赖便能解决了 , 将A包下的commons-codec排除 , 如下:
com.chaycao.maven.dependencyA1.0-SNAPSHOTcommons-codeccommons-codeccom.chaycao.maven.dependencyY1.0-SNAPSHOT复制代码排除后 , 这时将只有1.10版本的包 , 程序也可以正常运行了 。
2. 为什么需要Maven依赖调解问题已经解决了 , 大家是不是也明白了 , 为什么依赖冲突会常导致发生NoClassDefFoundError、NoSuchMethodException、IllegalAccessError等错误 。 虽然Y模块在编译时 , 由于引入了commons-codec 1.10能正常编译 , 但是在运行时 , 由于依赖冲突 , 只加载了1.6版本的包 , 所以不能正常运行 。
【一次Maven依赖冲突采坑,把依赖调解、类加载彻底整明白了】注意:代码的编译仅仅是编译当前的代码 。 编译成功后 , 最后能否正常运行 , 还要取决于运行时的环境是否等同或兼容编译时环境 。