[Copyright notice] Without the consent of the blogger, declined to reprint! (Please respect the original, the blogger reserves the right to pursue)

This post focuses on class loaders, and the source code listed in this article is based on the Java8 version, which may vary slightly. The main contents are as follows

The hierarchy of class loading mechanisms

Each “.java” extension class file is written to store the program logic that needs to be executed. These “.java” files are compiled by the Java compiler into a file named “.class”. The “.class” file holds the converted Java code and the virtual machine instructions. The virtual machine will load its “.class” file, create the corresponding class object, and load the class file into the VM memory. This process is called class loading. Here we need to understand the class loading process, as follows:

  • Loading: Phase of the Class loading process in which a bytecode file is found by the full qualification of a Class and a Class object is created from the bytecode file

  • Verification: The purpose is to ensure that the byte stream in the Class file meets the requirements of the VM and does not compromise vm security. It mainly includes four kinds of verification, file format verification, metadata verification, bytecode verification, symbol reference verification.

  • Preparation: Allocate memory for class variables and set their initial value to 0(e.g., static int I =5; Here will be initialized to zero, I only for 5 values will during initialization assignment), here do not include in the final of the static, because the final will be allocated at compile time, distribution of attention here not for instance variable initialization, class variables will distribute in the methods section, and an instance variable is allocated together with the object in the Java heap.

  • Parsing: The process of essentially replacing symbolic references in a constant pool with direct references. A symbolic reference is a set of symbols that describe a target, which can be any literal, while a direct reference is a pointer to a target, a relative offset, or a handle that is indirectly located to the target. There are class or interface resolution, field resolution, class method resolution, and interface method resolution (reference to bytecode variables is involved here, see In Depth Java Virtual Machine for more details).

  • Initialization: At the end of the class load phase, if the class has a superclass, it is initialized, performing a static initializer and statically initializing member variables (for example, static variables that previously initialized only default values will be assigned at this stage and member variables will be initialized).

The Class loader’s job is to read the binary byte stream of a Class based on its fully qualified name into the JVM and convert it into an instance of a java.lang.Class object corresponding to the target Class. The Bootstrap class loader, Extension class loader, and System class loader (also known as application class loader) are described separately below

The Bootstrap loader mainly loads the classes required by the JVM itself. This class loading is implemented in C++ language and is part of the virtual machine itself. It is responsible for loading the core class libraries in the

/lib directory or jar packages in the path specified by the -xbootclasspath parameter into the memory. Note that the JAR packages must be loaded according to the file name of the VM, such as rt.jar. If the file name is not recognized by the VM, the jar package must be loaded into the memory. Throwing jar packages into the lib directory won’t work (for security reasons, Bootstrap starts the classloader to load only classes whose package names start with Java, Javax, Sun, etc.).

The Extension class loader is a static inner class of the Sun.misc.Launcher$ExtClassLoader class implemented by The Java language. It is responsible for loading libraries in the

/lib/ext directory or in the bitpath specified by the system variable -djava.ext. dir. Developers can use the standard extension classloader directly.

// The code to get the path in the ExtClassLoader class
private static File[] getExtDirs() {
     // Load the class libraries in 
      
       /lib/ext
      
     String s = System.getProperty("java.ext.dirs");
     File[] dirs;
     if(s ! =null) {
         StringTokenizer st =
             new StringTokenizer(s, File.pathSeparator);
         int count = st.countTokens();
         dirs = new File[count];
         for (int i = 0; i < count; i++) {
             dirs[i] = newFile(st.nextToken()); }}else {
         dirs = new File[0];
     }
     return dirs;
 }Copy the code

System class loader

Also called application loader, it refers to Sun’s implementation of Sun.misc.Launcher$AppClassLoader. Java-classpath or -d java.class.path specifies the classpath path for the class libraries. Developers can use the system classloader directly, which is the default class loader. The class loader can be obtained by using the ClassLoader#getSystemClassLoader() method. In everyday Java application development, class loading is almost carried out by the above three types of loaders. If necessary, we can also customize the class loaders. It is important to note that the Java virtual machine is used to load class files on demand. That is, when a class is needed, its class file is loaded into memory to generate a class object, and when a class file is loaded, the Java virtual machine uses parent delegate mode to delegate requests to the parent class. It is a task delegate mode, which we will learn more about next.

Understand the parental delegation pattern

How the parent delegate pattern works

