【问题标题】:URLClassLoader.getResources("") (empty resource name) not giving roots of jarsURLClassLoader.getResources("") (空资源名称)不给出罐子的根
【发布时间】:2014-03-17 05:07:33
【问题描述】:

考虑使用URLs 集合参数化的URLClassLoader,这是扩展目录和jar 文件的混合。例如:

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 中的所有匹配项。

getResources("") 中传递的特殊空字符串名称应该为所有可用根(在目录和 jar 中)生成 URL。但是,ClassLoaders 中存在一个已知限制,即只返回与目录对应的根。 jar 的所有根都被丢弃。

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

我目前有一个有效的修复程序,我从 URLClassLoader 扩展并手动处理带有空字符串的请求,除了 URLs 返回的集合中的目录之外,还强制 jar 的根。

但是我的问题是:

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

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

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

感谢您对此的任何想法!

【问题讨论】:

  • “在 getResources("") 中传递的特殊空字符串名称应该产生所有可用根的 URL” - 真的吗?我在ClassLoader 或URLClassLoader 的文档中没有看到这一点。您在哪里看到它记录在案?
  • 我同意,它似乎没有被声明为“特殊”,但它经常被这样使用。提到的忽略罐子的行为是一个事实,例如在Spring docs 中(参见其他说明:警告:部分)。
  • getResources 不适用于目录的原因是 jar 文件中没有目录条目。当有条目时,它确实有效(与this answer 比较)。 JDK 的jar 命令不会创建目录条目(META-INF 除外,这就是为什么getResources("META-INF") 确实适用于 jar 文件[但不适用于默认文件系统,该目录通常不存在])。

标签: java url jar classloader


【解决方案1】:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-09-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-26
    相关资源
    最近更新 更多