问题

我知道这对经验丰富的程序员来说可能是一个愚蠢的问题。但我有一个库(一个http客户端),我的项目中使用的一些其他框架/ jar需要。但所有这些都需要不同的主要版本,如:

httpclient-v1.jar => Required by cralwer.jar
httpclient-v2.jar => Required by restapi.jar
httpclient-v3.jar => required by foobar.jar

类加载器是否足够智能以某种方式将它们分开?很可能不是吗?如果Class在所有三个jar中都相同,Classloader如何处理这个问题。哪一个加载,为什么?

类加载器是仅仅拾取一个罐子还是任意混合类?因此,例如,如果从Version-1.jar加载一个类,那么从同一个类加载器加载的所有其他类都将进入同一个jar?

你怎么处理这个问题?

是否有一些技巧以某种方式将罐子"合并"到"required.jar"中,以便被Classloader视为"一个单元/包",或以某种方式链接?


#1 热门回答(45 赞)

类加载器相关的问题是一个非常复杂的问题。在任何情况下,你都应该记住一些事实:

  • 应用程序中的类加载器通常不止一个。引导类加载器委托给适当的。实例化新类时,将调用更具体的类加载器。如果它没有找到你正在尝试加载的类的引用,它会委托给它的父类,依此类推,直到你进入引导类加载器。如果他们都没有找到你尝试加载的类的引用,则会得到ClassNotFoundException。
  • 如果你有两个具有相同二进制名称的类,可以通过相同的类加载器进行搜索,并且你想知道要加载哪个类,则只能检查特定类加载器尝试解析类名的方式。
  • 根据java语言规范,类二进制名称没有唯一性约束,但据我所知,它对于每个类加载器应该是唯一的。

我可以想出一种方法来加载具有相同二进制名称的两个类,并且它涉及通过两个不同的类加载器加载它们(以及它们的所有依赖项)来覆盖默认行为。一个粗略的例子:

ClassLoader loaderA = new MyClassLoader(libPathOne);
    ClassLoader loaderB = new MyClassLoader(libPathTwo);
    Object1 obj1 = loaderA.loadClass("first.class.binary.name", true)
    Object2 obj2 = loaderB.loadClass("second.class.binary.name", true);

我总是发现类加载器定制是一项棘手的任务。如果可能的话,我宁愿建议避免多个不兼容的依赖项。


#2 热门回答(19 赞)

每个类加载只选择一个类。通常是第一个找到的。

OSGi旨在解决同一jar的多个版本的问题.EquinoxApache Felix是OSGi的常见开源实现。


#3 热门回答(6 赞)

类加载器将首先从恰好位于类路径中的jar加载类。通常情况下,库的不兼容版本在包中会有所不同,但在不太可能的情况下,它们实际上是不兼容的,不能用一个替换 - 尝试jarjar。


原文链接