Before we look at class loaders in Tomcat and why Tomcat is implementing its own class loader that breaks the parental delegation model, we first need to know what class loaders are defined in Java and what the parental delegation model is.

Class loaders in Java

The classloader is responsible for dynamically loading Java files into the JVM while the program is running

From a Java virtual machine perspective, there are two different class loaders:

  • Bootstrap ClassLoader: this ClassLoader is implemented in C++ and is part of the virtual machine itself.

  • Other classloaders: These classloaders are implemented in the Java language, independent of the virtual machine, and all inherit from the abstract java.lang.ClassLoader class, which the other classloaders are presumably divided into

    • ExtensionClassLoader: This class loader is created byExtClassLoaderImplementation, which is responsible for loadingJAVA_HOME/lib/extAll classes in the directory, or byjava.ext.dirAll classes in the path specified by the system variable.
    • ApplicationClassLoader: This class loader is created byAppClassLoaderIt is responsible for loading all classes specified on the user’s ClassPath. If the application does not have its own custom class loader, it is generally the default class loader.
    • Custom loader: A customized loader that loads specific paths based on your own requirements.

For any class, its uniqueness within the Java virtual machine needs to be established both by the classloader that loads it and by the class itself

Parental delegation model

The hierarchy shown in the figure above is called the parent delegate model of the class loader. The parent delegate model requires that all loaders have their own parent loader except for the top-level boot class loader. The parent-child relationship is implemented not by inheritance, but by setting the parent variable.

Parents delegation model work process is: if you receive a class loading request, itself does not loading such first, but will delegate the request to the parent class loader to complete, each level, until the start in the class loader, only the parent class to load the file, then subclass will try to load.

Why set up the parent delegate model? For example, the Object class is stored in rt.jar. No matter which class loader loads the Object class, it will be delegated to the top-level BootStrapClassLoader. Therefore, all classes use the same Object class. Conversely, without the parent delegate model, any class loader could define a new Object class, and the application would be very messy. The parent delegate model code is pretty simple. Implemented in the loadClass method of ClassLoader.

protected Class<? > loadClass(String name, Boolean resolve) throws ClassNotFoundException {synchronized (getClassLoadingLock(name)) {// First, check whether the request Class has been loaded Class<? > c = findLoadedClass(name);if(c == null) { long t0 = System.nanoTime(); Try {// Delegate to the parent class if it has not been loaded by the class loaderif(parent ! = null) { c = parent.loadClass(name,false);
                } else{// If there is no parent class, it means that BootS is at the top leveltrapC = findBootstrapClassOrNull(name); } // If the topmost class is not found, a catch will be thrown (ClassNotFoundException e) {} // If the parent class has not loaded the class, the subclass will start loading the classif(c == null) { c = findClass(name); }}if (resolve) {
            resolveClass(c);
        }
        returnc; } } protected Class<? > findClass(String name) throws ClassNotFoundException { throw new ClassNotFoundException(name); }Copy the code

We can see that the findClass method is logic that subclasses need to implement themselves.

Class loaders in Tomcat

The following diagram is a diagram of Tomcat’s classloader as shown in the official documentation for Tomcat9.


      Bootstrap
          |
       System
          |
       Common
       /     \
  Webapp1   Webapp2 ..

Copy the code
  • Bootstrap: is the highest Java loader. Implemented in C, Bootstrap is used to load core classes that the JVM needs to start, for example$JAVA_HOME/jre/lib/extClasses under paths.
  • System: the System will be loadedCLASSPATHAll classes of the path defined by the system variable.
  • Common: all classes in the lib file in the Tomcat path will be loaded.
  • Webapp1 Webapp2… : loads all classes in the project in the WebApp path. Each project corresponds to a WebappClassLoader, thus achieving class isolation between applications.

All three are represented in the Java parental delegation model diagram above. Bootstrap is merged with the ExtClassLoader. The ExtClassLoader is loaded under JAVA_HOME/jre/lib. So why did Tomcat customize the classloader?

  • Isolate different applications: different applications A and B are deployed in the same Tomcat, for example, A uses Spring2.5. B uses Spring3.5, so if the two applications are using the same classloader, the Web application will not start due to jar package overwriting.
  • Flexibility: Class loaders between Web applications are independent of each other, so it is possible to replace the original class loaders by rebuilding them for different files. This does not affect other applications.
  • Performance: If you have multiple applications deployed in Tomcat, they all have the same library dependencies. The same library can then be loaded by the Common class loader.

