Java类资源与线程资源的区别

9
什么是“difference between”的区别?
getClass().getResource("some-resource-file.txt")

vs

Thread.currentThread().getContextClassLoader().getResource("some-resource-file.txt")

我有一些资源存储在src/test/resources中,我正在尝试从单元测试中访问它们。这是典型的maven风格的目录结构。
我本来期望两者都表现相同。但实际上并不是这样,getClass().getResource()无法获取资源,而使用Thread则可以获取到资源。
那么它们有什么区别呢?

2
你看过上下文类加载器了吗?你的资源在哪里,你的上下文是什么?(Web应用程序?还是其他什么?)我们目前没有足够的信息来帮助你。 - Jon Skeet
更新了问题以提供更多信息。 - Sairam Krish
你是否看过 getClass().getClassLoader()Thread.currentThread().getContextClassLoader() 的细节,就它们返回的对象而言? - Jon Skeet
2个回答

7
假设您正在开发一个库,并且将库Jar放置在Web容器的类路径中。现在假设一个使用此库的Web应用程序已经部署在容器中。
Web应用程序将具有自己的类加载器,使用WEB-INF/classes和WEB-INF/lib/*.jar作为其类路径。而对于发送到您的Web应用程序的每个请求,容器将设置当前线程类加载器为类路径的类加载器。
当您的库代码使用getClass().getResource()时,它将使用用于加载库类的类加载器加载资源。因此,它将使用容器的类加载器,并因此使用库的Jar文件中的资源以及用于启动容器的其他库中的资源。
如果您的库代码改为使用Thread.currentThread().getContextClassLoader()来加载资源,则它将使用与当前线程相关联的类加载器,并因此从Web应用程序的类加载器加载资源,在WEB-INF/classes中查找资源以及在WEB-INF/lib内的Jars中查找资源。
后者可能是您想要的。例如,如果您正在设计一个日志记录库(请不要这样做),则日志记录器将能够为每个Web应用程序读取不同的配置文件,而不是所有Web应用程序共享单个配置。
关于两种方法查找资源的方式,它们最终都会委托给ClassLoader来加载资源。但是,通过Class加载它将把相对路径视为相对于调用类,而通过ClassLoader加载它则需要从包树的根开始指定路径。假设您的类在包com.foo中,则:
 MyClass.class.getResource("hello.txt")

等同于

MyClass.class.getResource("/com/foo/hello.txt")

并等价于

MyClass.class.getClassLoader().getResource("com/foo/hello.txt");

2
有一个特殊情况需要运行第一个类(这就是为什么你必须将main()方法声明为静态的,带有字符串数组作为参数)。一旦该类已加载并正在运行,未来尝试加载类的操作由类加载器完成。 简单来说,类加载器创建了一个扁平的名称空间,其中包含由字符串名称引用的类主体。在Java中每个类都使用自己的类加载器来加载其他类。因此,如果ClassA.class引用ClassB.class,则ClassB需要在ClassLoader of ClassA或其父级的类路径上。
线程上下文类加载器是一个特殊的类加载器,它是当前运行线程的当前类加载器。这在多类加载器环境中很有用。可以从ClassLoader C中创建一个对象,然后将其传递给ClassLoader D拥有的线程。在这种情况下,如果对象想要加载在其自己的类加载器上不可用的资源,则需要直接使用Thread.currentThread().getContextClassLoader()。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接