简体   繁体   English

自定义类加载器在斜杠中使用名称ENDING的getResources时出现问题

[英]Custom classloader trouble with getResources for names ENDING in slash

I am desperate for help but was unable to find anything on the web about this particular subject (many related ones that leave my particular problem unanswered). 我迫不及待地寻求帮助,但无法在网上找到关于这个特定主题的任何内容(许多相关的问题都没有得到解答)。

Specifically, I need to be able to download code (jars) from a central and external code repository. 具体来说,我需要能够从中央和外部代码库下载代码(jar)。 This is done by the bootstrap code that needs to add this to the classpath of a class loader to be used thereafter. 这是由引导代码完成的,需要将其添加到类加载器的类路径中以便以后使用。 This is when we enter the subject that has been discussed so many times. 这是我们进入已经讨论了很多次的主题。 I don't like hacks, so I tried the following: 我不喜欢黑客,所以我尝试了以下方法:

Attempt #1: Create an instance of URLClassLoader configured for this purpose, then invoke the "rest" of the code through it. 尝试#1:创建为此目的配置的URLClassLoader实例,然后通过它调用代码的“rest”。

Failure: There are 1.5 problems here (one may be the cause of another). 失败:这里有1.5个问题(一个可能是另一个问题)。 One is that URLClassLoader, normally, prefers to load stuff from its parent. 一个是URLClassLoader通常更喜欢从其父级加载东西。 Some code has to exist in both, possibly different versions. 一些代码必须存在于两者中,可能存在不同的版本。 If the code from the parent is used, it continues using the "outer" class loader for the rest of loading, which is not what we want, even when the initial loading is OK. 如果使用来自父代码的代码,它继续使用“外部”类加载器进行剩余的加载,这不是我们想要的,即使初始加载是正常的。 Secondly, some third party libraries seem to access the system class loader directly, either by design or accidentally (may get it from one of the classes loaded by it). 其次,一些第三方库似乎直接访问系统类加载器,无论是设计还是意外(可以从其加载的类中获取它)。

Attempt #2: Create my subclass of the URLClassLoader that prefers self over the parent. 尝试#2:创建URLClassLoader的子类,它更喜欢self而不是父级。 Overrode loadClass, getResource, getResources, getPackage, getPackages... later other methods too to make sure of this. 覆盖loadClass,getResource,getResources,getPackage,getPackages ......以后的其他方法也可以确保这一点。

Failure: Didn't help (enough). 失败:没有帮助(足够)。 That third party code still couldn't load some resources . 该第三方代码仍然无法加载某些资源

Attempt #3 Create another custom subclass of the URLClassLoader and set it as the system class loader using -Djava.system.class.loader=... 尝试#3创建URLClassLoader的另一个自定义子类,并使用-Djava.system.class.loader = ...将其设置为系统类加载器

Failure: This worked better - went further, but still failed trying to get resources. 失败:这样做得更好 - 走得更远,但仍未能尝试获取资源。 This time it was different resources, though. 但这次是不同的资源。 I added logging to all the overridden methods to log their calls and resource names. 我添加了日志记录到所有重写的方法来记录他们的调用和资源名称。 Regular resources were MOSTLY found. 经常找到资源。 Some still weren't, even though they are there (confirmed). 有些人仍然没有,即使他们在那里(已确认)。 But something I don't know about even though I tried hard to learn is about many calls with resource names that end with a slash. 但是,即使我努力学习,我还不知道的是有很多关于资源名称以斜线结尾的调用。 Some also have slashes where a dollar sign would normally appear (nested/inner class resources). 有些也有斜线,通常会出现美元符号(嵌套/内部类资源)。 Some examples that were requested but NOT found: 一些已请求但未找到的示例:

com/acme/foo/bar/ClassName/ com/acme/foo/bar/ClassName/InnerClassName/ com / acme / foo / bar / ClassName / com / acme / foo / bar / ClassName / InnerClassName /

When I run the downloaded code with all content on the initial/boot classpath (and do not use my classloader), everything works fine - thus my class loader breaks things, but I need it to work. 当我在初始/ boot类路径上运行所有内容的下载代码(并且不使用我的类加载器)时,一切正常 - 因此我的类加载器会破坏一些东西,但我需要它才能工作。

