不同场景下Maven依赖排除机制工作原理相关问题咨询
问题1:项目未使用library-A中依赖library-B的类,Maven是否会下载library-B?
答案:会。
Maven的依赖解析逻辑完全基于pom文件的依赖声明,不会提前扫描项目代码实际用到哪些类来裁剪传递依赖。只要library-A的pom中声明了对library-B的compile范围传递依赖,不管你项目代码有没有用到关联的class A,Maven都会把library-B下载到本地仓库,同时纳入项目的编译、运行类路径。
问题2:排除了library-A传递的library-B,且项目没有其他引入library-B的配置,是否编译正常运行时报错?
答案:是的,符合描述的场景。
如果你的项目代码没有直接引用library-B中的类,只调用了library-A中class A的方法,编译阶段只会检查你直接引用的class A是否存在,不会检查class A的间接依赖,因此可以正常编译完成。但运行时类加载器加载class A时,需要同步加载它依赖的class B,此时类路径中没有library-B,就会抛出ClassNotFoundException或NoClassDefFoundError错误。
问题3:排除了library-A传递的library-B,但library-C传递引入了兼容版本的library-B,项目是否可以正常运行?
答案:是的,可以正常运行。
Maven最终只会将同一个groupId+artifactId的依赖的某一个版本放入全局类路径,只要library-C传递引入的library-B版本和library-A的需求兼容,不管这个library-B是从哪个依赖路径引入的,运行时class A都能正常加载到class B,不会有问题。
问题4:排除library-A的library-B后,dependency:tree不显示A依赖B,但A可能用到其他路径引入的B的说法是否正确?如何验证A是否依赖B?
首先这个说法是完全正确的。dependency:tree展示的是每个依赖经过排除规则过滤后的声明式传递依赖关系,你对library-A配置了library-B的排除,自然不会在library-A的子节点下看到library-B,但只要全局类路径中存在library-B,library-A运行时就可以正常使用它。
要确认library-A是否实际依赖library-B,可以用以下方法:
- 用JDK自带的
jdeps工具分析:执行jdeps library-A的jar包路径,就可以直接输出该jar包依赖的所有外部类和对应的依赖包 - 用字节码反编译工具(如JD-GUI)打开library-A的jar包,查看对应类的import和调用逻辑,确认是否有对library-B的引用
- 运行项目时添加JVM参数
-XX:+TraceClassLoading,观察运行日志中是否有library-B的类被加载的记录
内容的提问来源于stack exchange,提问作者MABC

