URLClassLoader.getResources(“")(空资源名称)没有给出 jars 的根 [英] URLClassLoader.getResources("") (empty resource name) not giving roots of jars

查看:40
本文介绍了URLClassLoader.getResources(“")(空资源名称)没有给出 jars 的根的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑一个 URLClassLoader 参数化的 URL 集合,它是扩展目录和 jar 文件的混合.例如:

Consider a URLClassLoader parameterized with a collection of URLs which is a mix of expanded directories and jar files. For example:

URL[] urls = new URL[] {
    new URL("file:/D:/work/temp/jars/spring-security-core-3.2.0.RELEASE.jar"),
    new URL("file:/D:/work/temp/jars/spring-security-config-3.2.0.RELEASE.jar"),
    ...
    new URL("file:/D:/work/temp/domain/bin/"),
    new URL("file:/D:/work/temp/web/bin/"),
    ...
}
URLClassLoader cl = new URLClassLoader(urls);

类加载器正确处理 getResources() 对位于包内某处的资源的请求,例如 "org/my/package/conf.properties".正确处理是指类加载器成功地在目录和 jar 中找到所有匹配项.

The classloader correctly handles getResources() requests for resources located somewhere inside a package like "org/my/package/conf.properties". By correctly handles I mean the classloader successfully finds all matches inside both directories and jars.

getResources("") 中传递的特殊空字符串名称应该产生所有可用根(在目录和 jar 中)的 URL.然而,ClassLoaders 有一个已知的限制,它导致只返回对应于目录的根.所有罐子的根都被丢弃.

A special empty string name passed in getResources("") is supposed to yield the URLs for all available roots (in both the directories and the jars). However there is a known limitation in ClassLoaders which results in only returning roots that correspond to directories. All roots to jars are discarded.

使用 classloader.getURLs[] 而不是 classloader.getResources("") 对我不起作用,因为我有一个复杂的相互依赖的图 URLClassLoaders,所以结果会完全不同.此外,我的类加载器将由第三方类路径扫描工具使用,该工具使用 getResources("") 调用以设置内部搜索库.这样,根本找不到位于 jars 中的资源.

Using classloader.getURLs[] instead of classloader.getResources("") will not work with me as I have a complex graph of interdependent URLClassLoaders, so the results are going to be completely different. Also my classloaders are to be consumed by a third party classpath scanning facilities that uses getResources("") calls in order to set up an internal search base. This way resources located in jars are simply not found.

我目前有一个工作修复程序,我从 URLClassLoader 扩展并手动处理带有空字符串的请求,方法是除了 URL<返回集合中的目录之外,还强制 jar 的根目录/code>s.

I currently have a working fix where I extend from URLClassLoader and manually handle requests with an empty string by forcing roots for jars in addition to those for directories within the returned collection of URLs.

不过我的问题是:

  • 此限制的概念/技术原因是什么(不返回 jar 的路径)?

  • What was the conceptual/technical reason for this limitation (where paths to jars are not returned)?

通过手动修复此问题,我是否违反了任何重要合同?

By fixing this manually, do I violate any important contract?

有什么好的方法可以得到想要的行为吗?

Is there any nice way to get the desired behavior?

感谢您对此的任何想法!

Thanks for any thoughts on that!

推荐答案

此限制的概念/技术原因是什么(不返回 jar 的路径)?

ClassLoader.getResources("") 的行为未指定.

URLClassPath$Loader 完全基于 URL.它通过将资源名称添加到目录的基本 URL 来构造一个新的文件 URL并在指向现有资源时返回 URL.对空资源名称没有特殊处理.这是否是通缉行为没有记录.

The implementation for loading resources from the file system in URLClassPath$Loader is solely based on URLs. It constructs a new file URL by adding the resource name to the base URL of the directory and returns the URL when it points to an existing resource. There's no special handling for an empty resource name. Whether this is wanted behavior or not is undocumented.

URLClassPath$JarLoader 处理 JAR 文件的索引.要获得 JAR 文件的相同行为,实现需要对空资源名称进行特殊处理,即它需要首先检查空资源名称并返回 JAR 文件的文件 URL,而不是在索引中搜索.该实现没有对 empy 资源名称进行特殊处理.这是否是通缉行为再次没有记录.

The implementation for JAR files in URLClassPath$JarLoader works on an index over JAR files. To get the same behaviour for JAR files the implementation would require a special handling for empty resource names, i.e. it would need to check for an empty resource name first and return the file URL of the JAR file instead of searching within the index. The implementation does not have a special handling for empy resource names. Whether this is wanted behavior or not is again undocumented.

由于 API 规范没有指定行为空资源名称两个实现都是有效的.

Since the API specification does not specify the behavior for empty resource names both implementations are valid.

有些人可能会争辩说,暴露根目录是一个安全问题,尤其是在沙箱中运行时.其他人可能会争辩说 getResources() 应该为空资源返回 null,因为实际上不存在名为"的资源.

Some may argue that exposing roots is a security issues, especially when running in a sandbox. Others may argue that getResources() should return null for empty resources since there actually does not exist a resource with the name "".

在任何情况下,URLClassLoader 的当前行为都会导致 Class.getResource() 中的意外行为.当使用默认包中的类的空字符串调用此方法时,它会在从文件系统加载类时返回该类的根目录.这违反了方法的约定.有关详细信息,请参见例如这个开放的 Java 错误:https://bugs.openjdk.java.net/browse/JDK-8202687.

In any case current behavior of URLClassLoader leads to unexpected behavior in Class.getResource(). When this method is called with an empty string for a class in the default package it returns the root directory of the class when the class was loaded from the file system. This violates the contract of the method. For details see for example this open Java bug: https://bugs.openjdk.java.net/browse/JDK-8202687.

通过手动修复此问题,我是否违反了任何重要合同?

只要你只覆盖了 findResource() 方法的 ClassLoader,调用 super 方法,然后添加额外的您不应违反任何合同的 JAR 文件的网址.

As long as you only override the findResource() method of your ClassLoader, call the super method and then add the additional URLs of your JAR files you shouldn't violate any contract.

但请注意,已经有一些实现对 URLClassLoaders 进行了特殊处理.例如Spring 的 PathMatchingResourcePatternResolver 有一个特殊的处理(here) 用于作为 URLClassLoader 实例的类加载器,它为 JAR 添加额外的 URL.

But be aware that there are already implementations out there, that have a special handling for URLClassLoaders. For example Spring's PathMatchingResourcePatternResolver has a special handling (here) for class loaders that are instances of URLClassLoader, which adds additional URLs for JARs.

有什么好的方法可以得到想要的行为吗?

没有很好的方法来获得所需的行为,因为每个解决方案都基于未指定的行为,理论上可能会随着每个新的 JRE 版本而改变.

There is no nice way to get the desired behavior since every solution would be based on unspecified behavior that may theoretically change with every new JRE version.

随着 Java 9 中多版本 JAR 文件的引入,行为已经改变:

With the introduction of multi-release JAR files in Java 9 the behavior already changed:

对于包含 Java 8 类和 Java 9 类的多版本 JAR 文件ClassLoader.getResource("") 现在返回 JAR 文件在 JRE 版本中执行时的 URL >8. 对于 JRE 8,它仍然不返回相同 JAR 文件的 URL.有了它,空资源字符串的返回 URL 现在甚至取决于 JRE 版本,resp.JAR 文件的类型.

For a multi-release JAR file with Java 8 classes and Java 9 classes ClassLoader.getResource("") returns now an URL for the JAR file when it is executed within a JRE version > 8. With JRE 8 it still returns no URL for the same JAR file. With it, the returned URLs for an empty resource string depend now even on the JRE version, resp. the type of JAR file.

存在获取 JAR 文件 URL 的变通方法.PathMachintResourcePatternResolver 例如从 java.class.path 系统属性加载 JAR 文件名(在系统类加载器的情况下)并通过调用 URLClassLoader.getURLs()(如果是 URLClassLoader).但同样,这些只是基于未指定行为的解决方法.

There exist workarounds to get also the URLs for JAR files. PathMachintResourcePatternResolver for example loads JAR file names from the java.class.path system property (in case of the system class loader) and loads additional URLs by calling URLClassLoader.getURLs() (in case of a URLClassLoader). But again, these are only workarounds based on unspecified behavior.

理想情况下,类路径上的搜索仅在 java 包的上下文中执行.像 Spring (boot) 这样的框架只在 java 包的上下文中对类路径执行搜索.这避免了依赖类加载器的未指定行为,也避免在不相关的第三方库的 JAR 文件中进行搜索.因此,我建议尽可能在 java 包的上下文中搜索类路径,而不是使用空资源名称搜索资源.

Ideally searches on the classpath are only performed in the context of a java package. Frameworks like Spring (boot) perform searches on the classpath only in the context of a java package. This avoids to rely on unspecified behavior of class loaders and also avoids to search in JAR files of irrelevant third-party libraries. So, whenever possible I recommend to search on the classpath in context of a java package instead of searching resources by using an empty resource name.

这篇关于URLClassLoader.getResources(“")(空资源名称)没有给出 jars 的根的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