tomcat系列之类加载器

jvm中有三种类加载器,好吧,为了介绍tomcat中的类加载器,先来普及下类加载器的概念,参见这篇文章——JVM类加载器 。

就像JVM的类加载器一样,为了安全tomcat中自己扩展了几种类加载器,分别来加载不同路径下的lib库,因为一个tomcat可以部署不同的应用,如果各个应用的lib库由相同的类加载器加载那么带来的安全隐患不言而喻 。来看一下标准的tomcat目录:


lib目录下的类文件是整个tomcat需要的,webapp下所有应用是不应该有权限访问到lib下的类的。我们通过源码来一步步看tomcat是如何处理类加载问题的,先来看下Bootstrap的主函数:

public static void main(String args[]) {

        if (daemon == null) {
            daemon = new Bootstrap();
            try {
                daemon.init();
            } catch (Throwable t) {
                t.printStackTrace();
                return;
            }
        }

        try {
            String command = "start";
            if (args.length > 0) {
                command = args[args.length - 1];
            }

            if (command.equals("startd")) {
                args[args.length - 1] = "start";
                daemon.load(args);
                daemon.start();
            } else if (command.equals("stopd")) {
                args[args.length - 1] = "stop";
                daemon.stop();
            } else if (command.equals("start")) {
                daemon.setAwait(true);
                daemon.load(args);
                daemon.start();
            } else if (command.equals("stop")) {
                daemon.stopServer(args);
            } else {
                log.warn("Bootstrap: command \"" + command + "\" does not exist.");
            }
        } catch (Throwable t) {
            t.printStackTrace();
        }

    }

首先要的逻辑是init初始化,init的代码就不贴了,主要的逻辑讲一下,初始化catalinahome和catalineabase,可以将catalinahome简单理解为tomcat的安装目录,catalinabase目录为tomcat的工作目录,默认这两个目录是一致的。紧接着是初始化类加载器的动作,重点看下这里的代码:

private void initClassLoaders() {
        try {
			//commonLoader,专门负责加载catalina.properties配置文件里的common.loader=后面的类库
            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专门负责加载server.loader=后面的类库
            catalinaLoader = createClassLoader("server", commonLoader);
			//专门加载shared.loader=后面的类库
            sharedLoader = createClassLoader("shared", commonLoader);
        } catch (Throwable t) {
            log.error("Class loader creation threw exception", t);
            System.exit(1);
        }
    }

三种类加载器,其中commonLoader是另外两个的父加载器,这三种类加载器都是继承自URLClassLoader。实际上还有一种类加载器,WebAppClassLoader专门负责加载webapp中的类库。初始化方法中有这么一句代码:Thread.currentThread().setContextClassLoader(catalinaLoader);这就是JVM类加载中讲到过的线程上下文加载器,目前还不知道这里设置的目的是什么。来看看WebappClassLoader的源码,其主要是覆写了loadClass方法:

// (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 = system.loadClass(name);
            if (clazz != null) {
                if (resolve)
                    resolveClass(clazz);
                return (clazz);
            }
        } catch (ClassNotFoundException e) {
            // Ignore
        }

首先去本地的缓存查找,如果没有找到则去父类的缓存查找,如果还没找到则根据用户的配置决定是由本加载器加载或者由父类加载器加载:

boolean delegateLoad = delegate || filter(name);

        // (1) Delegate to our parent if requested
        if (delegateLoad) {
            if (log.isDebugEnabled())
                log.debug("  Delegating to parent classloader1 " + parent);
			//由父加载器去加载,也就是sharedClassLoader
            ClassLoader loader = parent;
            if (loader == null)
                loader = system;
            try {
                clazz = loader.loadClass(name);
                if (clazz != null) {
                    if (log.isDebugEnabled())
                        log.debug("  Loading class from parent");
                    if (resolve)
                        resolveClass(clazz);
                    return (clazz);
                }
            } catch (ClassNotFoundException e) {
                ;
            }
        }

        // (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) {
            ;
        }

        // (3) Delegate to parent unconditionally
        if (!delegateLoad) {
            if (log.isDebugEnabled())
                log.debug("  Delegating to parent classloader at end: " + parent);
            ClassLoader loader = parent;
            if (loader == null)
                loader = system;
            try {
                clazz = loader.loadClass(name);
                if (clazz != null) {
                    if (log.isDebugEnabled())
                        log.debug("  Loading class from parent");
                    if (resolve)
                        resolveClass(clazz);
                    return (clazz);
                }
            } catch (ClassNotFoundException e) {
                ;
            }
        }

        throw new ClassNotFoundException(name);

比如WEB-INF\classes或WEB-INF\lib下面的类库通常需要从findClass中获取。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值