Java,类路径,类加载=>同一jar/项目的多个版本

2022-08-31 08:22:55

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

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

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

类加载器是只拾取一个 jar,还是任意混合类?例如,如果一个类是从版本 1 加载的.jar,那么从同一类加载器加载的所有其他类都将进入同一个 jar?

你如何处理这个问题?

有没有一些技巧可以以某种方式将罐子“合并”到“必需.jar”中,以便被视为“一个单元/包”,或者以某种方式链接?Classloader


答案 1

类加载器相关的问题是一个相当复杂的问题。无论如何,您应该记住一些事实:

  • 应用程序中的类装入器通常不止一个。引导类装入器委托给相应的。实例化新类时,将调用更具体的类装入器。如果它找不到对要尝试装入的类的引用,它将委托给其父级,依此类推,直到您到达引导类装入器。如果他们都没有找到对你尝试加载的类的引用,你会得到一个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

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

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


推荐