tomcat类加载器-with源码

本文详细解析了Tomcat中的类加载过程,特别是WebappClassLoader如何打破传统的类加载机制,实现自定义的加载流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

tomcat类加载器-with源码

基于Tomcat7

Bootstrap类
public void init()
        throws Exception
    {

        // Set Catalina path
        setCatalinaHome();
        setCatalinaBase();

        initClassLoaders();

        Thread.currentThread().setContextClassLoader(catalinaLoader);

        SecurityClassLoad.securityClassLoad(catalinaLoader);

        // Load our startup class and call its process() method
        if (log.isDebugEnabled())
            log.debug("Loading startup class");
        Class<?> startupClass =
            catalinaLoader.loadClass
            ("org.apache.catalina.startup.Catalina");
        Object startupInstance = startupClass.newInstance();

        // Set the shared extensions class loader
        if (log.isDebugEnabled())
            log.debug("Setting startup class properties");
        String methodName = "setParentClassLoader";
        Class<?> paramTypes[] = new Class[1];
        paramTypes[0] = Class.forName("java.lang.ClassLoader");
        Object paramValues[] = new Object[1];
        paramValues[0] = sharedLoader;
        Method method =
            startupInstance.getClass().getMethod(methodName, paramTypes);
        method.invoke(startupInstance, paramValues);

        catalinaDaemon = startupInstance;

    }

第三句: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);
    }
}

包括commonLoader、sharedLoader、catalinaLoader
使用前面已经构件好的catalinaLoader加载tomcat最核心的对象,那就是org.apache.catalina.startup.Catalina类的对象catalinaDaemon,并以反射的方式调用其setParentClassLoader方法,把sharedLoader作为参数传入

init()过程中:
Bootstrap类持有的几个对象(catalinaDaemon、commonLoader、sharedLoader、catalinaLoader)都得到了创建和必要的初始化,并且在main()
方法中知道init()方法完成才set daemon

Tomcat类加载器

为了支持和分隔多个web app,使用了WebappClassLoader,在实现上,它打破了系统默认规则,或者说是打破了java.lang.ClassLoader逻辑中的“双亲委派”模式,提供了一套自定义的类加载流程。

public class WebappClassLoader extends WebappClassLoaderBase 
public abstract class WebappClassLoaderBase extends URLClassLoader

WebappClassLoaderBase它覆盖了父类URLClassLoader中的loadClass方法,改变了默认的类加载顺序。

public Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
    synchronized (getClassLoadingLockInternal(name)) {
        if (log.isDebugEnabled())
            log.debug("loadClass(" + name + ", " + resolve + ")");
        Class<?> clazz = null;

        // Log access to stopped classloader
        if (!started) {
            try {
                throw new IllegalStateException();
            } catch (IllegalStateException e) {
                log.info(sm.getString("webappClassLoader.stopped", name), e);
            }
        }

        // (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 J2SE classes
        //系统类加载器
        try {
            clazz = j2seClassLoader.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;
                    if (name.endsWith("BeanInfo")) {
                        // BZ 57906: suppress logging for calls from
                        // java.beans.Introspector.findExplicitBeanInfo()
                        log.debug(error, se);
                    } else {
                        log.info(error, se);
                    }
                    throw new ClassNotFoundException(error, se);
                }
            }
        }

        boolean delegateLoad = delegate || filter(name);

        // (1) Delegate to our parent if requested
        //如果设置delegate,先由父类加载器加载,默认false
        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);
}

首先findLoadedClass0()和findLoadedClass()分别从本地和父类加载器的缓存中查找当前要加载的类是否已经加载过了。之后Tomcat类加载器会将加载请求委派给系统类加载器。接下来根据delegate变量的设置,决定是先由自己加载,
还是先由父类加载器去加载。
其中:WebappClassLoader是对应一个Web应用的类加载器,其父亲是Tomcat的lib的加载器,即Common 通用类加载。如果配置 ,那么加载顺序将改变:
- JVM 的 Bootstrap 类
- System 类加载器的类(如上所述)
- Common 类加载器的类(如上所述)
- Web 应用的 /WEB-INF/classes 类
- Web 应用的 /WEB-INF/lib/*.jar 类

本来的类加载顺序参考上篇文章:
http://blog.youkuaiyun.com/cx520forever/article/details/50780461

如果想要在Web应用间共享一些Jar包,则不仅需要将公共包放在Tomcat的lib下,还要删掉Web应用lib中的包,否则Tomcat启动时还是会优先加载Web应用lib下的包的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值