Parents delegate pattern requires that in addition to the start of the top class loader, the rest of the class loader should have its own parent class loader, please pay attention to the parents to model the father-son relationship is not usually what class inheritance relations, but the combination of reuse the code for the parent class loader, the relationship between class loader as follows:

Parents delegation model is introduced after the Java 1.2, its working principle is that if a class loader received class loading request, it will not go to loading, but the request entrusted to the parent class loader for execution, if the parent class loader has its parent class loader, further upward, in turn, recursion, Request will eventually reach the top to start the class loader, if the parent class loader finish class loading can be successfully returns, if the parent class loader cannot complete this task load, child loader will try to load, it is parents delegate pattern, namely every son is lazy, every time just throw it to the father to do, until my father said it also I can do, His son to find a way to complete, this is the legendary strength pit dad ah? So what’s the use of this model?

Advantages of parental delegation pattern

The advantage of parental delegation is that Java classes have a hierarchy of priorities with their classloaders. This hierarchy avoids reloading classes, and there is no need for the child ClassLoader to load the class again when the parent has already loaded the class. Second, for security reasons, defined types in the Java core API will not be arbitrarily replaced. Suppose that a class named java.lang.Integer is passed over the network through the parent delegate mode to the initiator class loader, and the initiator class loader finds the class with this name in the core Java API and finds that the class has been loaded. Instead of reloading java.lang.Integer passed by the network, integer.class is returned, which prevents the core API library from being tampered with. You might be thinking, what if we had a custom class named java.lang.SingleInterge in the classpath path (the class is made up)? The class does not exist in java.lang, and is passed to the startup class loader through the parent delegate mode. Since there is no class in the parent class loader path, it will not be loaded, and the reverse delegate to the subclass loader will be loaded, and eventually the class will be loaded through the system class loader. However, this is not allowed because java.lang is the core API package and requires access rights. Forced loading will result in the following exception

java.lang.SecurityException: Prohibited package name: java.langCopy the code

So it can’t be loaded successfully anyway. At the code level, let’s take a look at the implementation of several class loaders defined in Java and their parent delegate pattern, with the following class diagram relationship

