1. The introduction

When Tomcat deployable Web applications, the application is placed in the webapps folder. Webapps corresponds to Tomcat’s container Host, and the folder in the webapps folder corresponds to Context. After Tomcat starts, All Web applications in Webapps can provide services.

There will be more than one application under webapps. For example, APP1 and APP2 have their own independent jar packages. These JAR packages will be located in the web-info /lib directory of the APP. These jar packages are most likely to be duplicated, such as the commonly used Spring family bucket, in which the version must be different, so how does Tomcat handle?

2. JVM class loading mechanism

When it comes to The class loading mechanism of Tomcat, one of the most interesting topics is how classes are loaded by the JVM, since Tomcat also runs on the JVM.

The following content is from “Understanding Java Virtual Machines in Depth” by Zhou Zhiming.

2.1 What is class loading

Loading a class involves reading binary data from the class’s.class file into memory, placing it in the method area of the runtime data area, and then creating a java.lang. class object in the heap that encapsulates the class’s data structure in the method area. The end product of Class loading is the Class object in the heap, which encapsulates the Class’s data structure in the method area and provides the Java programmer with an interface to access the data structure in the method area.

Class loaders do not need to wait for “first active use” of a class to load it. The JVM specification allows classloaders to preload a class when they expect it to be used. If they encounter missing.class files or errors during preloading, The class loader must not report a LinkageError until the program first actively uses the class. If the class is never actively used by the program, the class loader will not report an error.

How to load. Class files - directly from the local system - Download. Class files from the network - Load. Class files from zip, JAR archives - Extract. Class files from a proprietary database - Dynamically compile Java source files into. Class filesCopy the code

2.2 Class life cycle

Next, let’s look at the life cycle of the next class:

When a type is loaded into vm memory and unloaded from memory, 2. Its life cycle must go through Loading, Verification, Preparation, Resolution, Initialization, Using and Unloading. The three parts of validation, preparation and parsing are collectively referred to as Linking.

2.3 Parental delegation model

Java provides three types of system classloaders:

  • Bootstrap ClassLoader: implemented in C++ and part of the JVM, it loads<JAVA_HOME>\libFiles in a directory, or by-XbootclasspathArgument, and the class loader only loads files with a specific name (such as rt.jar), not all files in that directory. The startup class loader cannot be referenced directly by a Java program.
  • Extension ClassLoader: by Extension ClassLoadersun.misc.Launcher.ExtClassLoaderImplementation, which is responsible for loading<JAVA_HOME>\lib\extDirectory, or byjava.ext.dirsFor all libraries in the path specified by the system variable, the developer can use the extended class loader directly.
  • Application ClassLoader: Also known as the system ClassLoadersun.misc.Launcher.AppClassLoaderThe implementation. It is responsible for loading libraries specified on the user’s Class Path. Developers can use this Class loader directly. If the application does not have a custom Class loader, this is generally the default Class loader in the application.

How the parental delegation model works:

If a classloader receives a classload request, it does not try to load the class itself at first. Instead, it delegates the request to the parent classloader. This is true at every level of classloaders, so all load requests should eventually be passed to the top level of the starting classloader. Only when the parent loader reports that it cannot complete the load request (it did not find the desired class in its search scope) will the child loader attempt to load it itself.

Why is that?

An example is the java.lang.Object class, which is stored in rt.jar. This class must be loaded by any class loader. The parent delegates the Bootstrap class loader at the top of the model. Thus, the Object class is the same class in the various classloader environments of the program. Instead of using the parent delegate model and having each class loader load it, if a user writes a class called “java.lang.Object” and places it in the program’s ClassPath, there will be multiple Object classes. The most basic behavior in the Java type system is not guaranteed, and the application is in chaos.

3. Tomcat class loading mechanism

Take a look at the Tomcat class loader as a whole:

As you can see, Tomcat has added several new class loaders to the original JVM’s class loading mechanism, including three base class loaders and a class loader for each Web application.

The three base classloaders are configured in conf/ Catalina.properties:

