Java包冲突常见解决方法

Java的好处之一是有大量的库可供开发者使用,然而,这些库通常都有较多版本,并且也往往会依赖其他的库。 使用Maven或者其他构建工具时,经常需要将这些依赖打包成一个Jar包,或者自己的Jar包与其他的Jar包同时放到Classpath中。 这些时候,很容易就会产生一个常见的问题,就是依赖的库不同的版本间会有冲突。

具体说来,如果项目代码同时依赖于库A和库B,库A和库B又依赖不同版本的库C。 如果库C不同版本间的函数签名或者实现上有差别,或是某个类的成员final等的修饰不同,就可能会出现如下两类异常。

  1. Method Not Found Exception
  2. Cannot Overwrite final …

解决这些冲突的第一步是定位问题,找到库C两个版本之间,冲突的原因是什么。 是新的版本删除或者添加了新的函数?还是新的版本把final关键字删了,还是把以前的非final的成员变成final了?

定位问题之后,最简单的解决方法就是,看看是否能够调整库A或库B的版本,使它们依赖的库C的版本能够兼容。

大多数时候,我们不能调整库A或库B的版本,或者这两个库的任何版本组合都不同使依赖的库C能够兼容。 我们需要进一步分析产生冲突的原因,区分清楚冲突究竟是属于以下三种情况中的哪一种,再分别解决:

  1. 库A和库B用到的库C的函数在两个版本中都存在并且签名一致。这种情况下,冲突产生的原因应该是C不同版本之间实现方法有区别。 例如库A调用了库C版本1中类File的open方法,该方法又调用类FileImpl的open(String path)方法,而在库C的版本2中类File的open方法调用的是FileImpl的open(URI path)函数,由于函数签名不一样,载入了版本1的File类和版本2的FileImpl类,就会产生冲突。在这种情况下,可以显式的exclude掉库A或者库B中的对库C的依赖,只使用其中的一个版本。
  2. 库A中用到的函数在库C两个版本中都存在,库B用到的函数在另一个版本中不存在。例如库A用到的函数File.open()在两个版本中都存在,但是库B中用到的函数File.safeOpen()只在库B所依赖的版本中存在。 这种情况下, 我们需要exclude掉库A对库C的依赖,只使用库B中使用的版本。
  3. 库A和库B用到的函数都只存在于各自依赖的版本中。这种情况下,需要去寻找看看是否存在另一个版本,里面能兼容库A和库B的函数调用。如果可以,则将库A和库B对库C的依赖都exclude,引入该版本。如果不存在,则只能让两个版本同时出现,这个时候只能采用Shade大法了。