Why did Tomcat break Java’s parental delegation mechanism?

  • 1. What is the class loading mechanism?

    • The relationship between classes and class loaders

  • 2. What is the Parental Appointment Model?

    • Why do you do that?

    • How is the parent delegation model implemented?

  • 3. How to destroy the parental delegation model?

  • 4.How is Tomcat’s class loader designed?

    • How does Tomcat implement its own unique class loading mechanism?

  • Summarize

I think, before studying tomcat class loading, we should review or consolidate the java default class loader. The original poster was ignorant about class loading before, so I would like to take this opportunity to review it.

The poster opened the second edition of the sacred book “In-depth Understanding of Java Virtual Machine”, p227, about the class loader. Please see:

1. What is the class loading mechanism?

The result of code compilation is converted from local machine code into bytecode. This is a small step in the storage format, but it is a big step in the development of programming languages.

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

The virtual machine design team implemented the action of “obtaining a binary byte stream describing this class through the fully qualified name of a class” in the class loading phase outside the Java virtual machine, so that the application can decide how to obtain all the data. required class. The code module that implements this action is called a “class loader”.

The relationship between classes and class loaders

Although the class loader is only used to implement class loading actions, its role in Java programs is far from limited to the class loading stage. For any class, the class loader that loads it and the class itself need to establish its uniqueness in the Java virtual machine. 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.

A front-end and back-end separated blog based on Spring Boot + MyBatis Plus + Vue 3.2 + Vite + Element Plus, including a back-end management system that supports articles, categories, tag management, dashboards and other functions.

  • GitHub address: https://github.com/weiwosuoai/WeBlog

  • Gitee address: https://gitee.com/AllenJiang/WeBlog

2. What is the Parent Appointment Model

1. From the perspective of the Java virtual machine, there are only two different class loaders: one is the boot class loader (Bootstrap ClassLoader). This class loader is implemented in the C++ language (HotSpot only) and is a virtual class loader. part of the machine itself; the other is all other class loaders, which are implemented in the Java language, independent 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 details. Most Java programmers will use the following three class loaders provided by the system:

  • Bootstrap ClassLoader: This class loader will be stored in the JAVA_HOME/lib directory, or in the path specified by the -Xbootclasspath parameter, and is recognized by the virtual machine ( Only identified by file name, such as rt.jar, class libraries with inconsistent names will not be overloaded even if they are placed in the lib directory).

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

  • 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 type, it also becomes the system class loader. It is responsible for loading the class library specified on the user class path (ClassPath). Developers can use this class loader directly. If the application does not define its own class loader, this is usually the default class loader in the program.

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

ac1b4b28b6de64a4b6380bb276740e9a.png

picture

The relationship between each class loader in the diagram becomes the Parents Dlegation Mode of the class loader. The parent delegation model requires that in addition to the top-level startup class loader, all other class loaders should be loaded by their own parent class loaders. The parent-child relationship between class loaders here is generally not realized by inheritance, but by inheritance. They all use composition relationships to reuse the code of the parent loader.

The parent delegation model of class loaders was introduced during JDK1.2 and is widely used in all subsequent Java programs. However, it is not a mandatory constraint model, but a class loading model recommended by Java designers to developers. Implementation method.

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 itself first, but delegates the request to the parent class loader to complete. This is true for every level of class loader, so all loading requests should eventually be sent to the top-level startup class loader. Only when the parent loader feedbacks that it cannot complete the request (the required one is not found in its search scope) class), the subloader will try to load it by itself.

Why do this?

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, then there will be multiple different Objects in the system. Classes, the most basic behavior in the Java type system, cannot be guaranteed. Applications will also become a mess.

How to implement the parent delegation model?

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

a3311bb6b7052ba0f0014ccff01fbdf3.png

picture

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 exception and then call its own findClass method to load it.

3. How to destroy the parental delegation model?

As we just said, 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 delegate model, but there are exceptions. So far, the parental delegation model has been “broken” on a large scale three times.

The first time: Before the emergence of the parental delegation model—–that is, before the release of JDK1.2.

The second time: It was caused by the flaws of the model itself. We say that the parent delegation model well solves the problem of unifying the base classes of each class loader (the more basic classes are loaded by the upper-level loader). The reason why the base class is called “basic” is Because they are always used as APIs called by user code, but there is no guarantee. What if the base class calls user 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 that was put in JDK1.3), but it needs to be called and implemented by an independent vendor. And deploy the JNDI interface provider (SPI, Service Provider Interface) code under the ClassPath of the application, but it is impossible for the startup class loader to “know” these codes. Because these classes are not in rt.jar, but they need to be loaded to start the class loader. How to do it?

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