My closest guesses are: 我最接近的猜测是:

  1. Third party code gets hold of the true system class loader somehow, perhaps via some class that was loaded by it, then uses that. 第三方代码以某种方式获取真正的系统类加载器,可能通过它加载的某个类,然后使用它。 I don't see requests to it and they are bound to fail because it does not have the entire class path. 我没有看到它的请求,它们必然会失败,因为它没有整个类路径。

  2. This business with resource names ending in slashes is the cause by being supported by the true system class loader but not by the URLClassLoader I am subclassing. 资源名称以斜杠结尾的业务是真正的系统类加载器支持的原因,而不是我子类化的URLClassLoader支持的原因。 I can only guess that the expected return URL somehow locates the collection of resources with that name as prefix. 我只能猜测,预期的返回URL会以某种方式定位具有该名称作为前缀的资源集合。 That would be tough to match, although possible. 虽然可能,但这很难匹配。 Furthermore, it appears that some slashes are in positions where a dollar sign separating the inner class name should be, ie in the above example (spaces added for clarity): 此外,似乎某些斜杠位于分隔内部类名称的美元符号的位置,即在上面的示例中(为了清楚起见添加了空格):

com/acme/foo/bar/ClassName / InnerClassName/ com / acme / foo / bar / ClassName / InnerClassName /

com/acme/foo/bar/ClassName $ InnerClassName/ com / acme / foo / bar / ClassName $ InnerClassName /

Please note that I cannot rely on hacking the actual system classloader by assuming that it is a subclass of the URLClassLoader and using reflection to call its addURL(URL) method. 请注意,我不能依赖于破解实际的系统类加载器,假设它是URLClassLoader的子类并使用反射来调用其addURL(URL)方法。

Is there a way to make this work? 有没有办法让这项工作? Please help! 请帮忙!

UPDATE UPDATE

I just made an additional attempt. 我只是做了一个额外的尝试。 I created a dummy wrapper classloader (extending ClassLoader, not URLClassLoader) that only logs requests, then passes them on to the parent (public methods) or superclass (protected methods). 我创建了一个虚拟包装器类加载器(扩展ClassLoader,而不是URLClassLoader),它只记录请求,然后将它们传递给父(公共方法)或超类(受保护的方法)。 I set this to be the system class loader and manually added the entire "inner" class path to the actual outer one, then tried to run the code. 我将其设置为系统类加载器并手动将整个“内部”类路径添加到实际的外部路径,然后尝试运行代码。 That works correctly, just as it does without the custom system class loader. 这是正常的,就像没有自定义系统类加载器一样。 What was logged also identified that even the system class loader return null for these resources ending in slashes for MOST of them, but not all. 记录的内容还发现,即使是系统类加载器也会为这些资源返回null,这些资源以MOST结尾的斜杠结尾,但不是全部。 I did not check whether these also work in the my real code but guessing they may - as they were not the stumbling block. 我没有检查这些是否也适用于我的真实代码,但猜测它们可能 - 因为它们不是绊脚石。 Somehow the custom system classloader is still being bypassed. 不知何故,自定义系统类加载器仍然被绕过。 How? 怎么样?

UPDATE 2 更新2

In my custom system class loaders I have let some classes come from the outer/true system class loader, eg those in java.lang. 在我的自定义系统类加载器中,我让一些类来自外部/真正的系统类加载器,例如java.lang中的类。 I am now suspecting that I should not have and that the inner "world" must be completely isolated. 我现在怀疑我不应该拥有内心的“世界”必须完全孤立。 That would make it problematic, though, to communicate with it and all I would have left is reflection... but not sure whether that would even work - ie can there be more than one java.lang.Class and/or java.lang.Object? 但是,如果与它进行通信会产生问题,而我将留下的只是反思...但不确定这是否会起作用 - 即可以有多个java.lang.Class和/或java.lang 。宾语?

Given all constraints this does not appear entirely possible in a rock solid fashion as I wanted it: 考虑到所有限制因素,这似乎并不像我想要的那样以坚如磐石的方式完全实现:

a) Third party libraries may always "misbehave" and get hold of lassloaders they are not supposed to use one way or another. a)第三方图书馆可能总是“行为不端”并抓住他们不应该以这种或那种方式使用的lassloader。 I looked at OneJar as suggested by fge but they have the same issue - they only detect a possibility of it. 我按照fge的建议看了OneJar,但他们有同样的问题 - 他们只检测到它的可能性。

b) There is no way to completely replace/hide the system class loader. b)无法完全替换/隐藏系统类加载器。

