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

发布于 2024-11-09 18:20:37 字数 532 浏览 4 评论 0原文

我知道对于经验丰富的程序员来说这可能是一个愚蠢的问题。但我有一个库(一个 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,还是任意混合类?因此,例如,如果从 Version-1.jar 加载一个类,那么从同一个类加载器加载的所有其他类都会进入同一个 jar 吗?

你如何处理这个问题?

是否有一些技巧可以以某种方式将罐子“合并”到“required.jar”中,以便类加载器将它们视为“一个单元/包”,或者以某种方式链接?

I know this may be a silly question for experienced coders. But I have a library (an http client) that some of the other frameworks/jars used in my project require. But all of them require different major versions like:

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

Is the classloader intelligent enough to seperate them somehow? Most likely not? How does the Classloader handle this, in case a Class is the same in all three jars. Which one is loaded and why?

Does the Classloader only pickup exactly one jar or does it mix classes arbitrarily? So for example if a class is loaded from Version-1.jar, all other classes loaded from the same classloader will all go into the same jar?

How do you handle this problem?

Is there some trick to somehow "incorporate" the jars into the "required.jar" so that the are seen as "one unit/package" by the Classloader, or somehow linked?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

祁梦 2024-11-16 18:20:37

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

  • 应用程序中的类加载器通常不止一个。引导类加载器委托给适当的。当您实例化一个新类时,会调用更具体的类加载器。如果它找不到对您尝试加载的类的引用,它将委托给其父级,依此类推,直到您到达引导类加载器。如果它们都找不到对您尝试加载的类的引用,则会出现 ClassNotFoundException。

  • 如果您有两个具有相同二进制名称的类,可由同一个类加载器搜索,并且您想知道正在加载其中哪一个,则只能检查特定类加载器尝试解析类名的方式。< /p>

  • 根据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);

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

Classloader related problems are a quite complex matter.
You should in any case keep in mind some facts:

  • Classloaders in an application are usually more than a single one. The bootstrap class loader delegates to the appropriate. When you instantiate a new class the more specific classloader is invoked. If it does not find a reference to the class you are trying to load, it delegates to its parent, and so on, until you get to the bootstrap class loader. If none of them find a reference to the class you are trying to load you get a ClassNotFoundException.

  • If you have two classes with the same binary name, searchable by the same classloader, and you want to know which one of them you are loading, you can only inspect the way that specific classloader tries to resolve a class name.

  • According to the java language specification, there is not a uniqueness constraint for a class binary name, but as far as I can see, it should be unique for each classloader.

I can figure out a way to load two classes with the same binary name, and it involves to have them loaded (and all their dependencies) by two different classloaders overriding default behaviour.
A rough example:

    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);

I always found classloader customization a tricky task. I'd rather suggest to avoid multiple
incompatible dependencies if possible.

时光清浅 2024-11-16 18:20:37

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

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

Each classload picks exactly one class. Usually the first one found.

OSGi aims to solve the problem of multiple versions of the same jar. Equinox and Apache Felix are the common open-source implementations for OSGi.

内心激荡 2024-11-16 18:20:37

类加载器将首先从恰好位于类路径中的 jar 中加载类。
通常,不兼容的库版本会有包差异,
但在不太可能的情况下,它们确实不兼容并且无法用一个替代 - 尝试 jarjar。

Classloader will load classes from the jar that happened to be in the classpath first.
Normally, incompatible versions of library will have difference in packages,
But in unlikely case they really incompatible and can't be replaced with one - try jarjar.

美人迟暮 2024-11-16 18:20:37

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

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

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

Classloaders load class on demand. This means that the class required first by your application and related libraries would be loaded before other classes; the request to load the dependent classes is typically issued during the loading and linking process of a depending class.

You are likely to encounter LinkageErrors stating that duplicate class definitions have been encountered for classloaders typically do not attempt to determine which class should be loaded first (if there are two or more classes of the same name present in the classpath of the loader). Sometimes, the classloader will load the first class occurring in the classpath and ignore the duplicate classes, but this depends on the implementation of the loader.

The recommended practice to resolve such kind of errors is to utilize a separate classloader for each set of libraries that have conflicting dependencies. That way, if a classloader attempts to load classes from a library, the dependent classes would be loaded by the same classloader that does not have access to the other libraries and dependencies.

尽揽少女心 2024-11-16 18:20:37

您可以使用 URLClassLoader for 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();

You can use the URLClassLoader for require to load the classes from a diff-2 version of jars:

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();
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文