1. Overview
1.概述
Java uses different types of class loaders to load resources during program execution. In this tutorial, we’ll explore the difference in the behavior of current and thread class loaders in Java.
Java 使用不同类型的类加载器来在程序执行期间加载资源。在本教程中,我们将探讨Java中的当前类加载器和线程类加载器的行为差异。
2. What Does a Class Loader Do?
2.类装载机是做什么的?
Java class loaders locate and load the classes required for application execution. If the requested class is dependent on any other resources, they are loaded as well.
Java类加载器定位并加载应用程序执行所需的类。如果请求的类依赖于任何其他资源,它们也会被加载。
We need appropriate class loaders for loading different types of classes whenever required by Java programs.
我们需要适当的类加载器,以便在需要时加载不同类型的类由Java程序。
3. Relationship Between Class Loaders
3.班级装载机之间的关系
Java class loaders follow a hierarchical relationship.
Java类加载器遵循一个层次关系。
Each request to find or load a class is delegated to the respective parent class loader. If all the ancestor class loaders are unable to find a class, then the current class loader tries to locate it. Here, “current class” implies the class of the currently executing method.
每个寻找或加载一个类的请求都被委托给相应的父类加载器。如果所有的祖先类加载器都无法找到一个类,那么当前的类加载器就会尝试找到它。这里,”当前类 “指的是当前执行的方法的类。
This relationship between class loaders helps in maintaining the uniqueness of resources in an application. Additionally, if a class has already been loaded by a parent class loader, the child class loader doesn’t need to reload it.
类加载器之间的这种关系有助于维护应用程序中资源的独特性。此外,如果一个类已经被父类加载器加载,则子类加载器不需要重新加载它。
4. Default Class Loaders
4.默认的类加载器
Class loaders load classes and resources present on their respective classpath:
类加载器加载存在于其各自classpath上的类和资源。
- System or application class loaders load classes from the application classpath
- Extension class loaders search on the Extension classpath (JRE/lib/ext)
- Bootstrap class loader looks on the Bootstrap classpath (JRE/lib/rt.jar)
A Bootstrap or Primordial class loader is the parent of all class loaders. It loads the Java runtime – the classes required to run the JVM itself.
Bootstrap 或 Primordial 类加载器是所有类加载器的父类。它加载Java运行时–运行JVM本身所需的类。
Current class loaders search for resources in a linear, hierarchical fashion. If a class loader can’t locate a class, it throws java.lang.ClassNotFoundException to the corresponding child class loader. The child class loader then tries to search for the class.
目前的类加载器是以线性、分层的方式搜索资源的。如果一个类加载器不能定位一个类,它会向相应的子类加载器抛出java.lang.ClassNotFoundException。然后,子类加载器会尝试搜索该类。
For scenarios where required resources aren’t found on classpaths of any of the class loaders in the hierarchy, we get error messages related to java.lang.ClassNotFoundException as the end result.
对于在层次结构中任何一个类加载器的classpaths上找不到所需资源的情况,我们会得到与java.lang.ClassNotFoundException有关的错误信息作为最终结果。
We can customize the default class loading behavior as well. We can explicitly specify the class loader while loading a class dynamically.
我们也可以自定义默认的类加载行为。我们可以在动态加载一个类时明确指定类加载器。
However, we should note that if we load the same class from different types of class loaders, these will be seen as different resources by the JVM.
然而,我们应该注意,如果我们从不同类型的类加载器中加载同一个类,这些将被JVM视为不同的资源。
5. Context Class Loaders
5.上下文类加载器
Apart from the default class loaders, J2SE also introduced context class loaders.
除了默认的类加载器之外,J2SE 还引入了context 类加载器。
Each thread in Java has an associated context class loader.
Java中的每个thread都有一个相关的上下文类加载器。
We can access/modify the context class loader for a thread using the getContextClassLoader() and setContextClassLoader() methods of the Thread class.
我们可以使用Thread类的getContextClassLoader()和setContextClassLoader()方法来访问/修改线程的上下文类加载器。
The context class loader is set at the time of the creation of a thread. If not set explicitly, it defaults to the context class loader of the parent thread.
上下文类加载器在创建线程时被设置。如果没有明确设置,它 默认为父线程的上下文类加载器。
Context class loaders also follow the hierarchy model. The root class loader, in this case, is the context class loader of the primordial thread. A primordial thread is the initial thread created by the operating system.
上下文类加载器也遵循层次结构模型。在这种情况下,根类加载器是原始线程的上下文类加载器。初始线程是由操作系统创建的初始线程。
As the application starts executing, other threads may get created. The context class loader of a primordial thread is initially set to the class loader that loads the application, i.e., the system class loader.
随着应用程序开始执行,其他线程可能会被创建。原始线程的上下文类加载器最初被设置为加载应用程序的类加载器,即系统类加载器。
Suppose we don’t update the context class loader for any thread at any level of the hierarchy. As a result, we can say that by default, the context class loader for a thread is the same as the system class loader. For such scenarios, if we perform Thread.currentThread().getContextClassLoader() and getClass().getClassLoader() operations, both will return the same objects.
假设我们不为任何层次的任何线程更新上下文类加载器。因此,我们可以说,默认情况下,线程的上下文类加载器与系统类加载器相同。对于这种情况,如果我们执行Thread.currentThread().getContextClassLoader()和getClass().getClassLoader()操作,两者将返回相同的对象。
5.1. Handle Issues With Delegation
5.1.处理授权的问题
Context class loaders are significant when required resources aren’t present on the classpath of the default Java class loaders. Therefore, we can use context class loaders to diverge from the traditional linear delegation model.
当所需的资源不存在于默认的Java类加载器的classpath上时,上下文类加载器就显得非常重要。因此,我们可以使用上下文类加载器来偏离传统的线性委托模型。
In the hierarchical model of class loaders, resources loaded by parent class loaders are visible to the child class loaders, but not vice versa. In some scenarios, parent class loaders might need to access classes present on the classpath of child class loaders.
在类加载器的分层模型中,父类加载器加载的资源对子类加载器是可见的,但反之亦然。在某些情况下,父类加载器可能需要访问子类加载器的classpath上的类。
Context class loaders are a useful tool to make this happen. We can set the context class loader to the desired value when accessing required resources. Hence, in the above case, we can use the child thread’s context class loader and can locate the resources present at the child class loader level.
上下文类加载器是实现这一目标的一个有用工具。我们可以在访问所需资源时将上下文类加载器设置为所需值。因此,在上述情况下,我们可以使用子线程的上下文类加载器,并可以定位存在于子类加载器层面的资源。
5.2. Multi-Module Environment
5.2.多模块环境
While setting the context class loader property, we’re basically switching the context for loading resources. Instead of searching at the current classpath, we fetch a new class loader pointing to a different classpath. This is particularly helpful if we want to load resources from a third-party module or if we are working in an environment with different class namespaces.
在设置上下文类加载器属性时,我们基本上是在切换加载资源的上下文。我们不是在当前的classpath上搜索,而是获取一个指向不同classpath的新类加载器。如果我们想从第三方模块加载资源,或者我们在一个具有不同类命名空间的环境中工作,这就特别有帮助。
However, we should exercise caution here and reset the context class loader property back to the original class loader to avoid any future discrepancies.
然而,我们应该在此谨慎行事,将上下文类加载器属性重新设置为原始类加载器,以避免未来出现任何差异。
6. Conclusion
6.结语
In this article, we’ve analyzed the significance of using context class loaders to load resources not accessible via normal class loaders. We saw that we could also choose to temporarily update the context class loader for a given thread for loading required classes.
在这篇文章中,我们已经分析了使用上下文类加载器来加载无法通过正常类加载器访问的资源的意义。我们看到,我们也可以选择暂时更新某个线程的上下文类加载器来加载所需的类。
It is essential to understand the context in which the current method is working. We can have resources with the same name existing on different classpaths. Hence, while loading resources from multiple class loaders, we should exercise caution.
了解当前方法所处的环境是至关重要的。我们可以在不同的classpaths上有相同名称的资源存在。因此,在从多个类加载器加载资源时,我们应该谨慎行事。