线程的上下文类加载器和普通类加载器有什么区别?
也就是说,如果Thread.currentThread().getContextClassLoader()
和getClass().getClassLoader()
返回不同的类加载器对象,会使用哪一个?
最佳答案
这并没有回答原来的问题,但是由于这个问题的排名很高并且与任何 ContextClassLoader
查询都有关联,所以我认为回答上下文类加载器应该何时加载的相关问题很重要用过的。简短回答:永远不要使用上下文类加载器!但当您必须调用缺少 ClassLoader
参数的方法时,将其设置为 getClass().getClassLoader()
。
当来自一个类的代码要求加载另一个类时,要使用的正确类加载器是与调用者类相同的类加载器(即,getClass().getClassLoader()
)。 99.9% 的时间都是这样,因为 this is what the JVM does itself第一次构造新类的实例、调用静态方法或访问静态字段时。
当您想使用反射创建类时(例如反序列化或加载可配置的命名类时),执行反射的库应该总是询问应用程序要使用哪个类加载器,通过从应用程序接收 ClassLoader
作为参数。应用程序(它知道所有需要构造的类)应该传递给它 getClass().getClassLoader()
。
获取类加载器的任何其他方式都是不正确的。如果图书馆使用诸如 Thread.getContextClassLoader()
之类的黑客手段, sun.misc.VM.latestUserDefinedLoader()
, 或 sun.reflect.Reflection.getCallerClass()
这是一个由 API 缺陷引起的错误。基本上,Thread.getContextClassLoader()
的存在只是因为设计 ObjectInputStream
API 的人忘记接受 ClassLoader
作为参数,这个错误一直困扰着Java 社区至今。
就是说,许多 JDK 类使用一些技巧之一来猜测要使用的类加载器。有些使用 ContextClassLoader
(当您在共享线程池上运行不同的应用程序时失败,或者当您离开 ContextClassLoader null
时失败),有些遍历堆栈(当类的直接调用者本身就是一个库),一些使用系统类加载器(这很好,只要记录为仅使用 CLASSPATH
中的类)或引导类加载器,还有一些使用上述技术的不可预知的组合(这只会让事情变得更加困惑)。这导致许多人哭泣和咬牙切齿。
当使用这样的 API 时,首先,尝试找到接受类加载器作为参数的方法的重载。如果没有明智的方法,则尝试在 API 调用之前设置 ContextClassLoader
(并在之后重置它):
ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
// call some API that uses reflection without taking ClassLoader param
} finally {
Thread.currentThread().setContextClassLoader(originalClassLoader);
}
关于java - 线程上下文类加载器和普通类加载器的区别,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18108711/