tomcat类加载机制

原创
10/27 10:18
阅读数 2.9K

一、tomcat类加载器继承图

tomcat类加载器的集成体系,包含两部分:

1.上半部分是jdk自带的,包含:启动类加载器,扩展类加载器,应用类加载器,这里不展开讲解。如果想了解,可以查看sun.misc.Launcher.Launcher()和java.lang.ClassLoader.loadClass(String, boolean)源码

2.下半部分是tomcat的自定义类加载器,包含:

a)commonClassLoader:通过${tomcatpath}/conf/catalina.properties中的common.loader配置,通常加载tomcat和webapp共享的class

b) catalinaClassLoader: 通过${tomcatpath}/conf/catalina.properties中的server.loader配置,加载tomcat需要的class,对webapp不可见

c) sharedClassLoader:通过${tomcatpath}/conf/catalina.properties中的shared.loader配置, 加载webapp们共享的class,对tomcat容器不可见

d) webappClassLoader: 这个类加载器,就是加载我们写的应用程序class的。通常说的tomcat打破双亲委派就是说的它

二、源码

1.初始化自定义加载器

// org.apache.catalina.startup.Bootstrap.initClassLoaders()
private void initClassLoaders() {
    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);
    }
}

2.初始化webappclassloader

org.apache.catalina.loader.WebappLoader.startInternal()
protected void startInternal() throws LifecycleException {
 
    if (log.isDebugEnabled())
        log.debug(sm.getString("webappLoader.starting"));
 
    if (context.getResources() == null) {
        log.info("No resources for " + context);
        setState(LifecycleState.STARTING);
        return;
    }
 
    // Construct a class loader based on our current repositories list
    try {
        // 创建classloader,并将父classloader设置成sharedClassLoader
        classLoader = createClassLoader();
        classLoader.setResources(context.getResources());
        // 设置委派模式
        classLoader.setDelegate(this.delegate);
 
        // Configure our repositories
        setClassPath();
 
        setPermissions();
 
        // 开始类加载
        ((Lifecycle) classLoader).start();
 
        String contextName = context.getName();
        if (!contextName.startsWith("/")) {
            contextName = "/" + contextName;
        }
        ObjectName cloname = new ObjectName(context.getDomain() + ":type=" +
                classLoader.getClass().getSimpleName() + ",host=" +
                context.getParent().getName() + ",context=" + contextName);
        Registry.getRegistry(null, null)
            .registerComponent(classLoader, cloname, null);
 
    } catch (Throwable t) {
        t = ExceptionUtils.unwrapInvocationTargetException(t);
        ExceptionUtils.handleThrowable(t);
        log.error( "LifecycleException ", t );
        throw new LifecycleException("start: ", t);
    }
 
    setState(LifecycleState.STARTING);
}
 
 
private WebappClassLoaderBase createClassLoader()
    throws Exception {
 
    Class<?> clazz = Class.forName(loaderClass);
    WebappClassLoaderBase classLoader = null;
 
    if (parentClassLoader == null) {
        parentClassLoader = context.getParentClassLoader();
    } else {
        context.setParentClassLoader(parentClassLoader);
    }
    Class<?>[] argTypes = { ClassLoader.class };
    Object[] args = { parentClassLoader };
    Constructor<?> constr = clazz.getConstructor(argTypes);
    classLoader = (WebappClassLoaderBase) constr.newInstance(args);
 
    return classLoader;
}

3.装配应用class文件

// org.apache.catalina.loader.WebappClassLoaderBase.start()
public void start() throws LifecycleException {
 
    state = LifecycleState.STARTING_PREP;
     
    // 扫描 /WEB-INF/classes 下的class
    WebResource[] classesResources = resources.getResources("/WEB-INF/classes");
    for (WebResource classes : classesResources) {
        if (classes.isDirectory() && classes.canRead()) {
            localRepositories.add(classes.getURL());
        }
    }
 
 
    // 扫描 /WEB-INF/lib 下的jar
    WebResource[] jars = resources.listResources("/WEB-INF/lib");
    for (WebResource jar : jars) {
        if (jar.getName().endsWith(".jar") && jar.isFile() && jar.canRead()) {
            localRepositories.add(jar.getURL());
            // 记录jar的修改时间,用于热更新
            jarModificationTimes.put(
                    jar.getName(), Long.valueOf(jar.getLastModified()));
        }
    }
 
    state = LifecycleState.STARTED;
}

4.tomcat热更新:

// org.apache.catalina.loader.WebappLoader.backgroundProcess()
public void backgroundProcess() {
    // reloadable:是否开启热更新
    // modified():判断jar和class是否有修改(这里会导致cpu瞬间高)
    if (reloadable && modified()) {
        try {
            Thread.currentThread().setContextClassLoader
                (WebappLoader.class.getClassLoader());
            if (context != null) {
                context.reload();
            }
        } finally {
            if (context != null && context.getLoader() != null) {
                Thread.currentThread().setContextClassLoader
                    (context.getLoader().getClassLoader());
            }
        }
    }
}

5.加载一个class文件的过程:

// org.apache.catalina.loader.WebappClassLoaderBase.loadClass(String)
public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
    synchronized (getClassLoadingLock(name)) {
 
        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 = 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 system class loader, to prevent
        //       the webapp from overriding Java SE classes. This implements
        //       SRV.10.7.2
        String resourceName = binaryNameToPath(name, false);
 
        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;
        }
 
        // 尝试用加载String类(即启动类加载器)的类加载器加载
        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 = "Security Violation, attempt to use " +
                        "Restricted Class: " + name;
                    log.info(error, se);
                    throw new ClassNotFoundException(error, se);
                }
            }
        }
 
        // 判断是否委派给父类加载器
        boolean delegateLoad = delegate || filter(name, true);
 
        // (1) Delegate to our parent if requested
        // 委派给父类加载器(sharedClassLoader),符合委派双亲
        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);
                    return clazz;
                }
            } catch (ClassNotFoundException e) {
                // Ignore
            }
        }
    }
 
    throw new ClassNotFoundException(name);
}

 

展开阅读全文
打赏
1
3 收藏
分享
加载中
更多评论
打赏
0 评论
3 收藏
1
分享
OSCHINA
登录后可查看更多优质内容
返回顶部
顶部