c) Casting the system class loader to a URLClassLoader may stop working at any moment. c)将系统类加载器强制转换为URLClassLoader可能随时停止工作。

It seems, you didn't understand the class loader structure. 看来,你不了解类加载器结构。 Within an ordinary JVM of the current version, there are at least three class loaders: 在当前版本的普通JVM中,至少有三个类加载器:

  1. The bootstrap loader which is responsible for loading the core classes. 引导加载程序 ,负责加载核心类。 Since this involves classes like Class and ClassLoader itself, it can't be represented by a ClassLoader instance. 因为这涉及类,如ClassClassLoader本身,它不能由一个表示ClassLoader实例。 All classes whose getClassLoader() returns null were loaded by the bootstrap loader getClassLoader()返回null所有类都由引导加载程序加载
  2. The extension loader. 扩展加载器。 It is responsible for loading classes within the ext/ directory of the JRE. 它负责在JRE的ext/目录中加载类。 Afaik, it may vanish in future versions. Afaik,它可能会在未来的版本中消失。 Its parent loader is the bootstrap loader 它的父加载器是引导加载程序
  3. The application loader. 应用程序加载器。 This is the one which will be returned by ClassLoader.getSystemClassLoader() and which will be used if no other parent was specified . 这是将由ClassLoader.getSystemClassLoader()返回的那个, 如果没有指定其他父级 ,将使用它。 In the current configurations, it's parent is the extension loader, but maybe it will have the bootstrap loader as its direct parent in future versions 在当前配置中,它的父级是扩展加载器,但也许它将在未来版本中将引导加载程序作为其直接父级

The conclusion is, if you want to reload your application's classes without the delegation to the parent loader destroying your effort, you don't need to manipulate the class loader's implementation. 结论是,如果要重新加载应用程序的类而不委托父加载器破坏您的工作,则不需要操作类加载器的实现。 You just have to specify the right parent. 您只需指定正确的父级。 It's as simple as 这很简单

URLClassLoader cl=new URLClassLoader(urls, ClassLoader.getSystemClassLoader().getParent());

That way, the new class loader's parent will be the original application class loader's parent, thus the already loaded application classes are not in the scope of the new loader while everything else works as usual. 这样,新的类加载器的父级将是原始应用程序类加载器的父级,因此已加载的应用程序类不在新加载器的范围内,而其他所有内容都像往常一样工作。

Regarding the resources ending with a slash, they are rather uncommon. 关于以斜杠结尾的资源,它们并不常见。 They may get resolved when they actually refer to a directory but that depends on the protocol of the URL and the actual handler for that protocol. 它们实际引用目录时可能会得到解决,但这取决于URL的协议和该协议的实际处理程序。 Eg it might work for file: URLs but usually doesn't for jar: URLs unless the jar file contains pseudo-entries for directories. 例如,它可能适用于file: URL但通常不适用于jar: URL,除非jar文件包含目录的伪条目。 I've also seen it working for ftp: URLs. 我也看到它适用于ftp: URL。

Another thing to understand is that if one class directly refers to another class, its original defining class loader will be queried, not necessarily the application class loader. 另一件需要理解的是,如果一个类直接引用另一个类,则将查询其原始定义类加载器,而不一定是应用程序类加载器。 Eg when the class java.lang.String contains a reference to java.lang.Object (it does), this reference will be directly resolved using the bootstrap loader as this is the defining loader of java.lang.String . 例如,当java.lang.String类包含对java.lang.Object (它确实)的引用时,将使用引导加载程序直接解析此引用,因为这是java.lang.String的定义加载器。

This implies that if you manipulate the parent lookup of a loader to not follow the standard parent delegation you are risking to resolve names to different runtime classes as the resolving of the same names when being referenced by classes loaded by the parent loader. 这意味着如果您操纵加载器的父查找不遵循标准父委派,则您可能会将名称解析为不同的运行时类,因为在由父加载器加载的类引用时会解析相同的名称。 You avoid such problems by following the standard procedure as in the solution above. 您可以按照上述解决方案中的标准程序避免此类问题。 The JRE classes will never contain references to your application classes and the new loader not having the original application loader as its parent will never interfere with the classes loaded by the original application loader. JRE类永远不会包含对应用程序类的引用,并且新的加载器没有原始的应用程序加载器,因为它的父级将永远不会干扰原始应用程序加载器加载的类。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM