Java 线程上下文类加载器和普通类加载器的区别
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1771679/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Difference between thread's context class loader and normal classloader
提问by abracadabra
What is the difference between a thread's context class loader and a normal class loader?
线程的上下文类加载器和普通的类加载器有什么区别?
That is, if Thread.currentThread().getContextClassLoader()
and getClass().getClassLoader()
return different class loader objects, which one will be used?
也就是说,如果Thread.currentThread().getContextClassLoader()
和getClass().getClassLoader()
返回不同的类加载器对象,会使用哪一个?
采纳答案by David Roussel
Each class will use its own classloader to load other classes. So if ClassA.class
references ClassB.class
then ClassB
needs to be on the classpath of the classloader of ClassA
, or its parents.
每个类将使用自己的类加载器来加载其他类。所以,如果ClassA.class
引用ClassB.class
则ClassB
需要上的类加载器的类路径ClassA
,或者它的父母。
The thread context classloader is the current classloader for the current thread. An object can be created from a class in ClassLoaderC
and then passed to a thread owned by ClassLoaderD
. In this case the object needs to use Thread.currentThread().getContextClassLoader()
directly if it wants to load resources that are not available on its own classloader.
线程上下文类加载器是当前线程的当前类加载器。可以从 in 的类创建对象ClassLoaderC
,然后将其传递给ClassLoaderD
. 在这种情况下,Thread.currentThread().getContextClassLoader()
如果对象要加载其自己的类加载器上不可用的资源,则需要直接使用。
回答by Timour
There is an article on javaworld.com that explains the difference => Which ClassLoader should you use
javaworld.com 上有一篇文章解释了区别=>你应该使用哪个类加载器
(1)
(1)
Thread context classloaders provide a back door around the classloading delegation scheme.
Take JNDI for instance: its guts are implemented by bootstrap classes in rt.jar (starting with J2SE 1.3), but these core JNDI classes may load JNDI providers implemented by independent vendors and potentially deployed in the application's -classpath. This scenario calls for a parent classloader (the primordial one in this case) to load a class visible to one of its child classloaders (the system one, for example). Normal J2SE delegation does not work, and the workaround is to make the core JNDI classes use thread context loaders, thus effectively "tunneling" through the classloader hierarchy in the direction opposite to the proper delegation.
线程上下文类加载器为类加载委托方案提供了一个后门。
以 JNDI 为例:它的内脏由 rt.jar 中的引导类实现(从 J2SE 1.3 开始),但这些核心 JNDI 类可能加载由独立供应商实现的 JNDI 提供程序,并可能部署在应用程序的 -classpath 中。这种情况要求父类加载器(在本例中为原始类加载器)加载对其子类加载器之一(例如系统类加载器)可见的类。正常的 J2SE 委派不起作用,解决方法是让核心 JNDI 类使用线程上下文加载器,从而在与正确委派相反的方向上有效地通过类加载器层次结构“隧道化”。
(2) from the same source:
(2) 来自同一来源:
This confusion will probably stay with Java for some time. Take any J2SE API with dynamic resource loading of any kind and try to guess which loading strategy it uses. Here is a sampling:
- JNDI uses context classloaders
- Class.getResource() and Class.forName() use the current classloader
- JAXP uses context classloaders (as of J2SE 1.4)
- java.util.ResourceBundle uses the caller's current classloader
- URL protocol handlers specified via java.protocol.handler.pkgs system property are looked up in the bootstrap and system classloaders only
- Java Serialization API uses the caller's current classloader by default
这种混淆可能会在 Java 中持续一段时间。使用任何类型的动态资源加载的 J2SE API 并尝试猜测它使用哪种加载策略。这是一个样本:
- JNDI 使用上下文类加载器
- Class.getResource() 和 Class.forName() 使用当前的类加载器
- JAXP 使用上下文类加载器(从 J2SE 1.4 开始)
- java.util.ResourceBundle 使用调用者的当前类加载器
- 通过 java.protocol.handler.pkgs 系统属性指定的 URL 协议处理程序仅在引导程序和系统类加载器中查找
- Java Serialization API 默认使用调用者的当前类加载器
回答by Ravindra babu
Adding to @David Roussel answer, classes may be loaded by multiple class loaders.
添加到@David Roussel 的答案中,类可以由多个类加载器加载。
Lets understand how class loaderworks.
让我们了解类加载器的工作原理。
From javin paul blog in javarevisited :
来自 javarevisited 中的 javin paul 博客:
ClassLoader
follows three principles.
ClassLoader
遵循三个原则。
Delegation principle
委托原则
A class is loaded in Java, when its needed. Suppose you have an application specific class called Abc.class, first request of loading this class will come to Application ClassLoader which will delegate to its parent Extension ClassLoader which further delegates to Primordial or Bootstrap class loader
一个类在需要时用 Java 加载。假设您有一个名为 Abc.class 的特定于应用程序的类,加载此类的第一个请求将来自 Application ClassLoader,它将委托给其父 Extension ClassLoader,后者进一步委托给 Primordial 或 Bootstrap 类加载器
Bootstrap ClassLoaderis responsible for loading standard JDK class files from rt.jar and it is parent of all class loaders in Java. Bootstrap class loader don't have any parents.
Extension ClassLoaderdelegates class loading request to its parent, Bootstrap and if unsuccessful, loads class form jre/lib/ext directory or any other directory pointed by java.ext.dirs system property
System or Application class loaderand it is responsible for loading application specific classes from CLASSPATH environment variable, -classpath or -cp command line option, Class-Path attribute of Manifest file inside JAR.
Application class loaderis a child of Extension ClassLoaderand its implemented by
sun.misc.Launcher$AppClassLoader
class.
Bootstrap ClassLoader负责从 rt.jar 加载标准的 JDK 类文件,它是 Java 中所有类加载器的父类。Bootstrap 类加载器没有任何父级。
扩展类加载器将类加载请求委托给其父类 Bootstrap,如果不成功,则从 jre/lib/ext 目录或 java.ext.dirs 系统属性指向的任何其他目录加载类
系统或应用程序类加载器,它负责从 CLASSPATH 环境变量、-classpath 或 -cp 命令行选项、JAR 内 Manifest 文件的 Class-Path 属性加载应用程序特定类。
应用程序类加载器是扩展类加载器的子类,由
sun.misc.Launcher$AppClassLoader
类实现。
NOTE:Except Bootstrap class loader, which is implemented in native language mostly in C, all Java class loaders are implemented using java.lang.ClassLoader
.
注意:除了Bootstrap 类加载器,它主要是用 C 语言实现的,所有的 Java 类加载器都是使用java.lang.ClassLoader
.
Visibility Principle
可见性原则
According to visibility principle, Child ClassLoadercan see class loaded by Parent ClassLoaderbut vice-versa is not true.
根据可见性原则,子类加载器可以看到父类加载器加载的类,反之则不然。
Uniqueness Principle
唯一性原则
According to this principle a class loaded by Parent should not be loaded by Child ClassLoader again
根据这个原则,父类加载的类不应再次被子类加载器加载
回答by yonran
This does not answer the original question, but as the question is highly ranked and linked for any ContextClassLoader
query, I think it is important to answer the related question of when the context class loader should be used. Short answer: never use the context class loader! But set it to getClass().getClassLoader()
when you have to call a method that is missing a ClassLoader
parameter.
这并没有回答最初的问题,但由于该问题的排名很高并且与任何ContextClassLoader
查询相关联,我认为回答有关何时应使用上下文类加载器的相关问题很重要。简短回答:永远不要使用上下文类加载器!但是getClass().getClassLoader()
当您必须调用缺少ClassLoader
参数的方法时将其设置为。
When code from one class asks to load another class, the correct class loader to use is the same class loader as the caller class(i.e., getClass().getClassLoader()
). This is the way things work 99.9% of the time because this is what the JVM does itselfthe first time you construct an instance of a new class, invoke a static method, or access a static field.
当一个类的代码要求加载另一个类时,要使用的正确类加载器是与调用者类(即getClass().getClassLoader()
)相同的类加载器。这是 99.9% 的情况下工作的方式,因为这就是 JVM在您第一次构造新类的实例、调用静态方法或访问静态字段时所做的工作。
When you want to create a class using reflection (such as when deserializing or loading a configurable named class), the library that does the reflection should always ask the applicationwhich class loader to use, by receiving the ClassLoader
as a parameter from the application. The application (which knows all the classes that need constructing) should pass it getClass().getClassLoader()
.
当您想使用反射创建类时(例如反序列化或加载可配置的命名类时),执行反射的库应始终通过ClassLoader
从应用程序接收作为参数来询问应用程序使用哪个类加载器。应用程序(知道所有需要构造的类)应该通过它getClass().getClassLoader()
。
Any other way to obtain a class loader is incorrect. If a library uses hacks such as Thread.getContextClassLoader()
, sun.misc.VM.latestUserDefinedLoader()
, or sun.reflect.Reflection.getCallerClass()
it is a bug caused by a deficiency in the API. Basically, Thread.getContextClassLoader()
exists only because whoever designed the ObjectInputStream
API forgot to accept the ClassLoader
as a parameter, and this mistake has haunted the Java community to this day.
获取类加载器的任何其他方式都是不正确的。如果一个库使用了诸如Thread.getContextClassLoader()
, 之类的hack sun.misc.VM.latestUserDefinedLoader()
,或者sun.reflect.Reflection.getCallerClass()
它是由 API 中的缺陷引起的错误。基本上,Thread.getContextClassLoader()
存在只是因为设计ObjectInputStream
API 的人忘记将 接受ClassLoader
为参数,而这个错误至今仍困扰着 Java 社区。
That said, many many JDK classes use one of a few hacks to guess some class loader to use. Some use the ContextClassLoader
(which fails when you run different apps on a shared thread pool, or when you leave the ContextClassLoader null
), some walk the stack (which fails when the direct caller of the class is itself a library), some use the system class loader (which is fine, as long as it is documented to only use classes in the CLASSPATH
) or bootstrap class loader, and some use an unpredictable combination of the above techniques (which only makes things more confusing). This has resulted in much weeping and gnashing of teeth.
也就是说,许多 JDK 类使用少数技巧之一来猜测要使用的某些类加载器。一些使用ContextClassLoader
(当你在共享线程池上运行不同的应用程序时失败,或者当你离开时ContextClassLoader null
),一些遍历堆栈(当类的直接调用者本身是一个库时失败),一些使用系统类加载器(这很好,只要记录在CLASSPATH
) 或引导类加载器中只使用类,并且有些使用上述技术的不可预测的组合(这只会使事情变得更加混乱)。这导致了很多人哭泣和咬牙切齿。
When using such an API, first, try to find an overload of the method that accepts the class loader as a parameter. If there is no sensible method, then try setting the ContextClassLoader
before the API call (and resetting it afterwards):
在使用这样的 API 时,首先尝试找到接受类加载器作为参数的方法的重载。如果没有合理的方法,则尝试ContextClassLoader
在 API 调用之前设置(并在之后重新设置):
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);
}