Java’s Parental Delegation Model
Three-layer class loader for JDK8 and previous versions
-
Bootstrap Class Loader
Realized by C++ language, HotSpot is a part of the virtual machine itself. This class loader is responsible for loading files stored in the
directory, or in the path specified by the\lib -Xbootclasspath
parameter, and is recognized by the Java virtual machine. (identified by file name, such asrt.jar
,tools.jar
, class libraries with inconsistent names will not be loaded even if they are placed in the lib directory) class libraries Loaded into the virtual machine’s memory. When loaded through this class,null
is returned whenclassloader
is obtained in java code -
Extension Class Loader
This class loader is implemented in the form of Java code in the classsun.misc.Launcher$ExtClassLoader
. It is responsible for loading all class libraries in the
directory, or in the path specified by the\lib\ext java.ext.dirs
system variable.
After JDK9, the modularization mechanism was introduced, and this class withdrew from the stage of history. -
Application Class Loader
This class loader is implemented bysun.misc.Launcher$AppClassLoader
. Since the application class loader is the return value of thegetSystemClassLoader()
method in theClassLoader
class, it is also called the “system class loader” in some occasions. It is responsible for loading all class libraries on the user class path (ClassPath), and developers can also use this class loader directly in the code. If the application has not customized its own class loader, generally this is the default class loader in the program. -
Custom class loader
Users can declare a class loader in their java code, but the parent loader can only be “Application Class Loader”. This may causeClassNotFoundException
Parents Delegation Model, the parent delegation model requires that in addition to the top-level startup class loader, all other class loaders should have their own parent class loaders. However, the parent-child relationship between class loaders here is generally not implemented through inheritance. Instead, the composition relationship is usually used to reuse the code of the parent loader.
ClassLoader class loading process
The specific class loading in the Java code is done through the loadClass
method in the Class
class
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {<!-- --> synchronized (getClassLoadingLock(name)) {<!-- --> // First, check if the class has already been loaded // Find whether the class exists from the jvm cache, this place should be the cache inside the thread Class<?> c = findLoadedClass(name); if (c == null) {<!-- --> try {<!-- --> if (parent != null) {<!-- --> c = parent.loadClass(name, false); } else {<!-- --> c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) {<!-- --> // ClassNotFoundException thrown if class not found // from the non-null parent class loader } // Some irrelevant code if (c == null) {<!-- --> // This method is empty in the ClassLoder class and needs to be implemented by subclasses, that is c = findClass(name); } } if (resolve) {<!-- --> // jdk11 this method is useless. resolveClass(c); } return c; } }
- lookup from vm cache
- Search from the parent class loader, if the parent class loader is empty, then search from
Bootstrap Class Loader
- Find from your own
findClass
overloaded method - Throw an exception
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. Classes at each level This is true for loaders, so all load requests should eventually be transmitted to the top-level startup class loader, and only when the parent loader feedbacks that it cannot complete the load request (the required class is not found in its search scope) , the subloader will try to complete the loading by itself.
Tomcat’s class loading model
Common Class Loader
The public class loader located at the top level of Tomcat architecture. The parent loader is Java’s Application Class Loader
. By default, it reads common in the
attribute, the default value is: conf/catalina.properties
configuration file. .loader${catalina.base}/lib","${catalina.base}/lib/*.jar","${catalina.home }/lib","${catalina.home}/lib/*.jar
Catalina Class Loader
With Common Class Loader
as the parent loader, it is used to load the class loader of the tomcat application server. By default, the server in the
attribute, the default value is emptyconf/catalina.properties
configuration file is read. .loader
Shared Class Loader
With Common Class Loader
as the parent loader, the parent class loader of all web applications reads the shared.loader in the
attribute, the default value is emptyconf/catalina.properties
configuration file by default
Webapp Class Loader
Use Shared Class Loader
as the parent loader to load Class files and resources in the /WEB-INF/classes
directory, /WEB-INF/lib
The jar package file in the directory is only visible to the current web application
The class corresponding to this level of ClassLoader is org.apache.catalina.loader.WebappClassLoaderBase
rewriting the loadClass
method, which destroys the “parental delegation model< in java /strong>“
public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {<!-- --> synchronized (JreCompat. isGraalAvailable() ? this : getClassLoadingLock(name)) {<!-- --> if (log.isDebugEnabled()) {<!-- --> log.debug("loadClass(" + name + ", " + resolve + ")"); } Class<?> clazz = null; // Log access to stopped class loader checkStateForClassLoading(name); // (0) Check our previously loaded local class cache clazz = findLoadedClass0(name); if (clazz != null) {<!-- --> if (log.isDebugEnabled()) {<!-- --> log.debug(" Returning class from cache"); } if (resolve) {<!-- --> resolveClass(clazz); } return clazz; } // (0.1) Check our previously loaded class cache clazz = JreCompat.isGraalAvailable() ? null : findLoadedClass(name); if (clazz != null) {<!-- --> if (log.isDebugEnabled()) {<!-- --> log.debug(" Returning class from cache"); } if (resolve) {<!-- --> resolveClass(clazz); } return clazz; } // (0.2) Try loading the class with the bootstrap class loader, to prevent // the webapp from overriding Java SE classes. This implements // SRV.10.7.2 String resourceName = binaryNameToPath(name, false); // In fact, this place can only get ExtensionClassLoader in Java, because BootClassLoader is null ClassLoader javaseLoader = getJavaseClassLoader(); boolean tryLoadingFromJavaseLoader; try {<!-- --> // Use getResource as it won't trigger an expensive // ClassNotFoundException if the resource is not available from // the Java SE class loader. However (see // https://bz.apache.org/bugzilla/show_bug.cgi?id=58125 for // details) when running under a security manager in rare cases // this call may trigger a ClassCircularityError. // See https://bz.apache.org/bugzilla/show_bug.cgi?id=61424 for // details of how this may trigger a StackOverflowError // Given these reported errors, catch Throwable to ensure any // other edge cases are also caught 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) {<!-- --> // Swallow all exceptions apart from those that must be re-thrown ExceptionUtils. handleThrowable(t); // The getResource() trick won't work for this class. We have to // try loading it directly and accept that we might get a // ClassNotFoundException. tryLoadingFromJavaseLoader = true; } if (tryLoadingFromJavaseLoader) {<!-- --> try {<!-- --> clazz = javaseLoader.loadClass(name); if (clazz != null) {<!-- --> if (resolve) {<!-- --> resolveClass(clazz); } return clazz; } } catch (ClassNotFoundException e) {<!-- --> //Ignore } } // (0.5) Permission to access this class when using a SecurityManager if (securityManager != null) {<!-- --> int i = name.lastIndexOf('.'); if (i >= 0) {<!-- --> try {<!-- --> securityManager.checkPackageAccess(name.substring(0,i)); } catch (SecurityException se) {<!-- --> String error = sm.getString("webappClassLoader.restrictedPackage", name); log. info(error, se); throw new ClassNotFoundException(error, se); } } } // Whether to use delegation mode, boolean delegateLoad = delegate || filter(name, true); // (1) Delegate to our parent if requested if (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); } return clazz; } } catch (ClassNotFoundException e) {<!-- --> //Ignore } } // (2) Search local repositories if (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); } return clazz; } } catch (ClassNotFoundException e) {<!-- --> //Ignore } // (3) Delegate to parent unconditionally if (!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); } returnclazz; } } catch (ClassNotFoundException e) {<!-- --> //Ignore } } } throw new ClassNotFoundException(name); }
To sum up, there are several steps:
When breaking the delegate mode:
- Load from cache
- Loaded from the ExtensionClassLoader in the JVM, because this place is in the delegation mode, it will be searched according to the path of Bootstrap -> Extension
- load from the current class loader
- Loaded from the parent loader, this place will also follow the delegation mode, so it will be searched according to the Bootstrap -> Extension -> Application -> Common -> Shared path
When delegation mode is turned on:
- Load from cache
- Loaded from the ExtensionClassLoader in the JVM, because this place is in the delegation mode, it will be searched according to the path of Bootstrap -> Extension
- Loaded from the parent loader, this place will also follow the delegation mode, so it will be searched according to the Bootstrap -> Extension -> Application -> Common -> Shared path
- load from the current class loader