Java,Classpath,Classloading =>同一个jar /项目的多个版本
我知道这对于有经验的编程人员来说可能是一个愚蠢的问题。 但我有一个库(一个HTTP客户端),我的项目中使用的一些其他框架/jar子需要。 但他们都需要不同的主要版本,如:
httpclient-v1.jar => Required by cralwer.jar httpclient-v2.jar => Required by restapi.jar httpclient-v3.jar => required by foobar.jar
类加载器是否足够聪明,以某种方式将它们分开? 最有可能不? Classloader如何处理这个问题,以防三个jar子中的Class是相同的。 哪一个被加载,为什么?
Classloader只拾取一个jar子,还是混合任意的类? 例如,如果一个类是从Version-1.jar加载的,那么从同一个类加载器加载的所有其他类将全部进入相同的jar?
你如何处理这个问题?
有没有什么把“jar子”合并到“required.jar”中的一些技巧,这样就可以被Classloader
看作是“一个单元/包”,或者以某种方式被链接?
类加载器相关的问题是一个相当复杂的问题。 无论如何,你应该记住一些事实:
-
应用程序中的类加载器通常不止一个。 引导类加载器委托给相应的。 当你实例化一个新的类时,更具体的类加载器被调用。 如果它没有find你要加载的类的引用,那么它将委托给它的父类,等等,直到你到达引导类加载器。 如果他们中没有一个find您正在尝试加载的类的引用,则会得到一个ClassNotFoundException。
-
如果您有两个具有相同二进制名称的类,可以使用相同的类加载器进行search,并且想知道您正在加载哪个类,那么您只能检查特定类加载器尝试parsing类名的方式。
-
根据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);
我总是发现类加载器定制是一个棘手的任务。 如果可能,我宁愿build议避免多个不兼容的依赖关系。
每个classload只select一个类。 通常find第一个。
OSGi旨在解决同一个jar子的多个版本的问题。 Equinox和Apache Felix是OSGi的常见开源实现。
类加载器将首先从恰好在classpath中的jar中加载类。 通常情况下,不兼容的版本库会有不同的包,但是不可能的情况下它们真的不兼容,不能用一个jarjar来代替。
Classloaders按需加载类。 这意味着您的应用程序和相关库首先需要的类将在其他类之前加载; 加载依赖类的请求通常在依赖类的加载和链接过程中发出。
您可能会遇到LinkageError
指出类加载器遇到重复的类定义通常不会尝试确定哪个类应该先加载(如果加载器的类path中存在两个或更多同名类)。 有时,类加载器将加载类path中出现的第一个类,并忽略重复的类,但这取决于加载器的实现。
解决此类错误的build议做法是对每个具有冲突依赖关系的库集合使用单独的类加载器。 这样,如果一个类加载器试图从一个库中加载类,那么相关的类将被相同的类加载器加载,而这个类加载器不能访问其他库和依赖。