Maven依赖冲突的产生原因和解决方式

ref:https://blog.csdn.net/qq_27529917/article/details/79741607

http://www.yangbing.club/2017/07/15/solution-for-jar-conflicts/

先来看下Maven的仲裁机制:

  • 优先按照依赖管理<dependencyManagement>元素中指定的版本声明进行仲裁,此时下面的两个原则都无效了
  • 若无版本声明,则按照“短路径优先”的原则(Maven2.0)进行仲裁,即选择依赖树中路径最短的版本
  • 若路径长度一致,则按照“第一声明优先”的原则进行仲裁,即选择POM中最先声明的版本

 

现在我们了解了classloader的结构和工作原理,那么我们如何实现jar包的容器隔离呢?

答案很简单: 我们实现一个新的classloader就可以了!

 

自定义ClassLoader

在自定义ClassLoader的子类时候,我们常见的会有两种做法,一种是重写loadClass方法,另一种是重写findClass方法。其实这两种方法本质上差不多,毕竟loadClass也会调用findClass,但是从逻辑上讲我们最好不要直接修改loadClass的内部逻辑。
个人认为比较好的做法其实是只在findClass里重写自定义类的加载方法。
为啥说这种比较好呢,因为前面我也说道,loadClass这个方法是实现双亲委托模型逻辑的地方,擅自修改这个方法会导致模型被破坏,容易造成问题。因此我们最好是在双亲委托模型框架内进行小范围的改动,不破坏原有的稳定结构。同时,也避免了自己重写loadClass方法的过程中必须写双亲委托的重复代码,从代码的复用性来看,不直接修改这个方法始终是比较好的选择。
当然,如果是刻意要破坏双亲委托模型就另说。

破坏双亲委托模型

为什么要破坏双亲委托模型呢?
其实在某些情况下,我们可能需要加载两个不同的类,但是不巧的是这两个类的名字完全一样,这时候双亲委托模型就无法满足我们的要求了,我们就要重写loadClass方法破坏双亲委托模型,让同一个类名加载多次。当然,这里说的破坏只是局部意义上的破坏。
但是类名相同了,jvm怎么区别这两个类呢?显然,这并不会造成什么世界观的崩塌,其实类在jvm里并不仅是通过类名来限定的,他还属于加载他的ClassLoader。由不同ClassLoader加载的类其实是互不影响的。

有没有不用反射的更优雅的调用方法

显然,每次都用反射来调用还是太蠢了,难道就没有更方便的类似用类名引用的方法么?当然是有的,前面之所以不能直接用类名引用是因为原生类的类加载器是systemClassLoader,而从class文件创建的类的类加载器是自定义的classLoader,这两个类本质不一样,因此才不能互相强制转换,如果硬要强制转换就会报ClassCastException。那么,如果我们提取一个父类,父类由systemClassLoader加载,而子类由自定义classLoader加载,然后强制转换的时候转换成父类不就好了么?
做个试验,创建一个父类Father,其实就是提取了个抽象方法:

 

posted on 2019-12-09 17:18  LittleSpring  阅读(1400)  评论(0编辑  收藏  举报