4. Why does the Tomcat class loader violate the parent delegation model

1. What is the class loading mechanism?

Converting the result of code compilation from native machine code to bytecode is one small step in storage format, but one giant leap in programming language development.

The Java virtual machine loads the data describing the class into the memory from the Class file, and verifies the data, converts, parses, and initializes the data, and finally forms a Java type that can be directly used by the virtual machine. This is the class loading mechanism of the virtual machine.

The virtual machine design team put the action of " getting the binary byte stream describing this class through the fully qualified name of a class " in the class loading phase to the outside of the Java virtual machine , so that the application can decide how to get all the data. required class. The code module that implements this action becomes the " class loader " .

The relationship between classes and class loaders

Although the class loader is only used to implement class loading, its role in Java programs is far beyond the class loading stage. For any class, its uniqueness in the Java virtual machine needs to be established by the class loader that loads it and the class itself . Each class loader has an independent class namespace. This sentence can be expressed more generally: comparing whether two classes are " equal " only makes sense if the two classes are loaded by the same class loader , otherwise, even if the two classes come from the same Class files are loaded by the same virtual machine. As long as the class loaders that load them are different, the two classes must not be equal.

2. What is the Parental Appointment Model

1. From the perspective of the Java virtual machine, there are only two different class loaders: one is the Bootstrap ClassLoader , which is implemented in C++ language (HotSpot only) and is the virtual machine itself. part of ; the other is all other class loaders, which are implemented by the Java language, independent of the outside of the virtual machine, and all inherit from the abstract class java.lang.ClassLoader .

2. From the perspective of Java developers, class loading can be divided into more detail. Most Java programmers use the class loaders provided by the following three systems:

1.    Bootstrap ClassLoader : This class loader will be stored in the JAVA_HOME/lib directory, or by the path specified by the -Xbootclasspath parameter, and recognized by the virtual machine (only by file name recognition) , such as rt.jar , the class library whose name does not match will not be overloaded even if it is placed in the lib directory).

2.    Extension ClassLoader : This class loader is implemented by sun.misc.Launcher $ExtClassLoader , which is responsible for mixing the paths in the JAVA_HOME/lib/ext directory, or the path specified by the java.ext.dirs system variable All class libraries of the species. Developers can use the extension class loader directly.

3.    Application class loader ( Application ClassLoader ): This class loader is implemented by sun.misc.Launcher$AppClassLoader . Since this class loader is the return value of the getSystemClassLoader method of ClassLoader , it also becomes the system class loader. It is responsible for loading the class library specified on the user's class path ( ClassPath ). Developers can use this class loader directly. If the application has not defined its own class loader, in general, this is the default class loader in the program.

The relationship between these class loaders is generally shown in the following figure: 

The relationship between the various class loaders in the figure becomes the  Parents Dlegation Mode of the class loader . The parent delegation model requires that, except for the top-level startup class loader, the rest of the class loaders should be loaded by their own parent class loader. The parent-child relationship between class loaders is generally not implemented by inheritance, but Both use the composition relationship to reuse the code of the parent loader.

The parent delegation model of the class loader was introduced during JDK 1.2 and was widely used in all subsequent Java programs, but it is not a mandatory constraint model, but a class loading recommended by Java designers to developers device implementation.

The working process of the parent delegation model is: if a class loader receives a class loading request, it will not try to load the class by itself first, but delegate the request to the parent class loader to complete it. This is true at every level of class loader, so all load requests should eventually be passed to the top-level startup class loader, only when the parent loader reports that it cannot complete the request (his search scope does not find the desired class), the subloader will try to load it by itself.

Why do you do that?

If the parent delegation model is not used, and each class loader loads it by itself, if the user writes a class called java.lang.Object and puts it in the ClassPath of the program, the system will appear multiple different Objects classes , the most fundamental behavior of the Java type system is not guaranteed. Applications will also become a mess.

How does it work when the parents appoint the model?

Very simple: all the code is in the loadClass method in java.lang.ClassLoader , the code is as follows:  

The logic is clear and easy to understand: first check whether it has been loaded, if not, call the loadClass method of the parent loader. If the parent loader is empty, the startup class loader will be used as the parent loader by default. If the parent class fails to load, throw a ClassNotFoundException, and then call its own findClass method to load it.

3. How to break the parental appointment model?

We just said that the parent delegation model is not a mandatory constraint model, but a suggested class loader implementation. In the Java world, most class loaders follow the model, but there are exceptions. So far, the parent delegation model has been "broken" on a large scale 3 times. 
The first time : Before the parental delegation model appeared - that is, before the release of JDK1.2. 
The second time : it is caused by the defects of the model itself. We say that the parent delegation model solves the problem of unification of the basic classes of each class loader (the more basic classes are loaded by the upper loader), the basic classes are called "basic" because they always Is as an API called by user code, but not absolutely, what if the underlying class calls back to the user's code ?

It's not impossible. A typical example is the JNDI service. JNDI is now a standard service in Java. Its code is loaded by the startup class loader (rt.jar, which was put in JDK1.3), but it needs to be called by an independent vendor. And deploy the code of the JNDI interface provider (SPI, Service Provider Interface) under the ClassPath of the application, but it is impossible for the startup class loader to "recognize" these codes. Because these classes are not in rt.jar, but the startup class loader needs to load again. How to do it?

In order to solve this problem, the Java design team had to introduce a less elegant design: the thread context class loader ( Thread Context ClassLoader ) . This class loader can be set via the setContextClassLoader method of the java.lang.Thread class. If it is not set when the thread is created, it will inherit one from the parent thread. If there is not too much set in the global scope of the application, then this class loader defaults to the application class loader .