common.loader="${catalina.base}/lib","${catalina.base}/lib/*.jar","${catalina.home}/lib","${catalina.home}/lib/*.jar"

server.loader=

shared.loader=
Copy the code
  • Common: The parent class of the application class loader is the Common class loader at the top of Tomcatconf/catalina.propertiesIn thecommon.loaderSpecifies that the default points to${catalina.home}/libUnder the bag.
  • Catalina: Uses the Common class loader as its parent class. It is used to load the Tomcat application serverserver.loaderDefault is null, in which case Tomcat uses the Common class loader to load the application server.
  • Shared: The Common class loader is the parent class of all Web applications. Its path is Sharedshared.loaderDefault is null, in which case Tomcat uses the Common class loader as the parent loader of the Web application.
  • Web applications: Load the Shared class loader as its parent class/WEB-INF/classesUncompressed Class and resource files in the directory as well/WEB-INF/libThis class loader is visible only to the current Web application and is invisible to other Web applications.

4. Tomcat class loading mechanism source code

4.1 Creating a ClassLoader

Take a look at the loader class diagram:

Start with the main method of BootStrap:

public static void main(String args[]) {
    synchronized (daemonLock) {
        if (daemon == null) {
            // Don't set daemon until init() has completed
            Bootstrap bootstrap = new Bootstrap();
            try {
                bootstrap.init();
            } catch (Throwable t) {
                handleThrowable(t);
                t.printStackTrace();
                return;
            }
            daemon = bootstrap;
        } else {
            // When running as a service the call to stop will be on a new
            // thread so make sure the correct class loader is used to
            // prevent a range of class not found exceptions.
            Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
        }
        // omit the rest of the code...}}Copy the code

If bootstrap is null, set Catalina ClassLoader to the current thread. If bootstrap is null, init() is performed.

public void init(a) throws Exception {
    // Initializes the classloader
    initClassLoaders();
    // Set the thread class loader, passing in the container's loader
    Thread.currentThread().setContextClassLoader(catalinaLoader);
    // Set the zone security class loader
    SecurityClassLoad.securityClassLoad(catalinaLoader);
    // omit the rest of the code...
}
Copy the code

InitClassLoaders () is called to initialize the ClassLoader. After initialization, Catalina ClassLoader is also set to the current thread.

private void initClassLoaders(a) {
    try {
        commonLoader = createClassLoader("common".null);
        if (commonLoader == null) {
            // no config file, default to this loader - we might be in a 'single' env.
            commonLoader = this.getClass().getClassLoader();
        }
        catalinaLoader = createClassLoader("server", commonLoader);
        sharedLoader = createClassLoader("shared", commonLoader);
    } catch (Throwable t) {
        handleThrowable(t);
        log.error("Class loader creation threw exception", t);
        System.exit(1); }}Copy the code

This should make it clear that three classLoaders will be created: CommClassLoader, Catalina ClassLoader, and SharedClassLoader, which correspond to the three basic classloaders described earlier.

Then go to createClassLoader() to see the code:

private ClassLoader createClassLoader(String name, ClassLoader parent)
    throws Exception {

    String value = CatalinaProperties.getProperty(name + ".loader");
    if ((value == null) || (value.equals("")))
        return parent;

    value = replace(value);

    List<Repository> repositories = new ArrayList<>();

    String[] repositoryPaths = getPaths(value);

    for (String repository : repositoryPaths) {
        // Check for a JAR URL repository
        try {
            @SuppressWarnings("unused")
            URL url = new URL(repository);
            repositories.add(new Repository(repository, RepositoryType.URL));
            continue;
        } catch (MalformedURLException e) {
            // Ignore
        }

        // Local repository
        if (repository.endsWith("*.jar")) {
            repository = repository.substring
                (0, repository.length() - "*.jar".length());
            repositories.add(new Repository(repository, RepositoryType.GLOB));
        } else if (repository.endsWith(".jar")) {
            repositories.add(new Repository(repository, RepositoryType.JAR));
        } else {
            repositories.add(newRepository(repository, RepositoryType.DIR)); }}return ClassLoaderFactory.createClassLoader(repositories, parent);
}
Copy the code

The common.loader, server.loader, and shared.loader are loaded in conf/catalina.properties.

4.2 ClassLoader Loading Process

Open ParallelWebappClassLoader directly, as to why not see WebappClassLoader, from the name will know that is a parallel ParallelWebappClassLoader WebappClassLoader.

Then see ParallelWebappClassLoader loadclass method is realized in its superclass WebappClassLoaderBase.

4.2.1 Step 1:

publicClass<? > loadClass(String name,boolean resolve) throws ClassNotFoundException {

    synchronized (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;
        }
        // omit the rest...
Copy the code

The findLoaderClass0() method is first called to check if this class has been loaded in the WebappClassLoader.

protectedClass<? > findLoadedClass0(String name) { String path = binaryNameToPath(name,true);

    ResourceEntry entry = resourceEntries.get(path);
    if(entry ! =null) {
        return entry.loadedClass;
    }
    return null;
}
Copy the code

Classes loaded by the WebappClassLoader are stored in the resourceEntries cache.

protected final Map<String, ResourceEntry> resourceEntries = new ConcurrentHashMap<>();
Copy the code

4.2.2 Step 2:

    // omit the rest...
    clazz = findLoadedClass(name);
    if(clazz ! =null) {
        if (log.isDebugEnabled())
            log.debug(" Returning class from cache");
        if (resolve)
            resolveClass(clazz);
        return clazz;
    }
    // omit the rest...
Copy the code

If the first step is not found, continue to check whether the class has been loaded in the JVM vm. Call the findLoadedClass() method of ClassLoader to check.

4.2.3 Step 3:

    ClassLoader javaseLoader = getJavaseClassLoader();
    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) {

        ExceptionUtils.handleThrowable(t);

        tryLoadingFromJavaseLoader = true;
    }

    if (tryLoadingFromJavaseLoader) {
        try {
            clazz = javaseLoader.loadClass(name);
            if(clazz ! =null) {
                if (resolve)
                    resolveClass(clazz);
                returnclazz; }}catch (ClassNotFoundException e) {
            // Ignore}}Copy the code

If the first two steps are not found, the class is loaded using the system class (that is, the ClassPath of the current JVM). To prevent overwriting the base class implementation, the class is determined to be a class in the base class library in JVMSE.

4.2.4 Step 4:

    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);
                returnclazz; }}catch (ClassNotFoundException e) {
            // Ignore}}Copy the code

Check whether the delegate property is set, set it to true, and the class will be loaded exactly as the JVM’s “parent delegate” mechanism does.

By default, the WebappClassLoader handles loading classes itself first. Of course, if the delegate is used, the parent delegate is not loaded into the class instance, so it is loaded using the WebappClassLoader.

4.2.5 Step 5:

    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);
            returnclazz; }}catch (ClassNotFoundException e) {
        // Ignore
    }
Copy the code

If there is no delegate, the default is to use the WebappClassLoader to load the class for the first time. A custom findClass() definition handles class loading rules.

FindClass () will look for classes in web-INF /classes.

4.2.6 Step 6:

    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}}Copy the code

If the WebappClassLoader fails to find the class in /WEB-INF/classes or/web-INF /lib, it forces the System or Common classloader to find the class.

4.2.7 summary

The default loading order for Web application class loaders is:

  1. Load from cache first;
  2. If not, it is loaded from the JVM’s Bootstrap classloader;
  3. If not, it is loaded from the current class loader (in web-INF /classes, web-INF /lib order);
  4. If not, it is loaded from the parent class loader. Since the parent class loader uses the default delegate mode, the load order is AppClassLoader, Common, and Shared.

reference

www.jianshu.com/p/69c4526b8…


This article is constantly updated, you can search “Geek excavator” on wechat to read it in the first time, and the key words in reply have all kinds of tutorials PREPARED by me, welcome to read.