线程上下文类加载器和普通类加载器有什么区别?
也就是说,如果Thread.currentThread().getContextClassLoader()
和getClass().getClassLoader()
返回不同的类加载器对象,哪一个会被使用?
线程上下文类加载器和普通类加载器有什么区别?
也就是说,如果Thread.currentThread().getContextClassLoader()
和getClass().getClassLoader()
返回不同的类加载器对象,哪一个会被使用?
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社区。ContextClassLoader
(当您在共享线程池上运行不同的应用程序或将ContextClassLoader
保留为空时会失败),有些遍历堆栈(当类的直接调用者本身是库时将失败),有些使用系统类加载器(这很好,只要它被记录为仅使用CLASSPATH
中的类)或引导类加载器,有些则使用上述技术的不可预测组合(这只会使事情更加混乱)。这导致了许多人哭泣和咬牙切齿。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);
}
ClassA.class
引用了 ClassB.class
,那么 ClassB
需要在 ClassA
的类加载器或其父类加载器的类路径中。ClassLoaderC
中的一个类创建,并传递给由 ClassLoaderD
拥有的线程。在这种情况下,如果对象想要加载不在它自己的类加载器中的资源,则需要直接使用 Thread.currentThread().getContextClassLoader()
。ClassB
必须在ClassA
的加载器(或者ClassA
的加载器的父级)的类路径上?难道ClassA
的加载器不能重写loadClass()
,这样即使ClassB
不在它的类路径上,它也可以成功地加载ClassB
吗? - Pacerierbootstrap
类加载器设置为上下文类加载器,而是将子级 system
类路径类加载器设置为正在设置的 Thread
。然后,JNDI
类确保使用 Thread.currentThread().getContextClassLoader()
来加载类路径上可用的 JNDI 实现类。 - Ravi K Thapliyal在 @David Roussel 的回答中,需要补充的是,类可以由多个类加载器加载。
让我们了解一下 class loader 的工作原理。
引导类加载器 负责从 rt.jar 中加载标准 JDK 类文件,并且它是 Java 中所有类加载器的父级。引导类加载器没有任何父级。
扩展类加载器 将类加载请求委派给其父级引导类加载器,如果不成功,则从 jre/lib/ext 目录或 java.ext.dirs 系统属性指定的任何其他目录加载类。
系统或应用程序类加载器 负责从 CLASSPATH 环境变量、-classpath 或 -cp 命令行选项、JAR 内部 Manifest 文件中的 Class-Path 属性加载应用程序特定的类。
应用程序类加载器 是 扩展类加载器 的子级,由
sun.misc.Launcher$AppClassLoader
类实现。
ClassLoader
遵循三个原则。
getClass().getClassLoader()
和ThisClass.class.getClassLoader()
不一定相同,除非ThisClass
是final
或者您知道子类不存在。请留意这一点。 - Jesse Glick