【问题标题】:Groovy resource from classpath not loaded未加载类路径中的 Groovy 资源
【发布时间】:2021-07-28 17:27:14
【问题描述】:

大家好。

我在我的 groovy 项目(IDE - Intellij Idea 12.6)中使用 spock 框架进行测试。我的 spock 规范类将文件名传递给 groovy 对象进行处理(该文件肯定在类路径中),但是当我尝试以这种方式获取该文件时

    def resource = getClass().getClassloader().getResourceAsStream(filepath)
    assert resource != null : "No input stream found for path ${filepath}"
    def rootNode = new XmlParser().parse(resource)

然后resource == null

我尝试调试并在表达式评估窗口中此代码 getClass().getResource(fileName) 返回资源。

我试图检查在第一种情况下(在类中使用代码)和在第二种情况下(表达式评估窗口)使用了哪个类加载器。

在第一种情况下类加载器是sun.misc.Launcher$AppClassLoader@18dabf1,但在表达式评估窗口中类加载器是groovy.lang.GroovyClassLoader$InnerLoader@1e69757 我想这就是我的资源为空的原因。

有人可以指导我做错了什么以及如何加载该资源文件吗?

更新:

更改了解析资源文件的方式。当filepath - 文件的完整路径时,这可行,但如果filepath 只是文件名并且该文件在类路径中,那么resource == null

UPDATE2

更改资源文件的加载方式,清理依赖项,一切正常,我想昨天不是我的一天。

【问题讨论】:

  • 仅当资源与getClass() 引用的类在同一个包中时,并且仅在使用Class#getResourceStream(而不是ClassLoader#getResourceStream)时才有效。

标签: groovy resources classloader


【解决方案1】:

这个问题很可能与 Spock 无关。从远处很难说是什么原因造成的,但读取资源的最安全方法是 getClass().getClassLoader().getResourceAsStream()Thread.currentThread().getContextClassLoader().getResourceAsStream(),具体取决于环境。

不确定当您执行 new File(resource) 时 Groovy 会做什么,因为没有 File(URL) 构造函数(只有 File(URI) 构造函数)。在任何情况下,都应尽可能避免从类路径中获取File

【讨论】:

  • 正在尝试 getClass().getClassLoader().getResourceAsStream() 和 Thread.currentThread().getContextClassLoader().getResourceAsStream() 方法,结果是相同的资源为空。你能详细说明为什么应该避免从类路径获取文件吗?在我的情况下,我需要读取 xml 文件然后处理它
  • 请注意,这些方法所期望的参数与 getClass().getResource() 所期望的不同(查看文档)。有关从类路径获取File 时可能遇到的问题,请参阅weblogs.java.net/blog/kohsuke/archive/2007/04/…(评论和主要文章)。对于处理 XML 文件,InputStream 通常可以正常工作。
  • 是的,我想 Spock 与这个问题无关,一定是类加载器的问题......
  • 您好,我认为解决方案是正确的。尝试类似的方法:URI filePath = this.class.getResource('/path_in_classpath').toURI() File file = new File(filePath)
【解决方案2】:

这可能是因为 Groovy 可能会以与您认为正在发生的情况不同的方式解释对象的类。请参阅以下其他 StackOverflow 项:

Why does groovy .class return a different value than .getClass()

当类错误时,ClassLoader 很可能会被引导加载器和 getClassLoader 返回 null。

所以不要使用像

这样的语句
def resource = getClass().getClassloader().getResourceAsStream(filepath)

使用像

这样的语句来指定实际的类
def resource = MyClass.class.getClassLoader().getResourceAsStream(filePath)

在几乎相同的情况下为我工作。

【讨论】:

    【解决方案3】:
    def resource = MyClass.class.getResourceAsStream(fileName)
    

    或者如果您希望文件的内容为字符串:

    def str = new String(MyClass.class.getResourceAsStream(fileName).readAllBytes(), StandardCharsets.UTF_8)
    

    请注意:

    1. 使用的是MyClass,而不是this.getClass();
    2. 在资源中,你必须创建与你的类的包相同的目录结构,并将文件放在那里;
    3. fileName 只是简单的文件名,没有任何路径;
    4. 您必须清理并重建您的项目。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-03-27
      • 1970-01-01
      • 1970-01-01
      • 2011-04-22
      • 2010-10-26
      • 2020-09-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多