ClassLoader 仅在特定线程中查找资源

Posted

技术标签:

【中文标题】ClassLoader 仅在特定线程中查找资源【英文标题】:ClassLoader finds Resource only in specific Threads 【发布时间】:2013-04-07 19:44:28 【问题描述】:

我正在努力解决 ClassLoader 试图解析仅在某些条件下有效的资源的情况。

用例如下:我将 IBM Rational Functional Tester 与 JBehave 结合用于自动化验收测试。 JBehave 将测试指定为纯文本故事文件。这些故事文件可以引用其他故事文件,即所谓的“Given Stories”。 JBehave 使用ExecutorService 执行可能多线程的故事。虽然 JBehave 加载文本文件(使用 ClassLoader.getResourceAsStream)没有问题,但它无法在从 ExecutorService 启动的线程中找到相同的文件。

实际中的 ClassLoader 是 ContextFinder。在调试应用程序并挂起两个线程时,最初启动 JBehave 的“主线程”和从执行器服务启动以运行故事文件的“故事线程”,我可以确定类加载器的实例是相同的。还有父母的实例等。

但是一个电话

Thread.currentThread().getContextClassLoader().getResource("HelloWorld.story")

在主线程中完美运行,故事线程失败并返回 null。

从 ContextFinder 的源代码来看,除了为堆栈上的类收集所有 ClassLoader 之外,它似乎没有做任何其他事情。所以我尝试了这个:

SomeClass.class.getClass().getClassLoader().getResource("HelloWorld.story")

... 结果相同。

这对我来说太奇怪了。任何用于调试或解释为什么显示此行为的指针都值得赞赏!

【问题讨论】:

【参考方案1】:

线程上下文类加载器 (TCCL) 在 OSGi 中基本上是未定义的。你应该避免使用它。

作为标准 OSGi 的扩展,Equinox 确实提供了这个称为 ContextFinder 的东西,它执行堆栈检查以尝试在调用堆栈中找到最顶层的 OSGi 捆绑类加载器。但是,您几乎无法控制,结果可能出乎您的意料。当然,由于这是 Equinox 特定的扩展,任何依赖 ContextFinder 正常工作的代码都将在所有其他 OSGi 框架上失败。

因此,与其浪费时间尝试调试它,不如根本不使用 TCCL。如果要加载相对于特定类的资源,请从字面量类对象中执行,例如:

MyClass.class.getResource("HelloWorld.story");

更新

我在您原来的问题中注意到了这一点:SomeClass.class.getClass()。其结果将是java.lang.Class 本身的类对象。调用 getClassLoader()总是返回 JVM 引导类加载器......可能不是你想要的!

【讨论】:

谢谢,这为我指明了正确的方向!在 JBehave 中,我能够稍微不同地配置测试的执行。我现在没有使用使用 TCCL 的 LoadFromClasspath 的默认构造函数,而是使用“我的”类实例化 LoadFromClasspath,现在类加载器能够找到所有资源。

以上是关于ClassLoader 仅在特定线程中查找资源的主要内容,如果未能解决你的问题,请参考以下文章

REST - 仅在集合中获取资源的特定属性

Netty classloader死锁问题及解决方案分享

java的ClassLoader的getResources方法怎么使用

如何仅在该特定组内按结果查找组中未排序的一组行?

两种类别的类加载器

Java中getResourceAsStream的用法