As can be seen from the figure, the top ClassLoader is the ClassLoader class. It is an abstract class, and all subsequent classloaders inherit from ClassLoader (excluding the launcher ClassLoader). Here we mainly introduce several important methods in ClassLoader.

  • loadClass(String)

    This method loads binary types with specified names (including package names). This method is not recommended to be overridden after JDK1.2, but users can call this method directly. The loadClass() method is implemented by the ClassLoader class itself. LoadClass (String name, Boolean resolve) loadClass(String name, Boolean resolve) is an overloaded method.

      protectedClass<? > loadClass(String name,boolean resolve)
            throws ClassNotFoundException
        {
            synchronized (getClassLoadingLock(name)) {
                // Find the class object in the cacheClass<? > c = findLoadedClass(name);if (c == null) {
                    long t0 = System.nanoTime();
                    try {
                        if(parent ! =null) {
                            // If not, delegate to the parent class loader
                            c = parent.loadClass(name, false);
                        } else {
                        // If there is no parent class, delegate it to the boot loaderc = 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
                        // If none is found, use the custom implementation findClass to find and load
                        c = findClass(name);
    
                        // this is the defining class loader; record the statssun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); }}if (resolve) {// Whether parsing is required at load time
                    resolveClass(c);
                }
                returnc; }}Copy the code

    As the loadClass method shows, when a class load request comes in, it looks for the class object in the cache, returns it if it exists, passes it to the class’s parent loader if it doesn’t exist, passes it to the top start class loader if it doesn’t have a parent, and finally, if it doesn’t find one, The findClass() method is used to load it (more on findClass() later). If you do not want to redefine the rules for loading classes, and do not have complex logic, you just want to load your specified classes at runtime, then you can use this.getClass().getClassloder.loadClass (“className”). You can call the loadClass method of the ClassLoader directly to get the class object.

  • Before JDK1.2, findClass(String) used to inherit the ClassLoader class and rewrite the loadClass method to implement custom class loading classes. However, after JDK1.2, it is no longer recommended that users override the loadClass() method. FindClass () is called in the loadClass() method. If the parent loadClass() method fails to load, the findClass() method is called in the loadClass() method. It calls its own findClass() method to complete the class loading, ensuring that the custom classloader also conforms to the parent delegate pattern. Note that there is no specific code logic in the ClassLoader class that implements the findClass() method. Instead, it throws a ClassNotFoundException, It should also be noted that the findClass method is usually used with the defineClass method (more on that later). The findClass() method in the ClassLoader class is listed below:

      // Throw an exception directly
      protectedClass<? > findClass(String name)throws ClassNotFoundException {
              throw new ClassNotFoundException(name);
      }Copy the code
  • DefineClass (byte[] b, int off, int len) defineClass() is used to parse the byte stream into a Class object that the JVM can recognize (the logic for this method is implemented in the ClassLoader). Using this method, you can instantiate a class object not only through a class file, but also through other methods, such as receiving the bytecode of a class over the network and converting it into a byte stream to create the corresponding class object. The defineClass() method is usually used together with the findClass() method. In general, when a custom ClassLoader is defined, the findClass() method of the ClassLoader is overridden directly and the loading rules are written. The bytecode of the class to be loaded is obtained and converted into a stream. Then call defineClass() to generate the Class object, as shown in a simple example:

    Protected Class findClass(String name) throws ClassNotFoundException {// Get the Class's byte array byte[] classData = getClassData(name); if (classData == null) { throw new ClassNotFoundException(); } else {return defineClass(name, classData, 0, classdata.length); }}Copy the code

    Note that if you call defineClass() directly to generate a Class object, the Class object is not parsed (or linked, because parsing is the last step of linking) and the parsing will have to wait for initialization.

  • ResolveClass (Class ≺? ≻ c) using this method, the Class object of the Class can be created and parsed simultaneously. Earlier we said that the linking phase is mainly about verifying the bytecode, allocating memory and setting initial values for class variables and converting symbolic references in the bytecode file to direct references.

The above four methods are the most important ones in the ClassLoader class, and they’re the ones we’ll probably use a lot. SercureClassLoader extends the ClassLoader to include several methods for using code sources (the location of the source and its certificate) and permission definition class validation (access to the source code). As mentioned earlier, ClassLoader is an abstract class, and many methods are empty and unimplemented, such as findClass() and findResource(). The URLClassLoader implementation Class provides concrete implementation for these methods, and adds the URLClassPath Class to assist in obtaining Class bytecode streams and other functions. When writing custom Class loaders, if there is no too complex requirements, you can directly inherit the URLClassLoader Class. This saves you from having to write your own findClass() method and how it fetches the bytecode stream, making it simpler to write a custom class loader.

The URLClassPath class is responsible for finding the bytecode stream to load, and then reading the byte stream. Finally, the Class object of the Class is created using the defineClass() method. As can be seen from the structure diagram of the URLClassLoader class, its constructors have a parameter URL[] that must be passed. The element of this parameter is the path of the bytecode file. In other words, when creating the URLClassLoader object, the class loader must be specified to find the class file in the directory. It should also be noted that URL[] is also a mandatory parameter of the URLClassPath class. When creating URLClassPath object, the path in the URL array will determine whether it is a file or jar package. A FileLoader or JarLoader or default Loader class is then created to load the class file in the corresponding path. When the JVM calls findClass(), one of the three loaders loads the bytecode stream of the class file into memory. Finally, the bytecode stream is used to create the class object of the class. Keep in mind that if we choose to inherit the ClassLoader class instead of URLClassLoader when defining the ClassLoader, we must manually write the loading logic for the findClass () method and the logic to retrieve the bytecode stream. After learning about URLClassLoader, we can look at the remaining two class loaders, namely the extension class loader ExtClassLoader and the system class loader AppClassLoader, which both inherit from URLClassLoader. Is a static inner class for sun.misc.Launcher. Sun.misc.Launcher is mainly used by the system to start the main application. ExtClassLoader and AppClassLoader are created by sun.misc.Launcher.

The relationship between them is as described above, and we find that ExtClassLoader does not overwrite the loadClass() method, which is enough to say that it follows parental delegation, while AppClassLoader overrides the loadCass() method. However, the parent loadClass() method is still called, so the parent delegate mode is still observed.

/** * Override loadClass; */ public Class loadClass(String name, boolean resolve) throws ClassNotFoundException { int i = name.lastIndexOf('.'); if (i ! = -1) { SecurityManager sm = System.getSecurityManager(); if (sm ! = null) { sm.checkPackageAccess(name.substring(0, i)); }} // return (super.loadClass(name, resolve)); }Copy the code

The fact that both ExtClassLoader and AppClassLoader inherit from URLClassLoader is unarguable, so they both adhere to the parent delegate model. Ok ~, so far we have a clear understanding of the relationship between ClassLoader, URLClassLoader, ExtClassLoader, AppClassLoader and Launcher classes, and also have a certain understanding of some major methods. There is no detailed analysis of the source code for these classes. After all, it is not necessary, because we mainly learn about the relationships between classes and common methods and also understand the implementation of the parent delegate pattern, which is enough to pave the way for writing custom class loaders. The parent class of each class loader is the same as the parent class of each class loader. The parent class of each class loader is the same as the parent class of each class loader.

Relationships between class loaders

Let’s take a closer look at the relationships between class loaders (rather than inheritance relationships), which can be divided into four main points

  • Start class loader, implemented by C++, no parent class.

  • ExtClassLoader (ExtClassLoader), implemented by Java language, the parent class loader is null

  • System class loader (AppClassLoader), implemented by the Java language, the parent class loader for ExtClassLoader

  • Custom class loader. The parent class loader must be AppClassLoader.

Let’s verify the above point of view through the program

/ * * * Created by zejian on 2017/6/18. * Blog: http://blog.csdn.net/javazejian [the original address, please respect the original] * /
// Custom ClassLoader, complete code will be analyzed later
class FileClassLoader extends  ClassLoader{
    private String rootDir;

    public FileClassLoader(String rootDir) {
        this.rootDir = rootDir;
    }
    // Write the logic to get the bytecode of the class and create the class object
    @Override
    protectedClass<? > findClass(String name)throws ClassNotFoundException {
       / /... Omit logical code
    }
    // Write a method to read the byte stream
    private byte[] getClassData(String className) {
        // Read the bytes of the class file
        // omit the code....}}public class ClassLoaderTest {

    public static void main(String[] args) throws ClassNotFoundException {

             FileClassLoader loader1 = new FileClassLoader(rootDir);

              System.out.println("Parent loader of custom class loader:"+loader1.getParent());
              System.out.println("Default AppClassLoader:"+ClassLoader.getSystemClassLoader());
              System.out.println("Parent of AppClassLoader:"+ClassLoader.getSystemClassLoader().getParent());
              System.out.println("Parent of ExtClassLoader:"+ClassLoader.getSystemClassLoader().getParent().getParent());

            /** The parent loader of the custom class loader: sun.misc.Launcher$AppClassLoader@29453f44 The default AppClassLoader of the system: Sun.misc.Launcher$AppClassLoader@29453f44 Parent of AppClassLoader: Sun.misc.Launcher$ExtClassLoader@6f94fa3e Parent of ExtClassLoader: null */}}Copy the code

In this code, we define a custom FileClassLoader. We inherit the ClassLoader instead of the URLClassLoader, so we need to write our own findClass() method logic and the logic to load the bytecode. We’ll look at the custom ClassLoader later. Just need to know is FileClassLoader custom loader, then in the main method, and through this. GetSystemClassLoader () get to the default system class loader, through access to its parent class loader and its FuFu class loader, We also get the parent of the custom class loader. The output is as expected: The parent of AppClassLoader is ExtClassLoader, and ExtClassLoader does not have a parent. If we implemented our own class loader, its parent loader would only be AppClassLoader. Here we can look at Lancher’s constructor source code

public Launcher(a) {
        // Create the extension class loader first
        ClassLoader extcl;
        try {
            extcl = ExtClassLoader.getExtClassLoader();
        } catch (IOException e) {
            throw new InternalError(
                "Could not create extension class loader");
        }

        // Now create the class loader to use to launch the application
        try {
            // Create AppClassLoader and pass extCL as parent to AppClassLoader
            loader = AppClassLoader.getAppClassLoader(extcl);
        } catch (IOException e) {
            throw new InternalError(
                "Could not create application class loader");
        }

        // Set the thread context classloader, which will be analyzed later
        Thread.currentThread().setContextClassLoader(loader);
// Omit other unnecessary code......}}Copy the code

Obviously Lancher initializes the ExtClassLoader, then creates the AppClassLoader and passes the ExtClassLoader to it as the parent class loader, and sets the AppClassLoader to the thread context class loader by default. The thread context class loader will be analyzed later. Why is the ExtClassLoader null? The ExtClassLoader is forced to set its parent to NULL.

// Create ExtClassLoader in Lancher
extcl = ExtClassLoader.getExtClassLoader();

/ / getExtClassLoader () method
public static ExtClassLoader getExtClassLoader(a) throws IOException{

  / /... Omit other code
  return new ExtClassLoader(dirs);                     
  / /...
}

// constructor
public ExtClassLoader(File[] dirs) throws IOException {
   // Call the parent class to construct URLClassLoader and pass null as parent
   super(getExtURLs(dirs), null, factory);
}

/ / URLClassLoader structure
public URLClassLoader(URL[] urls, ClassLoader parent, URLStreamHandlerFactory factory) {Copy the code

The parent of ExtClassLoader is null, and the parent of AppClassLoader is ExtClassLoader. All custom class loaders have AppClassLoader as their parent. Note that the parent class is not the parent class in Java inheritance.

Classes and class loaders

Classes and class loaders

There are two requirements in the JVM to indicate whether two class objects are the same class object

  • The full class name of the class must be consistent, including the package name.
  • The ClassLoader(ClassLoader instance object) that loads this class must be the same.

That is, even if two class objects come from the same class file and are loaded by the same VIRTUAL machine in a JVM, they are not equal as long as the ClassLoader instance that loaded them is different. This is because different ClassLoader instances have different independent class namespaces, so the loaded class objects also exist in different class namespaces, but only if the loadClass method is overridden, as shown in the source code of the loadClass() method. In the first step of the method, the Class
c = findLoadedClass(name); From the cache, a class with the same full name will not be loaded again, so we must bypass the cache query to reload the class object. You can also call the findClass() method directly to avoid a lookup from the cache, as follows

String rootDir="/Users/zejian/Downloads/Java8_Action/src/main/java/";
// Create two different instances of custom classloaders
FileClassLoader loader1 = new FileClassLoader(rootDir);
FileClassLoader loader2 = new FileClassLoader(rootDir);
// Create a Class object with findClassClass<? > object1=loader1.findClass("com.zejian.classloader.DemoObj"); Class<? > object2=loader2.findClass("com.zejian.classloader.DemoObj");

System.out.println("findClass->obj1:"+object1.hashCode());
System.out.println("findClass->obj2:"+object2.hashCode());

FindClass ->obj1:723074861 findClass->obj2:895328852 generates different instances */Copy the code

If you call the parent loadClass method, the result is the following, unless you override the loadClass() method to remove the cache lookup step, which is generally not recommended these days.

// Call the loadClass() method of the parent directlyClass<? > obj1 =loader1.loadClass("com.zejian.classloader.DemoObj"); Class<? > obj2 =loader2.loadClass("com.zejian.classloader.DemoObj");

// Custom class loaders for different instance objects
System.out.println("loadClass->obj1:"+obj1.hashCode());
System.out.println("loadClass->obj2:"+obj2.hashCode());
// System class loader
System.out.println("Class->obj3:"+DemoObj.class.hashCode());

* loadClass->obj1:1872034366 loadClass->obj2:1872034366 loadClass->obj2:1872034366 Obj3 :1872034366 is the same instance */Copy the code

Therefore, if you do not query a class object with the same exact class name from the cache, the class object created by the same bytecode file will not be the same unless the instance object of the ClassLoader is different.

Understand the concepts of explicit and implicit loading of class files

Explicit loading and implicit loading of class files refer to how the JVM loads class files into memory. Explicit loading refers to loading class objects in code by calling the ClassLoader. For example, use class.forname (name) or this.getClass().getClassLoader().loadClass() directly to load the Class object. Implicit loading means that class objects are not directly loaded by calling the ClassLoader method in the code, but are automatically loaded into the memory by the VM. For example, when the class file of a class is loaded, the class file of that class references the object of another class. In this case, the extra referenced classes are automatically loaded into the memory by the JVM. In daily development, these two methods are usually mixed, and we know that this is the case.

Write your own class loader

To implement a custom ClassLoader, you need to inherit the ClassLoader or URLClassLoader. To inherit the ClassLoader, you need to rewrite the findClass() method and write the loading logic. Inheriting URLClassLoader saves writing findClass() methods and converting class file loads into bytecode streams. So what’s the point of writing a custom classloader?

  • When the class file is not in the ClassPath path, the default system class loader cannot find the class file. In this case, we need to implement a custom ClassLoader to load the class file in the specific path to generate class objects.

  • When a class file is transferred over the network and may be encrypted, the class file needs to be decrypted and loaded into the JVM memory. In this case, you need to write a custom ClassLoader and implement the corresponding logic.

  • When hot deployment is required (a class file generates different class objects through different classloaders to achieve hot deployment), the logic of custom ClassLoader needs to be implemented.

Custom File class loader

Here we inherit ClassLoader to implement custom specific path under the file class loader and load the compiled demoobj.class, the source code is as follows

public class DemoObj {
    @Override
    public String toString(a) {
        return "I am DemoObj"; }}Copy the code
package com.zejian.classloader;

import java.io.*;

/** * Created by zejian on 2017/6/21. */
public class FileClassLoader extends ClassLoader {
    private String rootDir;

    public FileClassLoader(String rootDir) {
        this.rootDir = rootDir;
    }

    /** * write the logic of the findClass method *@param name
     * @return
     * @throws ClassNotFoundException
     */
    @Override
    protectedClass<? > findClass(String name)throws ClassNotFoundException {
        // Get the class file byte array of the class
        byte[] classData = getClassData(name);
        if (classData == null) {
            throw new ClassNotFoundException();
        } else {
            // Generate the class object directly
            return defineClass(name, classData, 0, classData.length); }}/** * write the logic that takes the class file and converts it to a bytecode stream *@param className
     * @return* /
    private byte[] getClassData(String className) {
        // Read the bytes of the class file
        String path = classNameToPath(className);
        try {
            InputStream ins = new FileInputStream(path);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            int bufferSize = 4096;
            byte[] buffer = new byte[bufferSize];
            int bytesNumRead = 0;
            // Read the bytecode of the class file
            while((bytesNumRead = ins.read(buffer)) ! = -1) {
                baos.write(buffer, 0, bytesNumRead);
            }
            return baos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    /** * full path to class file *@param className
     * @return* /
    private String classNameToPath(String className) {
        return rootDir + File.separatorChar
                + className.replace('. ', File.separatorChar) + ".class";
    }

    public static void main(String[] args) throws ClassNotFoundException {
        String rootDir="/Users/zejian/Downloads/Java8_Action/src/main/java/";
        // Create a custom file classloader
        FileClassLoader loader = new FileClassLoader(rootDir);

        try {
            // Load the specified class fileClass<? > object1=loader.loadClass("com.zejian.classloader.DemoObj");
            System.out.println(object1.newInstance().toString());

            // Output result :I am DemoObj
        } catch(Exception e) { e.printStackTrace(); }}}Copy the code

Obviously we found the class file using the getClassData() method and converted it to a byte stream, rewrote the findClass() method and created the class object using the defineClass() method. In the main method, the loadClass() method is called to load the class file in the specified path. Since the boot class loader, extension class loader, and system class loader cannot find the class file in its path, the custom class loader is finally loaded by calling the findClass() method. If you inherit the URLClassLoader implementation, the code is much more concise as follows:

/** * Created by zejian on 2017/6/21. */
public class FileUrlClassLoader extends URLClassLoader {

    public FileUrlClassLoader(URL[] urls, ClassLoader parent) {
        super(urls, parent);
    }

    public FileUrlClassLoader(URL[] urls) {
        super(urls);
    }

    public FileUrlClassLoader(URL[] urls, ClassLoader parent, URLStreamHandlerFactory factory) {
        super(urls, parent, factory);
    }


    public static void main(String[] args) throws ClassNotFoundException, MalformedURLException {
        String rootDir="/Users/zejian/Downloads/Java8_Action/src/main/java/";
        // Create a custom file classloader
        File file = new File(rootDir);
        //File to URI
        URI uri=file.toURI();
        URL[] urls={uri.toURL()};

        FileUrlClassLoader loader = new FileUrlClassLoader(urls);

        try {
            // Load the specified class fileClass<? > object1=loader.loadClass("com.zejian.classloader.DemoObj");
            System.out.println(object1.newInstance().toString());

            // Output result :I am DemoObj
        } catch(Exception e) { e.printStackTrace(); }}}Copy the code

It’s pretty neat without having to write byte stream conversion logic for the findClass() method and its class file except to override the constructor.

Custom network class loaders

Custom network class loader, mainly used to read the class file passed over the network (here we omit the decryption process of the class file), and convert it into byte stream to generate the corresponding class object, as follows

/** * Created by zejian on 2017/6/21. */
public class NetClassLoader extends ClassLoader {

    private String url;// The URL of the class file

    public NetClassLoader(String url) {
        this.url = url;
    }

    @Override
    protectedClass<? > findClass(String name)throws ClassNotFoundException {
        byte[] classData = getClassDataFromNet(name);
        if (classData == null) {
            throw new ClassNotFoundException();
        } else {
            return defineClass(name, classData, 0, classData.length); }}/** * Get class file * from network@param className
     * @return* /
    private byte[] getClassDataFromNet(String className) {
        String path = classNameToPath(className);
        try {
            URL url = new URL(path);
            InputStream ins = url.openStream();
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            int bufferSize = 4096;
            byte[] buffer = new byte[bufferSize];
            int bytesNumRead = 0;
            // Read the bytes of the class file
            while((bytesNumRead = ins.read(buffer)) ! = -1) {
                baos.write(buffer, 0, bytesNumRead);
            }
            // The decryption process is omitted here.......
            return baos.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    private String classNameToPath(String className) {
        // Get the URL of the class file
        return url + "/" + className.replace('. '.'/') + ".class"; }}Copy the code

Get the byte stream directly from the network, then transfer the byte array and create a class object using the defineClass method. If you inherit the URLClassLoader class, the implementation is similar to the above file path. You don’t have to worry about whether the path is a filePath or a Url, because the URLClassPath object in the URLClassLoader determines whether it is a file or a JAR based on the path in the Url array that is passed. Then create FileLoader or JarLoader or the default class Loader according to the different path to read the appropriate path or URL under the class file.

Hot deployed class loaders

Hot deployment is when two different class objects are created in memory using different classloaders for the same class file (for reasons discussed earlier, using different classloading instances). Since the JVM checks whether the requested class has been loaded before loading it (that is, calling the findLoadedClass() method in the loadClass() method), if it has been loaded, it is fetched directly from the cache without being reloaded. Note that instances of the same class loader and the same class file can only be loaded once by the loader. Loading multiple times will result in an error. Therefore, we must implement hot deployment so that the same class file can be reloaded according to different class loaders to achieve the so-called hot deployment. The previous implementations of FileClassLoader and FileUrlClassLoader already do this, but only if they call the findClass() method directly instead of the loadClass() method. The findLoadedClass() method is called in the loadClass() body of the ClassLoader to check whether it has been loaded, so we can bypass this problem by calling the findClass() method. But this is strongly discouraged. Use FileClassLoader to test the following code:

 public static void main(String[] args) throws ClassNotFoundException {
        String rootDir="/Users/zejian/Downloads/Java8_Action/src/main/java/";
        // Create a custom file classloader
        FileClassLoader loader = new FileClassLoader(rootDir);
        FileClassLoader loader2 = new FileClassLoader(rootDir);

        try {
            // To load the specified class file, call loadClass()Class<? > object1=loader.loadClass("com.zejian.classloader.DemoObj"); Class<? > object2=loader2.loadClass("com.zejian.classloader.DemoObj");

            System.out.println("loadClass->obj1:"+object1.hashCode());
            System.out.println("loadClass->obj2:"+object2.hashCode());

            // Load the specified class file, call findClass() directly, bypass the detection mechanism, create different class objects.Class<? > object3=loader.findClass("com.zejian.classloader.DemoObj"); Class<? > object4=loader2.findClass("com.zejian.classloader.DemoObj");

            System.out.println("loadClass->obj3:"+object3.hashCode());
            System.out.println("loadClass->obj4:"+object4.hashCode());

            /** * The following output is displayed: * loadClass->obj1:644117698 loadClass->obj2:644117698 findClass->obj3:723074861 findClass->obj4:895328852 */

        } catch(Exception e) { e.printStackTrace(); }}Copy the code

Destroyers of the parent delegate model – thread context classloaders

There are many Service Provider interfaces (SPI) in Java applications, which allow third parties to provide implementations for them. Common SPI include JDBC, JNDI, etc. The interfaces of these SPI belong to Java core libraries. Generally, it is stored in the rt.jar package loaded by the Bootstrap class loader, while the third-party implementation code of SPI is stored in the classpath path as the JAR package that Java applications depend on. Since the code in the SPI interface often needs to load specific third-party implementation classes and call their related methods, However, the core interface class of SPI is loaded by the Bootstrap class loader, while the Bootstrap class loader cannot directly load the implementation class of SPI, and due to the existence of parental delegation mode, the Bootstrap class loader cannot reverse delegate the implementation class of SPI of the AppClassLoader. In this case, we need a special class loader to load third-party libraries, and thread-context class loaders are a good choice. The contextClassLoader was introduced in JDK 1.2, We can get and set the context ClassLoader for the Thread through the getContextClassLoader() and setContextClassLoader(ClassLoader CL) methods in the java.lang.Thread class. If the context class loader is not set manually, the thread inherits its parent thread’s context class loader. The context class loader of the initial thread is the System Class loader (AppClassLoader). Code running in the thread can use this kind of loader to load classes and resources, as shown in the following figure, using jdbc.jar loading as an example

It can be seen from the figure that the rt.jar core package is loaded by the Bootstrap class loader, which contains SPI core interface classes. Since classes in SPI often need to call methods of external implementation classes, Jdbc.jar containing external implementation classes (jdbc.jar exists in the classpath path) cannot be loaded by the Bootstrap class loader, so you have to delegate the thread context class loader to load the implementation classes in jdbc.jar into memory for use by spI-related classes. Obviously, this thread-context classloader loading method breaks the “parent delegate model” by abandoning the parent delegate chain mode during execution, allowing programs to use class loaders in reverse, and of course making Java classloaders more flexible. To further verify this scenario, take a look at the source code for DriverManager, a class in the Java core Rt. jar package that manages implementation drivers for different databases, called drivers. Driver (com.mysql.jdbc.driver); Driver (com.mysql.jdbc.driver)

//DriverManager is a class of the Java core package rt.jar
public class DriverManager {
    // Omit unnecessary code
    static {
        loadInitialDrivers();// Execute the method
        println("JDBC DriverManager initialized");
    }

/ / loadInitialDrivers method
 private static void loadInitialDrivers(a) {
     sun.misc.Providers()
     AccessController.doPrivileged(new PrivilegedAction<Void>() {
            public Void run(a) {
                // Load the external Driver implementation class
                ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
              // Omit unnecessary code......}}); }Copy the code

The loadInitialDrivers() method is executed when the DriverManager class is initialized, where serviceloader.load (driver.class) is passed; To load the externally implemented driver class, the ServiceLoader class reads the meta-INF file in mysql jdbc.jar, as shown below

Com.mysql.jdbc.driver inherits the following classes:

public class Driver extends com.mysql.cj.jdbc.Driver { public Driver() throws SQLException { super(); } static { System.err.println("Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver'. " + "The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary."); }}Copy the code

Com.mysql.jdbc.driver is deprecated and replaced with com.mysql.cj.jdbc.driver. It is no longer recommended to register the mysql Driver with the following code

// It is not recommended to register driver classes in this way
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/cm-storylocker? characterEncoding=UTF-8";
// Get the database connection from the Java library
Connection conn = java.sql.DriverManager.getConnection(url, "root"."root@555");Copy the code

Instead, simply remove the registration step as follows

String url = "jdbc:mysql://localhost:3306/cm-storylocker? characterEncoding=UTF-8";
// Get the database connection from the Java library
Connection conn = java.sql.DriverManager.getConnection(url, "root"."root@555");Copy the code

So the ServiceLoader will take care of everything and eventually load it with the load() method. Look at the load() method implementation

public static ServiceLoader load(Class service) {
     // Through the thread context classloader
      ClassLoader cl = Thread.currentThread().getContextClassLoader();
      return ServiceLoader.load(service, cl);
  }Copy the code

It is obvious that the SPI class of the core package is actually loaded from the thread context class loader to the external implementation class, which is how the Java core code calls the external implementation class from within. We know that the thread-context classloader is AppClassLoader by default, so why not just get the classloader from getSystemClassLoader() to load classes in the classpath path? This works, but there is a drawback to using getSystemClassLoader() directly to get the AppClassLoader loading classes. That is, the code will have problems when deployed to different services, such as Java Web application services or SERVICES such as EJBs. The thread-context class loaders used by these services are not AppClassLoaders, but Java Web applications’ own class loaders, which are different. , so we should use getSystemClassLoader() less. In general, different services may use different default Classloaders, but using a thread-context ClassLoader can always get the same ClassLoader that the current program executes, thus avoiding unnecessary problems. Ok ~. The thread context class loader for the moment, the DriverManager class, you can see the source code, I believe you will have more experience, in addition to the ServiceLoader is not too much explained, after all, our topic is the class loader, But ServiceLoader is a great decoupling mechanism that you can check out for yourself.

Ok ~, this is the end of this paragraph, if there is any mistake, welcome to leave a message, thank you.

Resources: blog.csdn.net/yangcheng33… Ifeve.com/wp-content/…

In-depth Understanding of JVM Virtual Machines In-depth Analysis of Java Web Technology