Tomcat customizes the WebAppClassLoader. The parent delegate mechanism is broken, that is, if a class load request is received, it will try to load it itself, and if it cannot find the parent loader to load, the purpose is to load the class defined by the Web application first. The default loadClass method of the WebAppClassLoader is to load classes in the parent delegate model, so Tomcat should override the loadClass method to break this rule.

@Override public Class<? > loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { Class<? > clazz = null; Clazz = findLoadedClass0(name);if(clazz ! = null) {if (log.isDebugEnabled())
                log.debug(" Returning class from cache");
            if (resolve)
                resolveClass(clazz);
            returnclazz; Clazz = findLoadedClass(name);if(clazz ! = null) {if (log.isDebugEnabled())
                log.debug(" Returning class from cache");
            if (resolve)
                resolveClass(clazz);
            return clazz;
        }

        String resourceName = binaryNameToPath(name, false); JavaseLoader = getJavaseClassLoader(); // 3. Use the ExtClassLoader to load classes to prevent Web applications from overwriting the core JRE classes. boolean tryLoadingFromJavaseLoader; try { URL url;if(securityManager ! = null) { PrivilegedAction<URL> dp = new PrivilegedJavaseGetResource(resourceName); url = AccessController.doPrivileged(dp); }else{ url = javaseLoader.getResource(resourceName); } tryLoadingFromJavaseLoader = (url ! = null); } catch (Throwable t) { tryLoadingFromJavaseLoader =true;
        }

        boolean delegateLoad = delegate || filter(name, true); // 4. Check whether the delegate attribute is set, if sotrueThen load the class according to the parent delegate mechanismif (delegateLoad) {
            if (log.isDebugEnabled())
                log.debug(" Delegating to parent classloader1 " + parent);
            try {
                clazz = Class.forName(name, false, parent);
                if(clazz ! = null) {if (log.isDebugEnabled())
                        log.debug(" Loading class from parent");
                    if (resolve)
                        resolveClass(clazz);
                    returnclazz; } } catch (ClassNotFoundException e) { // Ignore } } // 5. The default is set delegate isfalse, then the WebAppClassLoader will be loaded firstif (log.isDebugEnabled())
            log.debug(" Searching local repositories");
        try {
            clazz = findClass(name);
            if(clazz ! = null) {if (log.isDebugEnabled())
                    log.debug(" Loading class from local repository");
                if (resolve)
                    resolveClass(clazz);
                returnclazz; } } catch (ClassNotFoundException e) { // Ignore } // 6. If the class is not found in WebAppClassLoader at this point, delegate the load to AppClassLoaderif(! delegateLoad) {if (log.isDebugEnabled())
                log.debug(" Delegating to parent classloader at end: " + parent);
            try {
                clazz = Class.forName(name, false, parent);
                if(clazz ! = null) {if (log.isDebugEnabled())
                        log.debug(" Loading class from parent");
                    if (resolve)
                        resolveClass(clazz);
                    return clazz;
                }
            } catch (ClassNotFoundException e) {
                // Ignore
            }
        }
    }
    throw new ClassNotFoundException(name);
}

Copy the code

Finally, the words on Tomcat’s official website are summarized as follows:

The default class loading order for Web applications is (breaking the parent delegate rule) :

  1. Load it first from the JVM’s BootStrapClassLoader.
  2. Load the Web application/WEB-INF/classesIn the class.
  3. Load the Web application/WEB-INF/lib/*.japClass in the JAR package in.
  4. Load the classes below the System path defined above.
  5. Load the classes below the Common path defined above.

If
is configured in the configuration file, then the parent delegate rule is followed and the load order is as follows:

  1. Load it first from the JVM’s BootStrapClassLoader.
  2. Load the classes below the System path defined above.
  3. Load the classes below the Common path defined above.
  4. Load the Web application/WEB-INF/classesIn the class.
  5. Load the Web application/WEB-INF/lib/*.japClass in the JAR package in.

The articles

How to debug Tomcat source code breakpoint

Tomcat Series 1 — Overall architecture

Tomcat series 2 – EndPoint source code parsing

Tomcat series (3) — How does Tomcat start and stop with one click

A weird trip to find StackOverflowError problems

A simple RPC framework for hand – to – hand

A Simple RPC Framework (2) – Project transformation

Refer to the article

  • Tomcat.apache.org/tomcat-9.0-…
  • In-depth understanding of the Java virtual machine
  • Take apart Tomcat in depth
  • Kyfxbl.iteye.com/blog/170723…