Confuse
In the past, when looking at the source code, I always encountered the code in the framework using Thread.currentThread.getContextClassLoader() to get the Context class loader of the current thread, and use this Context class loader to load the class.
When we usually write code in a program, when we want to load classes dynamically, we usually use Class.forName() to load the classes we need. For example, the most common thing is that when we are programming JDBC, we use Class.forName() to load the JDBC driver.
try { return Class.forName("oracle.jdbc.driver.OracleDriver");} catch (ClassNotFoundException e) { // skip}So why when we use Class.forName() to load a class, if the class cannot be found, we still try to load the class using the class loader obtained by Thread.currentThread.getContextLoader()? For example, we may encounter the following code:
try { return Class.forName(className);} catch (ClassNotFoundException e) { // skip}ClassLoader ctxClassLoader = Thread.currentThread().getContextClassLoader(); if (ctxClassLoader != null) { try { clazz = ctxClassLoader.loadClass(className); } catch (ClassNotFoundException e) { // skip }}The bold part here is to use the loader obtained by Thread.currentThread.getContextLoader() to load the class. Obviously, the class loader used when Class.forName() loads the class may be different from the class loader obtained by Thread.currentThread.getContextLoader(). So why does the difference occur?
JAVA class loader
Before understanding why this class loader obtained by Thread.currentThread.getContextLoader() is used, let's first understand the class loader (ClassLoader) used in the JVM.
There are three types of classloaders in JVM by default:
Bootstrap Class Loader
The Bootstrap Class Loader class loader is a class loader built into JDK, which is used to load classes inside JDK. The Bootstrap class loader is used to load the classes below $JAVA_HOME/jre/lib in the JDK, such as the classes in the rt.jar package. The Bootstrap class loader is part of the JVM and is generally written in native code.
Extension Class Loader
Extension Class Loader class loader is mainly used to load classes in JDK extension package. Generally, the packages under $JAVA_HOME/lib/ext are loaded through this class loader, and the classes under this package basically start with javax.
System Class Loader
System Class Loader class loader is also called Application Class Loader (AppClassLoader). As the name suggests, this class loader is used to load the application code that developers usually write. The System class loader is used to load application-level classes stored in the classpath path.
The following code lists these three class loaders:
public class MainClass { public static void main(String[] args) { System.out.println(Integer.class.getClassLoader()); System.out.println(Logging.class.getClassLoader()); System.out.println(MainClass.class.getClassLoader()); }}Where to get Bootstrap class loader returns null value forever
null # Bootstrap class loader sun.misc.Launcher$ExtClassLoader@5e2de80c # Extension class loader sun.misc.Launcher$AppClassLoader@18b4aac2 # System class loader
Parent delegation model
The three type loaders introduced above are not isolated, they have a hierarchical relationship:
The three class loaders work together through this hierarchical relationship and are responsible for the loading of classes together. The above hierarchical model is called the "parent delegation" model of the class loader. The parent delegation model requires that all class loaders must have a parent loader except for the top-level Bootstrap class loader. When the class loader loads a class, first check whether there are classes in the cache that have been loaded. If not, then the parent loader is delegated to load the class first. The parent loader performs the same work as the previous child loader until the request reaches the top-level Bootstrap class loader. If the parent loader cannot load the required class, then the child loader will try to load the class by itself. It works similar to the following:
We can use the code in ClassLoader in JDK to see the implementation of the parent delegation mechanism. The code is implemented in ClassLoader.loadClass()
rotated Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException synchronized (getClassLoadingLock(name)) { // First, check if the class has already been loaded Class<?> c = findLoadedClass(name); if (c == null) { long t0 = System.nanoTime(); try { if (parent != null) { c = parent.loadClass(name, false); } else { c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader } if (c == null) { // If still not found, then invoke findClass in order // to find the class. long t1 = System.nanoTime(); c = findClass(name); // this is the defining class loader; record the stats sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } } if (resolve) { resolveClass(c); } return c; }One advantage of using parent delegation to organize class loaders is to be safe. If we define a String class ourselves, we hope to replace this String class with the implementation of java.lang.String in the default Java.
We put the class file of the String class we implemented in the classpath path. When we use the class loader to load the String class we implemented, first, the class loader will delegate the request to the parent loader, and through layer by layer, the Bootstrap class loader will finally load the String type in the rt.jar package, and then return it to us all the way. In this process, our class loader ignores the String class we placed in the classpath.
If the parent delegation mechanism is not adopted, the System class loader can find the String class file in the classpath path and load it into the program, causing the String implementation in the JDK to be overwritten. Therefore, this working method of class loaders ensures that Java programs can run safely and stably to a certain extent.
Thread context class loader
The above talks about so many class loaders related content, but still does not talk about today's topic, thread context class loaders.
At this point, we already know that Java provides three type loaders and works in concert according to a strict parent delegation mechanism. On the surface, it seems perfect, but it is this strict parent delegation mechanism that causes some limitations when loading classes.
When our more basic framework needs to use application-level classes, we can only use these classes if this class is loaded when the class loader used by our current framework can be loaded. In other words, we cannot use the class loader of the current class loader. This limitation is caused by the parent delegation mechanism, because the delegation of class loading requests is one-way.
Although there are not many cases, there is still such a demand. A typical JNDI service. JNDI provides an interface to query resources, but the specific implementation is implemented by different manufacturers. At this time, JNDI's code is loaded by the JVM's Bootstrap class loader, but the specific implementation is code other than the JDK provided by the user, so it can only be loaded by the System class loader or other user-defined class loader. Under the parent delegation mechanism, JNDI cannot obtain the implementation of JNDI's SPI.
To solve this problem, a thread context class loader is introduced. SetContextClassLoader() of the java.lang.Thread class setContextClassLoader() (if not set, it will be inherited from the parent thread by default. If the program has not set it, it will default to the System class loader). With the thread context class loader, the application can pass the class loader used by the application to the code that uses the top-level class loader through java.lang.Thread.setContextClassLoader(). For example, the above JNDI service can use this method to obtain a class loader that can load SPI implementations and obtain the required SPI implementation classes.
It can be seen that introducing threaded class loaders is actually a destruction of the parent delegation mechanism, but it provides flexibility in class loading.
Resolve doubts
Back to the beginning, in order to load classes implemented by users outside the framework, these classes may not be loaded through the class loader used by the framework. In order to bypass the parent delegation model of the class loader, Thread.getContextClassLoader() is used to load these classes.
The above is all the content of this article. I hope it will be helpful to everyone's learning and I hope everyone will support Wulin.com more.