Hey, with the thread context loader, the JNDI service uses this thread context loader to load the required SPI code, that is, the parent class loader requests the child class loader to complete the class loading action , this behavior is actually getting through Reversing the hierarchy of the parent delegation model to use class loaders actually violates the general principles of the parent delegation model. But this is helpless, all loading actions involving SPI in Java basically use this method. For example JNDI, JDBC , JCE, JAXB, JBI, etc.

The third time : In order to achieve hot swap, hot deployment, and modularization, which means adding a function or subtracting a function without restarting, just replace the module together with the class loader to realize the hot replacement of the code.

The book also states:

There is basically a consensus in Java programs: OSGI is worth learning about the use of class loaders. If you understand the implementation of OSGI , you can be regarded as mastering the essence of class loaders.

Niubi! ! !

Now, we have basically understood the function of Java's default class loading, and we also know the parent delegation model. Having said so much, I almost forgot our tomcat. Our question is why does the Tomcat loader violate the parent delegation model? Let's talk about the class loader of our tomcat.

4. How is Tomcat's class loader designed?

First, let's ask a question:

Can Tomcat use the default class loading mechanism?

Let's think about it: Tomcat is a web container, so what problems does it solve: 
1. A web container may need to deploy two applications, and different applications may depend on different versions of the same third-party class library, and cannot require the same one There is only one copy of the class library on the same server, so it is necessary to ensure that the class library of each application is independent and isolated from each other .  
2. The same version of the same class library deployed in the same web container can be shared . Otherwise, if the server has 10 applications, it is nonsense to have 10 copies of the same class library loaded into the virtual machine. 
3. The web container also has its own dependent class library, which cannot be confused with the class library of the application . Based on security considerations, the class library of the container should be isolated from the class library of the program. 
4. The web container must support the modification of jsp . We know that the jsp file must be compiled into a class file in order to run in the virtual machine, but it is commonplace to modify the jsp after the program is running. Otherwise, why should you use it? Therefore, the web container needs to support jsp modification without restarting.

Let's look at our question again: Can Tomcat use the default class loading mechanism? 
The answer is no. Why? Let's see, the first problem, if you use the default class loader mechanism, you cannot load two different versions of the same class library. The default accumulator is no matter what version you are, it only cares about your fully qualified class name and only one copy. The second problem, the default class loader can be implemented, because his responsibility is to ensure uniqueness . The third question is the same as the first. Let's look at the fourth question. We think how we can implement the hot modification of the jsp file (the name given by the landlord), the jsp file is actually a class file, then if it is modified, but the class name is still the same, the class loader will directly The modified jsp will not be reloaded if it already exists in the method area. So what to do? We can directly uninstall the class loader of the jsp file, so you should have thought that each jsp file corresponds to a unique class loader. When a jsp file is modified, the jsp class loader is directly uninstalled. Recreate the classloader, reload the jsp file.

How does Tomcat implement its own unique class loading mechanism?

So, how is Tomcat implemented? The awesome Tomcat team has designed it. Let's take a look at their blueprints:

We can see that the first three class loads are the same as the default ones, CommonClassLoader , CatalinaClassLoader , SharedClassLoader and WebappClassLoader are class loaders defined by Tomcat themselves , which load /common/* , /server/* , /shared/* ( After tomcat 6, it has been merged into the lib directory under the root directory) and the Java class library in /WebApp/WEB-INF/* . Among them, there are usually multiple instances of the WebApp class loader and the Jsp class loader, each Web application corresponds to a WebApp class loader, and each JSP file corresponds to a Jsp class loader.

·         commonLoader : Tomcat 's most basic class loader, the classes in the loading path can be accessed by the Tomcat container itself and each Webapp ;

·         catalinaLoader : The private class loader of the Tomcat container, the classes in the loading path are not visible to the Webapp ;

sharedLoader         : The class loader shared by each Webapp , the class in the loading path is visible to all Webapps , but not visible to the Tomcat container;

·         WebappClassLoader : The private class loader of each Webapp , the classes in the loading path are only visible to the current Webapp ;

As can be seen from the delegation relationship in the figure:

The classes that CommonClassLoader can load can be used by Catalina ClassLoader and SharedClassLoader , thereby realizing the sharing of public class libraries, while the classes that CatalinaClassLoader and SharedClassLoader can load are isolated from each other.

WebAppClassLoader can use the classes loaded by SharedClassLoader , but the individual WebAppClassLoader instances are isolated from each other.

The loading scope of JasperLoader is only the .Class file compiled by this JSP file , and its purpose is to be discarded: when the Web container detects that the JSP file has been modified, it will replace the current JasperLoader instance, and By creating a new Jsp class loader to realize the HotSwap function of the JSP file .

Ok, so far, we already know why tomcat is designed this way, and how it is designed, so, does tomcat violate the parent delegation model recommended by java? The answer is: violated. We said earlier:

The parent delegation model requires that all class loaders, except the top-level startup class loader, should be loaded by their own parent class loader.

Obviously, tomcat is not implemented like this. In order to achieve isolation, tomcat does not follow this convention. Each webappClassLoader loads class files in its own directory and will not pass it to the parent class loader.

We extend a question: what if the Common ClassLoader of tomcat wants to load the classes in the WebApp ClassLoader?

After reading the previous content about destroying the parent delegation model, we have a good idea. We can use the thread context class loader to implement it. Using the thread context loader, the parent class loader can request the child class loader to complete the class loading action. . Be cool.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325541451&siteId=291194637