线程的上下文类加载器和普通类加载器的区别

11 浏览
0 Comments

线程的上下文类加载器和普通类加载器的区别

线程的上下文类加载器和普通类加载器有什么区别?

即,如果 Thread.currentThread().getContextClassLoader()getClass().getClassLoader() 返回不同的类加载器对象,哪一个将被使用?

admin 更改状态以发布 2023年5月21日
0
0 Comments

\n\n每个类都使用自己的类加载器来加载其他类。因此,如果 ClassA.class 引用了 ClassB.class,那么 ClassB 需要在 ClassA 的类加载器或其父加载器的类路径上。\n\n线程上下文类加载器是当前线程的类加载器。一个对象可以从 ClassLoaderC 中的类创建,然后传递给 ClassLoaderD 拥有的一个线程。在这种情况下,如果对象想要加载其自己的类加载器上没有的资源,则需要直接使用 Thread.currentThread().getContextClassLoader()

0
0 Comments

这篇文章并不是在回答原本的问题,但由于这个问题在任何关于ContextClassLoader的查询中排名都很高并且被链接,因此我认为回答相关问题:在什么情况下应该使用上下文类加载器,是很重要的。简短的回答是:永远不要使用上下文类加载器!但是当你必须要调用缺失了ClassLoader参数的方法时,将它设置为getClass().getClassLoader()

当一个类的代码请求加载另一个类时,正确的类加载器应该是调用者类相同的类加载器(即getClass().getClassLoader())。99.9%的情况下,这就是事情的工作方式,因为这是JVM自己在你构造一个新类的实例、调用静态方法或访问静态字段时所做的。

当你想要使用反射创建一个类(例如当反序列化或加载一个可配置的命名类时),执行反射的库应该总是询问应用程序使用哪个类加载器,即从应用程序收到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中的类即可),或引导类加载器,还有些使用以上技术的不可预测的组合(这只会使事情更加混乱)。这导致了人们的大量哭泣和牙 gnashing。

在使用这样的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);
}

0