Java,Classpath,Classloading =>同一个jar /项目的多个版本

jen*_*ens 114 java jar classpath classloader

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

httpclient-v1.jar => Required by cralwer.jar
httpclient-v2.jar => Required by restapi.jar
httpclient-v3.jar => required by foobar.jar
Run Code Online (Sandbox Code Playgroud)

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

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

你怎么处理这个问题?

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

小智 54

类加载器相关的问题是一个非常复杂的问题.无论如何,你应该记住一些事实:

  • 应用程序中的类加载器通常不止一个.引导类加载器委托给适当的.实例化新类时,将调用更具体的类加载器.如果它没有找到您正在尝试加载的类的引用,它会委托给它的父类,依此类推,直到您进入引导类加载器.如果他们都没有找到您正在尝试加载的类的引用,则会得到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);
Run Code Online (Sandbox Code Playgroud)

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

  • __ bootstrap类加载器委托给相应的.实例化新类时,将调用更具体的类加载器.如果它没有找到你正在尝试加载的类的引用,它会委托给它的parent__请耐心等待,但这取决于classloader策略,默认为Parent First.换句话说,子类将首先要求其父类加载该类,并且只有在整个层次结构未能加载它时才会加载,不会? (12认同)
  • 否 - 通常在查找类本身之前,类加载器会委托其父级.请参阅类加载器的类javadoc. (5认同)

Tar*_*log 20

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

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


Ale*_*man 6

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


Vin*_*lds 6

类加载器按需加载类.这意味着应用程序和相关库首先需要的类将在其他类之前加载; 加载依赖类的请求通常在依赖类的加载和链接过程中发出.

您可能会遇到LinkageError声明已经遇到过类加载器的重复类定义,通常不会尝试确定应该首先加载哪个类(如果加载器的类路径中存在两个或多个同名的类).有时,类加载器将加载类路径中出现的第一个类并忽略重复的类,但这取决于加载器的实现.

解决此类错误的建议做法是为每组具有冲突依赖关系的库使用单独的类加载器.这样,如果类加载器尝试从库加载类,则依赖类将由无法访问其他库和依赖项的同一类加载器加载.


小智 6

您可以使用URLClassLoaderfor require 从 diff-2 版本的 jar 中加载类:

URLClassLoader loader1 = new URLClassLoader(new URL[] {new File("httpclient-v1.jar").toURL()}, Thread.currentThread().getContextClassLoader());
URLClassLoader loader2 = new URLClassLoader(new URL[] {new File("httpclient-v2.jar").toURL()}, Thread.currentThread().getContextClassLoader());

Class<?> c1 = loader1.loadClass("com.abc.Hello");

Class<?> c2 = loader2.loadClass("com.abc.Hello");

BaseInterface i1 = (BaseInterface) c1.newInstance();

BaseInterface i2 = (BaseInterface) c2.newInstance();
Run Code Online (Sandbox Code Playgroud)