Hehe, 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 to open up Reverse the use of class loaders without the hierarchical structure of the parental delegation model, which actually violates the general principles of the parental delegation model. But there is nothing you can do about it. 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 plugging, hot deployment, and modularization, which means adding a function or subtracting a function without restarting, you only need to replace the module together with the class loader to achieve hot code replace.

The book also says:

There is basically a consensus in Java programs: OSGI’s use of class loaders is worth learning. Understanding the implementation of OSGI can be regarded as mastering the essence of class loaders.

Awesome! ! !

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

A front-end and back-end separated blog based on Spring Boot + MyBatis Plus + Vue 3.2 + Vite + Element Plus, including a back-end management system that supports articles, categories, tag management, dashboards and other functions.

  • GitHub address: https://github.com/weiwosuoai/WeBlog

  • Gitee address: https://gitee.com/AllenJiang/WeBlog

4. How is Tomcat’s class loader designed?

First, let’s ask a question:

Is it possible for Tomcat to use the default class loading mechanism?

Let’s think about it: Tomcat is a web container, so what problems does it need to solve:

  • A web container may need to deploy two applications. Different applications may rely on different versions of the same third-party class library. You cannot require only one copy of the same class library on the same server, so you must ensure that each application Class libraries are all independent and guaranteed to be isolated from each other.

  • The same version of the same class library deployed in the same web container can be shared. Otherwise, if the server has 10 applications, then 10 copies of the same class library must be loaded into the virtual machine, which is nonsense.

  • The web container also has its own dependent class libraries, which cannot be confused with the application’s class libraries. For security reasons, the container’s class library should be isolated from the program’s class library.

  • The web container must support the modification of jsp. We know that the jsp file must eventually be compiled into a class file before it can be run in a virtual machine. However, it is common to modify jsp after the program is run. Otherwise, what is the use of it? Therefore, the web container needs to support jsp modification without restarting.

Look at our question again: Is it okay if Tomcat uses the default class loading mechanism?

The answer is no. Why? Let’s look at 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 only cares about your fully qualified class no matter what version you have. name, and only one copy.

The second question is that the default class loader can be implemented because its responsibility is to ensure uniqueness. The third question is the same as the first question. Let’s look at the fourth question again. We think how do we implement hot modification of jsp files (the name given by the poster). The jsp file is actually a class file. So if it is modified, but the class name is still the same, the class loader will directly If the jsp already exists in the method area is retrieved, the modified jsp will not be reloaded.

So what to do? We can directly uninstall the class loader of this 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 unloaded. Re-create the class loader and reload the jsp file.

How does Tomcat implement its own unique class loading mechanism?

So, how does Tomcat implement it? The awesome Tomcat team has already designed it. Let’s take a look at their design drawings:

6ed14753c359564036f6dfd3ba760bb4.png

picture

We see that the first three class loadings are consistent with the default ones. CommonClassLoader, CatalinaClassLoader, SharedClassLoader and WebappClassLoader are Tomcat’s own class loaders. They load /common/* and / respectively. server/*, /shared/* (already merged into the lib directory in the root directory after tomcat 6) and /WebApp/WEB-INF/* Java class library in. 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. Classes in the loading path can be accessed by the Tomcat container itself and each Webapp;

  • catalinaLoader: Tomcat container’s private class loader, the classes in the loading path are not visible to the Webapp;

  • sharedLoader: A class loader shared by each Webapp. The classes in the loading path are visible to all Webapps, but not to the Tomcat container;

  • WebappClassLoader: A private class loader for each Webapp. The classes in the loading path are only visible to the current Webapp;

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

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

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

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

Okay, so far, we already know why tomcat is designed this way and how it is designed. So, does tomcat violate the parental delegation model recommended by Java? The answer is: violated.

We said before:

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

Obviously, tomcat is not implemented in this way. In order to achieve isolation, tomcat does not comply with this agreement. Each webappClassLoader loads the class file in its own directory and will not pass it to the parent class loader.

We extend a question: What should we do if tomcat’s Common ClassLoader wants to load classes in WebApp ClassLoader? After reading the previous content about destroying the parent delegation model, we have a clear 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. . Awesome.

Summary

Okay, finally, we understand why Tomcat violates the parental delegation model, and we also know how tomcat’s class loader is designed. By the way, I reviewed Java’s default class loader mechanism, and also learned how to destroy Java’s class loading mechanism. This time the harvest is not small! ! ! hey-hey.

Source: www.jianshu.com/p /abf6fd4531e7

13854bc4d815cf59dce3bc83f3b02fdb.gif

The knowledge points of the article match the official knowledge files, and you can further learn related knowledge. Java Skill TreeHomepageOverview 139136 people are learning the system