jiaji's blog

maven包冲突和解决

玄学问题

maven的作者认为项目中依赖哪个包应该完全由开发人员来负责(确实应该如此),所以maven只提供了几个在加载包时的优先级机制和规则。在工作中,开发人员对maven了解程度不同、依赖的包又间接依赖混乱等问题,导致应用在启动时经常会出现”class not found”或者”no such method”的异常,最终应用无法启动。如果不解决包冲突,那就经常会出现测试环境ok,但是发到线上环境就异常的“玄学”问题。

maven加载优先级

所以maven的优先级和规则究竟是怎样的呢?在研究了官方文档后做了一些总结,一共有如下几个规则:

  1. “nearest definition”最近优先原则:如果两个包A和B在同一层,它们分别间接依赖了包C,那么会加载离AB这层最近的版本的C。如果C的距离也是相同的,那么就加载遇到的第一个。maven的这个机制试图解决传递依赖过程中,具体用哪个包的问题。
  2. “Dependency management”提供一种机制可以让开发者显示指定要用哪个版本,实现统一管理。一般在多模块的父子pom模式中,在父pom中使用指定好所有依赖的版本,子pom中写自己要用的包,但不带版本号。

优先级问题:

子模块在加载时,遇到一个包要确定版本号:如果子pom指定了版本号,则使用此版本的包(不推荐这样做);否则尝试使用父pom中dependencyManagement中标明的版本号;dependencyManagement中也没有标明,则依赖“nearest definition”规则,找离根节点最近的包,或者同距离的第一个包。

最佳实践

  1. 创建项目:在父pom中dependencyManagement列清所有要依赖的包和版本号,子pom中写自己要用的包的groupId和artifactId,版本由父pom统一管理。
  2. 给别人打包:maven依赖可以指定在不同阶段使用不通的范围,具体可以参考官方文档中Dependency Scope的介绍。给别人打包时尽量少依赖第三方的包、或者最好把第三包的Scope设置为provided,减少对于使用方的影响。

解决方法

了解了maven加载优先级,但是还是躲不掉包冲突的问题,因为我们不可能去遍历依赖包的所有传递依赖。解决包冲突问题有几个步骤:

  1. 查看日志,定位到包的代码。确定是哪些包冲突了。
  2. 使用maven:tree命令或者idea辅助确定:
  3. 在父pom中使用exclude把不用的包排掉。

选择依赖时尽量使用有名的或者大厂的包,可能会避免很多问题。