Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

What is the JVM parent delegation model and the principle of SPI implementation

2025-02-14 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/02 Report--

This article mainly explains the "JVM parent delegation model and SPI implementation principle is what", the article explains the content is simple and clear, easy to learn and understand, the following please follow the editor's ideas slowly in-depth, together to study and learn "JVM parent delegation model and SPI implementation principle is what" it!

1. Parental delegation model

We know that the class loading mechanism is the process of converting a class from a bytecode piece to a virtual machine, but who is responsible for the loading process in this process? how does it complete or ensure the accuracy and security of class loading?

The answer is the classloader and the parent delegation mechanism.

The mechanism of the parent delegation model is that when the class loader receives a request for a class load, it does not attempt to load the class, but delegates the request to the class loader to complete it. Only when the classloader reports back that the load request is completed, the class loader will attempt to load the class.

We can see its "working mechanism" from the JDK source code.

ClassLoader#loadClass (String, boolean)

This is the source code in the java.lang.ClassLoader class of jdk1.8, which is used to load the specified class.

/ * * @ author pre-court cloud fall * @ Date on 2020-8-22 21:29 * @ description * / public class ClassLoader {protected Class loadClass (String name, boolean resolve) throws ClassNotFoundException {synchronized (getClassLoadingLock (name)) {/ / First, check if the class has already been loaded / / first, check whether the class has been loaded by the current class loader, if the class has not been loaded by the current class load Call the load class method of the Class class to load the class (if the class is null, give it to the startup class loader to load) Class c = load (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 foun / / from the non-null parent class loader} if (c = = null) {/ / If still not found, then invoke findClass in orde / / to find the class. / / if the addTime class does not finish loading, ask the current class loader to load the class long T1 = System.nanoTime (); c = findClass (name); / / this is the defining class loader; record the stats sun.misc.PerfCounter.getParentDelegationTime () .class (T1-T0) Sun.misc.PerfCounter.getFindClassTime () .addElapsedTimeFrom (T1); sun.misc.PerfCounter.getFindClasses () .increment ();}} if (resolve) {/ / link the specified class resolveClass (c) } return c;}

After looking at the previous code, we know that this is the explanation of the parent delegation model code layer:

When the class loader receives the request for class loading, the class loader first checks whether the class has been loaded by the current class loader

If the class has not been loaded, the current class loader will delegate the load request to the loading class loader to complete

If the classloader of the current classloader (or the classloader of the current classloader Recursive up to null, which will delegate the startup class loader to complete loading

If the classloader method finishes loading the class, the current classloader will try to load the class.

2. Classification of class loaders and their respective responsibilities

There are three predefined classloaders in JVM: boot classloader (Bootstrap ClassLoader), extended classloader (ExtensionClassLoader), response classloader / system classloader (App/SystemClassLoader), and user-defined classloaders, each with its own responsibilities.

2.1. Start the classloader Bootstrap ClassLoader

The boot classloader, as the "ancestor" of all classloaders, is implemented by C++ and does not inherit from the java.lang.ClassLoader class. It will be loaded by the virtual machine's "C++ code feed" when the virtual machine starts, so it does not have a classloader, and after loading is complete, it will be responsible for loading the extension classloader and the corresponding classloader.

The startup class loader is located in the kernel class that loads the Java-located in\ lib, or in the path specified by the-Xbootclasspath parameter, and is a class library that can be recognized by the virtual machine (identified only by the name of the kernel, such as rt.jar, tools.jar. Class libraries whose names do not match will not be loaded even if they are placed in the lib directory).

2.2. Extended class loader Extension ClassLoader

The extension classloader inherits from the java.lang.ClassLoader class, its boot classloader is the startup classloader, and the display of the boot classloader in Java is null.

Quote the comment on the "jdk1.8 ClassLoader#getParent ()" method, which is used to obtain the classloader's classloader: Returns the parent class loader for delegation. Some implementations may use null to represent the bootstrap class loader. This method will return null in such implementations if this class loader's parent is the bootstrap class loader.

The extension class loader is responsible for loading all classes in the\ lib\ ext directory or the path specified by the java.ext.dirs system variable.

It is important to note that the extension class loader only holds the bytecode components that are packaged in .jar format.

2.3. Should be classed / system classloader App/System ClassLoader

The classloader inherits from the java.lang.ClassLoader class, and its classloader is the extension classloader.

The classloader is responsible for loading the class libraries specified on the user classpath classpath.

If there is no classloader defined in the application, in general, the classloader is the default classloader in the program.

2.4.Definitions classloader Custom ClassLoader

The classic definition classloader inherits from the java.lang.ClassLoader class, and its classic classloader is the should classloader.

This is a class loader defined by a household registration account, which can load the bytecode parts of the specified path.

To define a class loader, you need to inherit the java.lang.ClassLoader class and override the load class logic defined by the findClass method (explained below why the loadClass method is not overridden) to implement.

3. Advantages of parent delegation model

Based on the hierarchical relationship with priority stipulated by the parent delegation model, the repeated loading of classes can be avoided when the virtual machine runs the program. When the Class loader has already loaded the class, if another request for loading the class is passed to the loader, and the loader executes the loadClass method, and then entrusts the loader to the classloader to attempt to load the class, but the loader loads the class c = findLoadedClass (name). If you check whether the class has been loaded at this stage, you will check that the class has been loaded and return to the class directly, and will not load the class again.

The parent delegation model can avoid kernel tampering. The core classes we describe are rt.jar and tools.jar, which are loaded by startup class loaders. These class libraries are widely operated in regular development. If they are tampered with, the consequences will be unimaginable. Suppose we "define" a java.lang.Integer class, which is similar to the process of benefit 1. When the request to load the class is passed to the startup classloader, the startup class loader executes the findLoadedClass (String) method to find that the java.lang.Integer has been loaded, and then directly returns to the class. The request to load the class ends. Although the reason to avoid tampering with the kernel class is to avoid repeated loading of the class, it can still be used as an advantage of the parental delegation model.

4. The deficiency of the parental delegation model.

This "no" can also be understood as breaking the parent delegation model, when the parent delegation model is dissatisfied with the needs of the customer, but it is because of its inadequacy that causes the customer to break the description of the model, that is, the three kinds of parent delegation model.

Due to historical reasons (the ClassLoader class already exists at the time of JDK1.0, and the parent delegation model is introduced after JDK1.2), when the parent delegation model is not referenced, the user-defined class loader needs to inherit the java.lang.ClassLoader class and override the loadClass () method, because the virtual machine calls ClassLoader#loadClassInternal (String) when the class is loaded. This method (source code below) invokes the custom class loading overridden loadClass () method.

After introducing the parental delegation model, the ClassLoader#loadClass method is actually the implementation of the parental delegation model. If this method is rewritten, it is tantamount to breaking the parental delegation model. In order to make the classloader defined by the "household" also follow the parent delegation model, JDK added the findClass method to "implement" the defined classloading logic.

/ * * @ author in front of the court * @ Date 21:29 on 2020-8-22 * @ description * / public class ClassLoader {/ / This method is invoked by the virtual machine to load a class. Private Class loadClassInternal (String name) throws ClassNotFoundException {/ / For backward compatibility, explicitly lock on 'this' when / / the current class loader is not parallel capable. If (parallelLockMap = = null) {synchronized (this) {return loadClass (name);}} else {return loadClass (name);}} / / other methods omitted.}

Due to the hierarchical relationship stipulated by the parent delegation model, the classes loaded by the class loader can access the classes loaded by the class loader, and the classes loaded by the class loader can access the classes loaded by the class loader. In order to enable the classes loaded by the upper class loader to access the classes loaded by the lower class loader, or to let the class loader delegate the class loader to complete the load request, JDK introduces the thread up and down class loader to break down the barrier of the parent delegation model.

When customers need the dynamic nature of the program, such as code hot replacement, module hot deployment, etc., the parent delegation model is no longer suitable, and the class loader will develop into a more complex structure.

5. Thread context class loader

When talking about the parents' delegation model, the previous post mentioned the thread up and down class loader Thread Context ClassLoader. The thread up and down class loader is a private member variable of type ClassLoader defined in the Thread class, which points to the current thread's class loader.

It has been mentioned in the previous section that the thread up and down class loading allows the class loader to delegate the class loader to complete the load request, so how is this achieved? Let's discuss it next time.

Application of 5.1.SPI in JDBC

We know that Java provides some SPI (Service Provider Interface) connection, which allows the service provider to write specific code logic to complete the function of the connection.

However, the SPI connection provided by Java is loaded by the startup class loader in the kernel class library, and the specific logic code implemented by the quotient is in classpath and is loaded by the client class loader. The class loaded by the startup class loader accesses the class loaded by the Ying class loader, that is to say, the startup class loader method finds the SPI implementation class and relies solely on the parent delegation model to realize the function of SPI. So the class loader should be loaded up and down the thread.

JDBC is probably the most common SPI provided by Java. Let's take JDBC as an example to see how the downthreaded up and down class loader breaks the parent delegation model.

To recall the scenario where we used to JDBC, we need to create a driver and then create a connection, just like the code for the following:

/ * * @ author pre-court cloud fall * @ Date 2020-8-22 21:29 * @ description * / public class ThreadContextClassLoaderDemoOfJdbc {public static void main (String [] args) throws Exception {/ / load the implementation class Class.forName ("com.mysql.jdbc.Driver") of Driver; / / build the connection Connection conn = DriverManager.getConnection ("jdbc:mysql://localhost:3306/mysql", "root", "1234") }}

It is possible to write Class.forName ("com.mysql.jdbc.Driver") after JDK1.6, and the code still works normally. This is because the jdbc4.0 version of the belt already adheres to the SPI service loading mechanism. As long as the service provider's implementation class is in the classpath path, the Java program will actively and dynamically load the specific driver implementation class that conforms to the SPI specification, and the driver's fully qualified class name is in the META-INF.services package.

So, let's focus the light on the statement that builds the connection, which adjusts the static getConnection of the DriverManager class.

Before calling this method, according to the initialization time of the class loading mechanism, the static method of the class will trigger the initialization of the class, and when the DriverManager class is initialized, it will hold its static code block.

/ * * @ author pre-court cloud settlement * @ Date 21:29 on 2020-8-22 * @ description * / public class DriverManager {static {loadInitialDrivers (); println ("JDBC DriverManager initialized");} private static void loadInitialDrivers () {String drivers / / omit the code: read the system property jdbc.drivers / / load the driver class AccessController.doPrivileged in classpath through SPI (new PrivilegedAction () {public Void run () {/ / ServiceLoader class is SPI-loaded hardware class ServiceLoader loadedDrivers = ServiceLoader.load (Driver.class); Iterator driversIterator = loadedDrivers.iterator () Try {while (driversIterator.hasNext ()) {driversIterator.next ();}} catch (Throwable t) {/ / Do nothing} return null;}}) / / omit the code: make the "should" class loader continue to load the driver class in the system property jdbc.drivers}}

From the code above, you can see that the dynamic loading of the program is completed by calling the ServiceLoader.load (Driver.class) method. All the specific quotient implementation classes in the classpath path that implement Driver.class connection. In the ServiceLoader.load () method, you get the current thread up and down class loader, pass it on, and use it as a class loader to implement the loading logic.

/ * * @ author pretrial cloud fall * @ Date on 2020-8-22 21:29 * @ description * / public final class ServiceLoader implements Iterable {public static ServiceLoader load (Class service) {/ / get the thread up and down class loader AppClassLoader of the current thread, which is called ClassLoader cl = Thread.currentThread (). GetContextClassLoader () in loading classpath; return ServiceLoader.load (service, cl);}}

By default, JDK loads the class loader of the current class to load the classes that the current class depends on and is not loaded, while the ServiceLoader class is located under the java.util package, but the startup class loader completes the loading, while the specific driver class implemented by the vendor is located under classpath. Start the class loader to load the classes in the classpath directory, and if the class loader that loads the specific driver class becomes a response class loader, then you can complete the loading.

By tracking the code, it is not difficult to see that the ServiceLoader#load (Class) iteration method creates a LazyIterator class and returns a ServiceLoader object. The former is a lazy iterator, and it is also a member variable of the latter. When traversing the iterator, it triggers the loading of the "mark-up" implementation class.

/ * * @ author pretrial cloud settlement * @ Date 21:29 on 2020-8-22 * @ description * / private class LazyIterator implements Iterator {public S next () {if (acc = = null) {return nextService ();} else {PrivilegedAction action = new PrivilegedAction () {public S run () {return nextService ();}} Return AccessController.doPrivileged (action, acc);}}

In the DriverManager#loadInitialDrivers substitution method, which is held by the static code block of the DriverManager class, there is a section of code like this:

AccessController.doPrivileged (new PrivilegedAction () {public Void run () {ServiceLoader loadedDrivers = ServiceLoader.load (Driver.class); Iterator driversIterator = loadedDrivers.iterator (); try {while (driversIterator.hasNext ()) {driversIterator.next ()) }} catch (Throwable t) {/ / Do nothing} return null;})

This code returns a ServiceLoader object in which there is a LazyIterator iterator class, which is located in the specific driver class that holds all the quotient implementations. When we iterate through the iterator LazyIterator, we start the logic of class loading.

Private S nextService () {if (! hasNextService ()) throw new NoSuchElementException (); String cn = nextName; nextName = null; Class c = null; try {/ / do not write Class.forName ("com.mysql.jdbc.Driver"); the reason is that the method c = Class.forName (cn, false, loader) is dynamically tuned here. } catch (ClassNotFoundException x) {fail (service, "Provider" + cn + "not found");} if (! service.isAssignableFrom (c)) {fail (service, "Provider" + cn + "not a subtype");} try {S p = service.cast (c.newInstance ()); providers.put (cn, p) Return p;} catch (Throwable x) {fail (service, "Provider" + cn + "could not be instantiated", x);} throw new Error (); / / This cannot happen}

Each traversal invokes the Class.forName (cn, false, loader) method to load and instantiate the specified class feed, which is why, as mentioned earlier, Class.forName ("com.mysql.jdbc.Driver"); is not written after jdk1.6.

In this custom Class.forName (cn, false, loader), the parameter cn is the full path class name, false refers to no initialization, and loader is the class loader that specifies the completion of cn class loading.

In the loader variable of the thread, we review the description of the "next". Did we get the thread up and down class loader in the ServiceLoader.load (Driver.class) method and pass it on?

Don't you remember? Look back and watch it again and again!

The "thread up and down" class loader is passed as a parameter to the constructor of the ServiceLoader class

Private ServiceLoader (Class svc, ClassLoader cl) {service = Objects.requireNonNull (svc, "Service interface cannot be null"); loader = (cl = = null)? ClassLoader.getSystemClassLoader (): cl; acc = (System.getSecurityManager ()! = null)? AccessController.getContext (): null; reload ();}

The cl variable here is the thread up and down class loader that adjusts the static method of the DriverManager class, that is, the load class loader.

In other words, through the static substitution method of the DriverManager class, it is implemented that the ServiceLoader class triggers the loading of the driver class of the quotient implementation located in classpath. As mentioned earlier, the ServiceLoader class is located in the java.util package and is loaded by the startup class loader, and the class loaded by the startup class loader actually implements a "delegate" class loader to load the driver class, which is suspected to be contrary to the parent delegation mechanism. The implementation of this function is the thread up and down class loader.

Thank you for your reading, the above is the content of "what is the JVM parent delegation model and SPI implementation principle". After the study of this article, I believe you have a deeper understanding of what the JVM parent delegation model and SPI implementation principle is, and the specific use needs to be verified in practice. Here is, the editor will push for you more related knowledge points of the article, welcome to follow!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report